From d1cbd17c2cf90cb22463b91a87e4b4dbe4971ce8 Mon Sep 17 00:00:00 2001 From: Leo Korinth <lkorinth@openjdk.org> Date: Mon, 13 Nov 2017 15:28:17 +0100 Subject: [PATCH 001/165] 8190408: Run G1CMRemarkTask with the appropriate amount of threads instead of starting up everyone Reviewed-by: tschatzl, sjohanss --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 36 +++++++++----------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 9073616c33f..2ef2c2938b3 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1756,28 +1756,24 @@ private: G1ConcurrentMark* _cm; public: void work(uint worker_id) { - // Since all available tasks are actually started, we should - // only proceed if we're supposed to be active. - if (worker_id < _cm->active_tasks()) { - G1CMTask* task = _cm->task(worker_id); - task->record_start_time(); - { - ResourceMark rm; - HandleMark hm; + G1CMTask* task = _cm->task(worker_id); + task->record_start_time(); + { + ResourceMark rm; + HandleMark hm; - G1RemarkThreadsClosure threads_f(G1CollectedHeap::heap(), task); - Threads::threads_do(&threads_f); - } - - do { - task->do_marking_step(1000000000.0 /* something very large */, - true /* do_termination */, - false /* is_serial */); - } while (task->has_aborted() && !_cm->has_overflown()); - // If we overflow, then we do not want to restart. We instead - // want to abort remark and do concurrent marking again. - task->record_end_time(); + G1RemarkThreadsClosure threads_f(G1CollectedHeap::heap(), task); + Threads::threads_do(&threads_f); } + + do { + task->do_marking_step(1000000000.0 /* something very large */, + true /* do_termination */, + false /* is_serial */); + } while (task->has_aborted() && !_cm->has_overflown()); + // If we overflow, then we do not want to restart. We instead + // want to abort remark and do concurrent marking again. + task->record_end_time(); } G1CMRemarkTask(G1ConcurrentMark* cm, uint active_workers) : From cb1ea0fc333bac73720d8faf15370b2b9b18fa6b Mon Sep 17 00:00:00 2001 From: Alexander Harlap <aharlap@openjdk.org> Date: Thu, 16 Nov 2017 14:06:44 -0500 Subject: [PATCH 002/165] 8187819: gc/TestFullGCALot.java fails on jdk10 started with "-XX:-UseCompressedOops" option Need to initialized metaspace performance counters before their potential use Reviewed-by: tschatzl, sjohanss --- src/hotspot/share/memory/universe.cpp | 8 ++++---- test/hotspot/jtreg/gc/TestFullGCALot.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 32da59f6053..538ca6e294e 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -687,6 +687,10 @@ jint universe_init() { Metaspace::global_initialize(); + // Initialize performance counters for metaspaces + MetaspaceCounters::initialize_performance_counters(); + CompressedClassSpaceCounters::initialize_performance_counters(); + AOTLoader::universe_init(); // Checks 'AfterMemoryInit' constraints. @@ -1085,10 +1089,6 @@ bool universe_post_init() { // ("weak") refs processing infrastructure initialization Universe::heap()->post_initialize(); - // Initialize performance counters for metaspaces - MetaspaceCounters::initialize_performance_counters(); - CompressedClassSpaceCounters::initialize_performance_counters(); - MemoryService::add_metaspace_memory_pools(); MemoryService::set_universe_heap(Universe::heap()); diff --git a/test/hotspot/jtreg/gc/TestFullGCALot.java b/test/hotspot/jtreg/gc/TestFullGCALot.java index 19ac9641cd2..90d05f9253b 100644 --- a/test/hotspot/jtreg/gc/TestFullGCALot.java +++ b/test/hotspot/jtreg/gc/TestFullGCALot.java @@ -24,7 +24,7 @@ /* * @test TestFullGCALot * @key gc - * @bug 4187687 + * @bug 4187687 8187819 * @summary Ensure no access violation when using FullGCALot * @requires vm.debug * @run main/othervm -XX:NewSize=10m -XX:+FullGCALot -XX:FullGCALotInterval=120 TestFullGCALot From d5adf1df921e5ecb8ff4c7e4349a12660069ed28 Mon Sep 17 00:00:00 2001 From: Lutz Schmidt <lucy@openjdk.org> Date: Wed, 22 Nov 2017 17:10:38 +0100 Subject: [PATCH 003/165] 8189793: [s390]: Improve String compress/inflate by exploiting vector instructions Reviewed-by: mdoerr, goetz --- src/hotspot/cpu/s390/assembler_s390.hpp | 39 +- .../cpu/s390/assembler_s390.inline.hpp | 14 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 796 +++++++++++++++--- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 37 +- src/hotspot/cpu/s390/s390.ad | 30 +- 5 files changed, 788 insertions(+), 128 deletions(-) diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index a839700259c..9900adbfebc 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.hpp @@ -582,7 +582,11 @@ class Assembler : public AbstractAssembler { #define LOC_ZOPC (unsigned long)(0xebL << 40 | 0xf2L) // z196 #define LOCG_ZOPC (unsigned long)(0xebL << 40 | 0xe2L) // z196 -#define LMG_ZOPC (unsigned long)(235L << 40 | 4L) + +// LOAD multiple registers at once +#define LM_ZOPC (unsigned int)(0x98 << 24) +#define LMY_ZOPC (unsigned long)(0xebL << 40 | 0x98L) +#define LMG_ZOPC (unsigned long)(0xebL << 40 | 0x04L) #define LE_ZOPC (unsigned int)(0x78 << 24) #define LEY_ZOPC (unsigned long)(237L << 40 | 100L) @@ -613,7 +617,10 @@ class Assembler : public AbstractAssembler { #define STOC_ZOPC (unsigned long)(0xebL << 40 | 0xf3L) // z196 #define STOCG_ZOPC (unsigned long)(0xebL << 40 | 0xe3L) // z196 -#define STMG_ZOPC (unsigned long)(235L << 40 | 36L) +// STORE multiple registers at once +#define STM_ZOPC (unsigned int)(0x90 << 24) +#define STMY_ZOPC (unsigned long)(0xebL << 40 | 0x90L) +#define STMG_ZOPC (unsigned long)(0xebL << 40 | 0x24L) #define STE_ZOPC (unsigned int)(0x70 << 24) #define STEY_ZOPC (unsigned long)(237L << 40 | 102L) @@ -874,15 +881,19 @@ class Assembler : public AbstractAssembler { // Shift // arithmetic -#define SLA_ZOPC (unsigned int)(139 << 24) -#define SLAG_ZOPC (unsigned long)(235L << 40 | 11L) -#define SRA_ZOPC (unsigned int)(138 << 24) -#define SRAG_ZOPC (unsigned long)(235L << 40 | 10L) +#define SLA_ZOPC (unsigned int)(0x8b << 24) +#define SLAK_ZOPC (unsigned long)(0xebL << 40 | 0xddL) +#define SLAG_ZOPC (unsigned long)(0xebL << 40 | 0x0bL) +#define SRA_ZOPC (unsigned int)(0x8a << 24) +#define SRAK_ZOPC (unsigned long)(0xebL << 40 | 0xdcL) +#define SRAG_ZOPC (unsigned long)(0xebL << 40 | 0x0aL) // logical -#define SLL_ZOPC (unsigned int)(137 << 24) -#define SLLG_ZOPC (unsigned long)(235L << 40 | 13L) -#define SRL_ZOPC (unsigned int)(136 << 24) -#define SRLG_ZOPC (unsigned long)(235L << 40 | 12L) +#define SLL_ZOPC (unsigned int)(0x89 << 24) +#define SLLK_ZOPC (unsigned long)(0xebL << 40 | 0xdfL) +#define SLLG_ZOPC (unsigned long)(0xebL << 40 | 0x0dL) +#define SRL_ZOPC (unsigned int)(0x88 << 24) +#define SRLK_ZOPC (unsigned long)(0xebL << 40 | 0xdeL) +#define SRLG_ZOPC (unsigned long)(0xebL << 40 | 0x0cL) // Rotate, then AND/XOR/OR/insert // rotate @@ -2262,12 +2273,16 @@ class Assembler : public AbstractAssembler { // shift inline void z_sla( Register r1, int64_t d2, Register b2=Z_R0); // shift left r1 = r1 << ((d2+b2)&0x3f) ; int32, only 31 bits shifted, sign preserved! + inline void z_slak(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left r1 = r3 << ((d2+b2)&0x3f) ; int32, only 31 bits shifted, sign preserved! inline void z_slag(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left r1 = r3 << ((d2+b2)&0x3f) ; int64, only 63 bits shifted, sign preserved! inline void z_sra( Register r1, int64_t d2, Register b2=Z_R0); // shift right r1 = r1 >> ((d2+b2)&0x3f) ; int32, sign extended + inline void z_srak(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int32, sign extended inline void z_srag(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int64, sign extended inline void z_sll( Register r1, int64_t d2, Register b2=Z_R0); // shift left r1 = r1 << ((d2+b2)&0x3f) ; int32, zeros added + inline void z_sllk(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left r1 = r3 << ((d2+b2)&0x3f) ; int32, zeros added inline void z_sllg(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left r1 = r3 << ((d2+b2)&0x3f) ; int64, zeros added inline void z_srl( Register r1, int64_t d2, Register b2=Z_R0); // shift right r1 = r1 >> ((d2+b2)&0x3f) ; int32, zero extended + inline void z_srlk(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int32, zero extended inline void z_srlg(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int64, zero extended // rotate @@ -3035,7 +3050,11 @@ class Assembler : public AbstractAssembler { inline void z_tam(); inline void z_stckf(int64_t d2, Register b2); + inline void z_stm( Register r1, Register r3, int64_t d2, Register b2); + inline void z_stmy(Register r1, Register r3, int64_t d2, Register b2); inline void z_stmg(Register r1, Register r3, int64_t d2, Register b2); + inline void z_lm( Register r1, Register r3, int64_t d2, Register b2); + inline void z_lmy(Register r1, Register r3, int64_t d2, Register b2); inline void z_lmg(Register r1, Register r3, int64_t d2, Register b2); inline void z_cs( Register r1, Register r3, int64_t d2, Register b2); diff --git a/src/hotspot/cpu/s390/assembler_s390.inline.hpp b/src/hotspot/cpu/s390/assembler_s390.inline.hpp index 19c472787c5..583d86e18b0 100644 --- a/src/hotspot/cpu/s390/assembler_s390.inline.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.inline.hpp @@ -334,12 +334,16 @@ inline void Assembler::z_stfle(int64_t d2, Register b2) { emit_32(STFLE_ZOPC | u // SHIFT/RORATE OPERATIONS //----------------------------------- inline void Assembler::z_sla( Register r1, int64_t d2, Register b2) { emit_32( SLA_ZOPC | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); } +inline void Assembler::z_slak(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLAK_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_slag(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLAG_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_sra( Register r1, int64_t d2, Register b2) { emit_32( SRA_ZOPC | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); } +inline void Assembler::z_srak(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRAK_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_srag(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRAG_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_sll( Register r1, int64_t d2, Register b2) { emit_32( SLL_ZOPC | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); } +inline void Assembler::z_sllk(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLLK_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_sllg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLLG_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_srl( Register r1, int64_t d2, Register b2) { emit_32( SRL_ZOPC | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); } +inline void Assembler::z_srlk(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRLK_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } inline void Assembler::z_srlg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRLG_ZOPC | regt(r1, 8, 48) | simm20(d2) | reg(b2, 16, 48) | reg(r3, 12, 48)); } // rotate left @@ -690,10 +694,14 @@ inline void Assembler::z_ahhlr(Register r1, Register r2, Register r3) { emit_32( inline void Assembler::z_tam() { emit_16( TAM_ZOPC); } inline void Assembler::z_stckf(int64_t d2, Register b2) { emit_32( STCKF_ZOPC | uimm12(d2, 20, 32) | regz(b2, 16, 32)); } -inline void Assembler::z_stmg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMG_ZOPC | simm20(d2) | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) ); } -inline void Assembler::z_lmg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( LMG_ZOPC | simm20(d2) | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) ); } +inline void Assembler::z_stm( Register r1, Register r3, int64_t d2, Register b2) { emit_32( STM_ZOPC | reg(r1, 8, 32) | reg(r3,12,32)| reg(b2,16,32) | uimm12(d2, 20,32)); } +inline void Assembler::z_stmy(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMY_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); } +inline void Assembler::z_stmg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMG_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); } +inline void Assembler::z_lm( Register r1, Register r3, int64_t d2, Register b2) { emit_32( LM_ZOPC | reg(r1, 8, 32) | reg(r3,12,32)| reg(b2,16,32) | uimm12(d2, 20,32)); } +inline void Assembler::z_lmy( Register r1, Register r3, int64_t d2, Register b2) { emit_48( LMY_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); } +inline void Assembler::z_lmg( Register r1, Register r3, int64_t d2, Register b2) { emit_48( LMG_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); } -inline void Assembler::z_cs(Register r1, Register r3, int64_t d2, Register b2) { emit_32( CS_ZOPC | regt(r1, 8, 32) | reg(r3, 12, 32) | reg(b2, 16, 32) | uimm12(d2, 20, 32)); } +inline void Assembler::z_cs( Register r1, Register r3, int64_t d2, Register b2) { emit_32( CS_ZOPC | regt(r1, 8, 32) | reg(r3, 12, 32) | reg(b2, 16, 32) | uimm12(d2, 20, 32)); } inline void Assembler::z_csy(Register r1, Register r3, int64_t d2, Register b2) { emit_48( CSY_ZOPC | regt(r1, 8, 48) | reg(r3, 12, 48) | reg(b2, 16, 48) | simm20(d2)); } inline void Assembler::z_csg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( CSG_ZOPC | regt(r1, 8, 48) | reg(r3, 12, 48) | reg(b2, 16, 48) | simm20(d2)); } inline void Assembler::z_cs( Register r1, Register r3, const Address& a) { assert(!a.has_index(), "Cannot encode index"); z_cs( r1, r3, a.disp(), a.baseOrR0()); } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index af2c02934ff..069e54816ea 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -936,7 +936,7 @@ void MacroAssembler::load_long_pcrelative(Register Rdst, address dataLocation) { // Some extra safety net. if (!RelAddr::is_in_range_of_RelAddr32(total_distance)) { - guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "too far away"); + guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "load_long_pcrelative can't handle distance " INTPTR_FORMAT, total_distance); } (this)->relocate(rspec, relocInfo::pcrel_addr_format); @@ -956,7 +956,7 @@ void MacroAssembler::load_addr_pcrelative(Register Rdst, address addrLocation) { // Some extra safety net. if (!RelAddr::is_in_range_of_RelAddr32(total_distance)) { - guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "too far away"); + guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "load_long_pcrelative can't handle distance " INTPTR_FORMAT, total_distance); } (this)->relocate(rspec, relocInfo::pcrel_addr_format); @@ -1025,6 +1025,13 @@ void MacroAssembler::testbit(Register r, unsigned int bitPos) { } } +void MacroAssembler::prefetch_read(Address a) { + z_pfd(1, a.disp20(), a.indexOrR0(), a.base()); +} +void MacroAssembler::prefetch_update(Address a) { + z_pfd(2, a.disp20(), a.indexOrR0(), a.base()); +} + // Clear a register, i.e. load const zero into reg. // Return len (in bytes) of generated instruction(s). // whole_reg: Clear 64 bits if true, 32 bits otherwise. @@ -4896,77 +4903,295 @@ unsigned int MacroAssembler::CopyRawMemory_AlignedDisjoint(Register src_reg, Reg // Intrinsics for CompactStrings -// Compress char[] to byte[]. odd_reg contains cnt. Kills dst. Early clobber: result +// Compress char[] to byte[]. +// Restores: src, dst +// Uses: cnt +// Kills: tmp, Z_R0, Z_R1. +// Early clobber: result. +// Note: +// cnt is signed int. Do not rely on high word! +// counts # characters, not bytes. // The result is the number of characters copied before the first incompatible character was found. -// If tmp2 is provided and the compression fails, the compression stops exactly at this point and the result is precise. +// If precise is true, the processing stops exactly at this point. Otherwise, the result may be off +// by a few bytes. The result always indicates the number of copied characters. // // Note: Does not behave exactly like package private StringUTF16 compress java implementation in case of failure: -// - Different number of characters may have been written to dead array (if tmp2 not provided). +// - Different number of characters may have been written to dead array (if precise is false). // - Returns a number <cnt instead of 0. (Result gets compared with cnt.) -unsigned int MacroAssembler::string_compress(Register result, Register src, Register dst, Register odd_reg, - Register even_reg, Register tmp, Register tmp2) { - int block_start = offset(); - Label Lloop1, Lloop2, Lslow, Ldone; - const Register addr2 = dst, ind1 = result, mask = tmp; - const bool precise = (tmp2 != noreg); +unsigned int MacroAssembler::string_compress(Register result, Register src, Register dst, Register cnt, + Register tmp, bool precise) { + assert_different_registers(Z_R0, Z_R1, src, dst, cnt, tmp); - BLOCK_COMMENT("string_compress {"); - - z_sll(odd_reg, 1); // Number of bytes to read. (Must be a positive simm32.) - clear_reg(ind1); // Index to read. - z_llilf(mask, 0xFF00FF00); - z_ahi(odd_reg, -16); // Last possible index for fast loop. - z_brl(Lslow); - - // ind1: index, even_reg: index increment, odd_reg: index limit - z_iihf(mask, 0xFF00FF00); - z_lhi(even_reg, 16); - - bind(Lloop1); // 8 Characters per iteration. - z_lg(Z_R0, Address(src, ind1)); - z_lg(Z_R1, Address(src, ind1, 8)); if (precise) { + BLOCK_COMMENT("encode_iso_array {"); + } else { + BLOCK_COMMENT("string_compress {"); + } + int block_start = offset(); + + Register Rsrc = src; + Register Rdst = dst; + Register Rix = tmp; + Register Rcnt = cnt; + Register Rmask = result; // holds incompatibility check mask until result value is stored. + Label ScalarShortcut, AllDone; + + z_iilf(Rmask, 0xFF00FF00); + z_iihf(Rmask, 0xFF00FF00); + +#if 0 // Sacrifice shortcuts for code compactness + { + //---< shortcuts for short strings (very frequent) >--- + // Strings with 4 and 8 characters were fond to occur very frequently. + // Therefore, we handle them right away with minimal overhead. + Label skipShortcut, skip4Shortcut, skip8Shortcut; + Register Rout = Z_R0; + z_chi(Rcnt, 4); + z_brne(skip4Shortcut); // 4 characters are very frequent + z_lg(Z_R0, 0, Rsrc); // Treat exactly 4 characters specially. + if (VM_Version::has_DistinctOpnds()) { + Rout = Z_R0; + z_ngrk(Rix, Z_R0, Rmask); + } else { + Rout = Rix; + z_lgr(Rix, Z_R0); + z_ngr(Z_R0, Rmask); + } + z_brnz(skipShortcut); + z_stcmh(Rout, 5, 0, Rdst); + z_stcm(Rout, 5, 2, Rdst); + z_lgfr(result, Rcnt); + z_bru(AllDone); + bind(skip4Shortcut); + + z_chi(Rcnt, 8); + z_brne(skip8Shortcut); // There's more to do... + z_lmg(Z_R0, Z_R1, 0, Rsrc); // Treat exactly 8 characters specially. + if (VM_Version::has_DistinctOpnds()) { + Rout = Z_R0; + z_ogrk(Rix, Z_R0, Z_R1); + z_ngr(Rix, Rmask); + } else { + Rout = Rix; + z_lgr(Rix, Z_R0); + z_ogr(Z_R0, Z_R1); + z_ngr(Z_R0, Rmask); + } + z_brnz(skipShortcut); + z_stcmh(Rout, 5, 0, Rdst); + z_stcm(Rout, 5, 2, Rdst); + z_stcmh(Z_R1, 5, 4, Rdst); + z_stcm(Z_R1, 5, 6, Rdst); + z_lgfr(result, Rcnt); + z_bru(AllDone); + + bind(skip8Shortcut); + clear_reg(Z_R0, true, false); // #characters already processed (none). Precond for scalar loop. + z_brl(ScalarShortcut); // Just a few characters + + bind(skipShortcut); + } +#endif + clear_reg(Z_R0); // make sure register is properly initialized. + + if (VM_Version::has_VectorFacility()) { + const int min_vcnt = 32; // Minimum #characters required to use vector instructions. + // Otherwise just do nothing in vector mode. + // Must be multiple of 2*(vector register length in chars (8 HW = 128 bits)). + const int log_min_vcnt = exact_log2(min_vcnt); + Label VectorLoop, VectorDone, VectorBreak; + + VectorRegister Vtmp1 = Z_V16; + VectorRegister Vtmp2 = Z_V17; + VectorRegister Vmask = Z_V18; + VectorRegister Vzero = Z_V19; + VectorRegister Vsrc_first = Z_V20; + VectorRegister Vsrc_last = Z_V23; + + assert((Vsrc_last->encoding() - Vsrc_first->encoding() + 1) == min_vcnt/8, "logic error"); + assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()"); + z_srak(Rix, Rcnt, log_min_vcnt); // # vector loop iterations + z_brz(VectorDone); // not enough data for vector loop + + z_vzero(Vzero); // all zeroes + z_vgmh(Vmask, 0, 7); // generate 0xff00 mask for all 2-byte elements + z_sllg(Z_R0, Rix, log_min_vcnt); // remember #chars that will be processed by vector loop + + bind(VectorLoop); + z_vlm(Vsrc_first, Vsrc_last, 0, Rsrc); + add2reg(Rsrc, min_vcnt*2); + + //---< check for incompatible character >--- + z_vo(Vtmp1, Z_V20, Z_V21); + z_vo(Vtmp2, Z_V22, Z_V23); + z_vo(Vtmp1, Vtmp1, Vtmp2); + z_vn(Vtmp1, Vtmp1, Vmask); + z_vceqhs(Vtmp1, Vtmp1, Vzero); // high half of all chars must be zero for successful compress. + z_brne(VectorBreak); // break vector loop, incompatible character found. + // re-process data from current iteration in break handler. + + //---< pack & store characters >--- + z_vpkh(Vtmp1, Z_V20, Z_V21); // pack (src1, src2) -> tmp1 + z_vpkh(Vtmp2, Z_V22, Z_V23); // pack (src3, src4) -> tmp2 + z_vstm(Vtmp1, Vtmp2, 0, Rdst); // store packed string + add2reg(Rdst, min_vcnt); + + z_brct(Rix, VectorLoop); + + z_bru(VectorDone); + + bind(VectorBreak); + z_sll(Rix, log_min_vcnt); // # chars processed so far in VectorLoop, excl. current iteration. + z_sr(Z_R0, Rix); // correct # chars processed in total. + + bind(VectorDone); + } + + { + const int min_cnt = 8; // Minimum #characters required to use unrolled loop. + // Otherwise just do nothing in unrolled loop. + // Must be multiple of 8. + const int log_min_cnt = exact_log2(min_cnt); + Label UnrolledLoop, UnrolledDone, UnrolledBreak; + if (VM_Version::has_DistinctOpnds()) { - z_ogrk(tmp2, Z_R0, Z_R1); + z_srk(Rix, Rcnt, Z_R0); // remaining # chars to compress in unrolled loop } else { - z_lgr(tmp2, Z_R0); - z_ogr(tmp2, Z_R1); + z_lr(Rix, Rcnt); + z_sr(Rix, Z_R0); } - z_ngr(tmp2, mask); - z_brne(Lslow); // Failed fast case, retry slowly. + z_sra(Rix, log_min_cnt); // unrolled loop count + z_brz(UnrolledDone); + + bind(UnrolledLoop); + z_lmg(Z_R0, Z_R1, 0, Rsrc); + if (precise) { + z_ogr(Z_R1, Z_R0); // check all 8 chars for incompatibility + z_ngr(Z_R1, Rmask); + z_brnz(UnrolledBreak); + + z_lg(Z_R1, 8, Rsrc); // reload destroyed register + z_stcmh(Z_R0, 5, 0, Rdst); + z_stcm(Z_R0, 5, 2, Rdst); + } else { + z_stcmh(Z_R0, 5, 0, Rdst); + z_stcm(Z_R0, 5, 2, Rdst); + + z_ogr(Z_R0, Z_R1); + z_ngr(Z_R0, Rmask); + z_brnz(UnrolledBreak); + } + z_stcmh(Z_R1, 5, 4, Rdst); + z_stcm(Z_R1, 5, 6, Rdst); + + add2reg(Rsrc, min_cnt*2); + add2reg(Rdst, min_cnt); + z_brct(Rix, UnrolledLoop); + + z_lgfr(Z_R0, Rcnt); // # chars processed in total after unrolled loop. + z_nilf(Z_R0, ~(min_cnt-1)); + z_tmll(Rcnt, min_cnt-1); + z_brnaz(ScalarShortcut); // if all bits zero, there is nothing left to do for scalar loop. + // Rix == 0 in all cases. + z_lgfr(result, Rcnt); // all characters processed. + z_sgfr(Rdst, Rcnt); // restore ptr + z_sgfr(Rsrc, Rcnt); // restore ptr, double the element count for Rsrc restore + z_sgfr(Rsrc, Rcnt); + z_bru(AllDone); + + bind(UnrolledBreak); + z_lgfr(Z_R0, Rcnt); // # chars processed in total after unrolled loop + z_nilf(Z_R0, ~(min_cnt-1)); + z_sll(Rix, log_min_cnt); // # chars processed so far in UnrolledLoop, excl. current iteration. + z_sr(Z_R0, Rix); // correct # chars processed in total. + if (!precise) { + z_lgfr(result, Z_R0); + z_aghi(result, min_cnt/2); // min_cnt/2 characters have already been written + // but ptrs were not updated yet. + z_sgfr(Rdst, Z_R0); // restore ptr + z_sgfr(Rsrc, Z_R0); // restore ptr, double the element count for Rsrc restore + z_sgfr(Rsrc, Z_R0); + z_bru(AllDone); + } + bind(UnrolledDone); } - z_stcmh(Z_R0, 5, 0, addr2); - z_stcm(Z_R0, 5, 2, addr2); - if (!precise) { z_ogr(Z_R0, Z_R1); } - z_stcmh(Z_R1, 5, 4, addr2); - z_stcm(Z_R1, 5, 6, addr2); - if (!precise) { - z_ngr(Z_R0, mask); - z_brne(Ldone); // Failed (more than needed was written). + + { + Label ScalarLoop, ScalarDone, ScalarBreak; + + bind(ScalarShortcut); + z_ltgfr(result, Rcnt); + z_brz(AllDone); + +#if 0 // Sacrifice shortcuts for code compactness + { + //---< Special treatment for very short strings (one or two characters) >--- + // For these strings, we are sure that the above code was skipped. + // Thus, no registers were modified, register restore is not required. + Label ScalarDoit, Scalar2Char; + z_chi(Rcnt, 2); + z_brh(ScalarDoit); + z_llh(Z_R1, 0, Z_R0, Rsrc); + z_bre(Scalar2Char); + z_tmll(Z_R1, 0xff00); + z_lghi(result, 0); // cnt == 1, first char invalid, no chars successfully processed + z_brnaz(AllDone); + z_stc(Z_R1, 0, Z_R0, Rdst); + z_lghi(result, 1); + z_bru(AllDone); + + bind(Scalar2Char); + z_llh(Z_R0, 2, Z_R0, Rsrc); + z_tmll(Z_R1, 0xff00); + z_lghi(result, 0); // cnt == 2, first char invalid, no chars successfully processed + z_brnaz(AllDone); + z_stc(Z_R1, 0, Z_R0, Rdst); + z_tmll(Z_R0, 0xff00); + z_lghi(result, 1); // cnt == 2, second char invalid, one char successfully processed + z_brnaz(AllDone); + z_stc(Z_R0, 1, Z_R0, Rdst); + z_lghi(result, 2); + z_bru(AllDone); + + bind(ScalarDoit); + } +#endif + + if (VM_Version::has_DistinctOpnds()) { + z_srk(Rix, Rcnt, Z_R0); // remaining # chars to compress in unrolled loop + } else { + z_lr(Rix, Rcnt); + z_sr(Rix, Z_R0); + } + z_lgfr(result, Rcnt); // # processed characters (if all runs ok). + z_brz(ScalarDone); + + bind(ScalarLoop); + z_llh(Z_R1, 0, Z_R0, Rsrc); + z_tmll(Z_R1, 0xff00); + z_brnaz(ScalarBreak); + z_stc(Z_R1, 0, Z_R0, Rdst); + add2reg(Rsrc, 2); + add2reg(Rdst, 1); + z_brct(Rix, ScalarLoop); + + z_bru(ScalarDone); + + bind(ScalarBreak); + z_sr(result, Rix); + + bind(ScalarDone); + z_sgfr(Rdst, result); // restore ptr + z_sgfr(Rsrc, result); // restore ptr, double the element count for Rsrc restore + z_sgfr(Rsrc, result); } - z_aghi(addr2, 8); - z_brxle(ind1, even_reg, Lloop1); - - bind(Lslow); - // Compute index limit and skip if negative. - z_ahi(odd_reg, 16-2); // Last possible index for slow loop. - z_lhi(even_reg, 2); - z_cr(ind1, odd_reg); - z_brh(Ldone); - - bind(Lloop2); // 1 Character per iteration. - z_llh(Z_R0, Address(src, ind1)); - z_tmll(Z_R0, 0xFF00); - z_brnaz(Ldone); // Failed slow case: Return number of written characters. - z_stc(Z_R0, Address(addr2)); - z_aghi(addr2, 1); - z_brxle(ind1, even_reg, Lloop2); - - bind(Ldone); // result = ind1 = 2*cnt - z_srl(ind1, 1); - - BLOCK_COMMENT("} string_compress"); + bind(AllDone); + if (precise) { + BLOCK_COMMENT("} encode_iso_array"); + } else { + BLOCK_COMMENT("} string_compress"); + } return offset() - block_start; } @@ -4997,53 +5222,432 @@ unsigned int MacroAssembler::string_inflate_trot(Register src, Register dst, Reg return offset() - block_start; } -// Inflate byte[] to char[]. odd_reg contains cnt. Kills src. -unsigned int MacroAssembler::string_inflate(Register src, Register dst, Register odd_reg, - Register even_reg, Register tmp) { - int block_start = offset(); +// Inflate byte[] to char[]. +// Restores: src, dst +// Uses: cnt +// Kills: tmp, Z_R0, Z_R1. +// Note: +// cnt is signed int. Do not rely on high word! +// counts # characters, not bytes. +unsigned int MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp) { + assert_different_registers(Z_R0, Z_R1, src, dst, cnt, tmp); BLOCK_COMMENT("string_inflate {"); + int block_start = offset(); - Label Lloop1, Lloop2, Lslow, Ldone; - const Register addr1 = src, ind2 = tmp; + Register Rcnt = cnt; // # characters (src: bytes, dst: char (2-byte)), remaining after current loop. + Register Rix = tmp; // loop index + Register Rsrc = src; // addr(src array) + Register Rdst = dst; // addr(dst array) + Label ScalarShortcut, AllDone; - z_sll(odd_reg, 1); // Number of bytes to write. (Must be a positive simm32.) - clear_reg(ind2); // Index to write. - z_ahi(odd_reg, -16); // Last possible index for fast loop. - z_brl(Lslow); +#if 0 // Sacrifice shortcuts for code compactness + { + //---< shortcuts for short strings (very frequent) >--- + Label skipShortcut, skip4Shortcut; + z_ltr(Rcnt, Rcnt); // absolutely nothing to do for strings of len == 0. + z_brz(AllDone); + clear_reg(Z_R0); // make sure registers are properly initialized. + clear_reg(Z_R1); + z_chi(Rcnt, 4); + z_brne(skip4Shortcut); // 4 characters are very frequent + z_icm(Z_R0, 5, 0, Rsrc); // Treat exactly 4 characters specially. + z_icm(Z_R1, 5, 2, Rsrc); + z_stm(Z_R0, Z_R1, 0, Rdst); + z_bru(AllDone); + bind(skip4Shortcut); - // ind2: index, even_reg: index increment, odd_reg: index limit - clear_reg(Z_R0); - clear_reg(Z_R1); - z_lhi(even_reg, 16); + z_chi(Rcnt, 8); + z_brh(skipShortcut); // There's a lot to do... + z_lgfr(Z_R0, Rcnt); // remaining #characters (<= 8). Precond for scalar loop. + // This does not destroy the "register cleared" state of Z_R0. + z_brl(ScalarShortcut); // Just a few characters + z_icmh(Z_R0, 5, 0, Rsrc); // Treat exactly 8 characters specially. + z_icmh(Z_R1, 5, 4, Rsrc); + z_icm(Z_R0, 5, 2, Rsrc); + z_icm(Z_R1, 5, 6, Rsrc); + z_stmg(Z_R0, Z_R1, 0, Rdst); + z_bru(AllDone); + bind(skipShortcut); + } +#endif + clear_reg(Z_R0); // make sure register is properly initialized. - bind(Lloop1); // 8 Characters per iteration. - z_icmh(Z_R0, 5, 0, addr1); - z_icmh(Z_R1, 5, 4, addr1); - z_icm(Z_R0, 5, 2, addr1); - z_icm(Z_R1, 5, 6, addr1); - z_aghi(addr1, 8); - z_stg(Z_R0, Address(dst, ind2)); - z_stg(Z_R1, Address(dst, ind2, 8)); - z_brxle(ind2, even_reg, Lloop1); + if (VM_Version::has_VectorFacility()) { + const int min_vcnt = 32; // Minimum #characters required to use vector instructions. + // Otherwise just do nothing in vector mode. + // Must be multiple of vector register length (16 bytes = 128 bits). + const int log_min_vcnt = exact_log2(min_vcnt); + Label VectorLoop, VectorDone; - bind(Lslow); - // Compute index limit and skip if negative. - z_ahi(odd_reg, 16-2); // Last possible index for slow loop. - z_lhi(even_reg, 2); - z_cr(ind2, odd_reg); - z_brh(Ldone); + assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()"); + z_srak(Rix, Rcnt, log_min_vcnt); // calculate # vector loop iterations + z_brz(VectorDone); // skip if none - bind(Lloop2); // 1 Character per iteration. - z_llc(Z_R0, Address(addr1)); - z_sth(Z_R0, Address(dst, ind2)); - z_aghi(addr1, 1); - z_brxle(ind2, even_reg, Lloop2); + z_sllg(Z_R0, Rix, log_min_vcnt); // remember #chars that will be processed by vector loop - bind(Ldone); + bind(VectorLoop); + z_vlm(Z_V20, Z_V21, 0, Rsrc); // get next 32 characters (single-byte) + add2reg(Rsrc, min_vcnt); + + z_vuplhb(Z_V22, Z_V20); // V2 <- (expand) V0(high) + z_vupllb(Z_V23, Z_V20); // V3 <- (expand) V0(low) + z_vuplhb(Z_V24, Z_V21); // V4 <- (expand) V1(high) + z_vupllb(Z_V25, Z_V21); // V5 <- (expand) V1(low) + z_vstm(Z_V22, Z_V25, 0, Rdst); // store next 32 bytes + add2reg(Rdst, min_vcnt*2); + + z_brct(Rix, VectorLoop); + + bind(VectorDone); + } + + const int min_cnt = 8; // Minimum #characters required to use unrolled scalar loop. + // Otherwise just do nothing in unrolled scalar mode. + // Must be multiple of 8. + { + const int log_min_cnt = exact_log2(min_cnt); + Label UnrolledLoop, UnrolledDone; + + + if (VM_Version::has_DistinctOpnds()) { + z_srk(Rix, Rcnt, Z_R0); // remaining # chars to process in unrolled loop + } else { + z_lr(Rix, Rcnt); + z_sr(Rix, Z_R0); + } + z_sra(Rix, log_min_cnt); // unrolled loop count + z_brz(UnrolledDone); + + clear_reg(Z_R0); + clear_reg(Z_R1); + + bind(UnrolledLoop); + z_icmh(Z_R0, 5, 0, Rsrc); + z_icmh(Z_R1, 5, 4, Rsrc); + z_icm(Z_R0, 5, 2, Rsrc); + z_icm(Z_R1, 5, 6, Rsrc); + add2reg(Rsrc, min_cnt); + + z_stmg(Z_R0, Z_R1, 0, Rdst); + + add2reg(Rdst, min_cnt*2); + z_brct(Rix, UnrolledLoop); + + bind(UnrolledDone); + z_lgfr(Z_R0, Rcnt); // # chars left over after unrolled loop. + z_nilf(Z_R0, min_cnt-1); + z_brnz(ScalarShortcut); // if zero, there is nothing left to do for scalar loop. + // Rix == 0 in all cases. + z_sgfr(Z_R0, Rcnt); // negative # characters the ptrs have been advanced previously. + z_agr(Rdst, Z_R0); // restore ptr, double the element count for Rdst restore. + z_agr(Rdst, Z_R0); + z_agr(Rsrc, Z_R0); // restore ptr. + z_bru(AllDone); + } + + { + bind(ScalarShortcut); + // Z_R0 must contain remaining # characters as 64-bit signed int here. + // register contents is preserved over scalar processing (for register fixup). + +#if 0 // Sacrifice shortcuts for code compactness + { + Label ScalarDefault; + z_chi(Rcnt, 2); + z_brh(ScalarDefault); + z_llc(Z_R0, 0, Z_R0, Rsrc); // 6 bytes + z_sth(Z_R0, 0, Z_R0, Rdst); // 4 bytes + z_brl(AllDone); + z_llc(Z_R0, 1, Z_R0, Rsrc); // 6 bytes + z_sth(Z_R0, 2, Z_R0, Rdst); // 4 bytes + z_bru(AllDone); + bind(ScalarDefault); + } +#endif + + Label CodeTable; + // Some comments on Rix calculation: + // - Rcnt is small, therefore no bits shifted out of low word (sll(g) instructions). + // - high word of both Rix and Rcnt may contain garbage + // - the final lngfr takes care of that garbage, extending the sign to high word + z_sllg(Rix, Z_R0, 2); // calculate 10*Rix = (4*Rix + Rix)*2 + z_ar(Rix, Z_R0); + z_larl(Z_R1, CodeTable); + z_sll(Rix, 1); + z_lngfr(Rix, Rix); // ix range: [0..7], after inversion & mult: [-(7*12)..(0*12)]. + z_bc(Assembler::bcondAlways, 0, Rix, Z_R1); + + z_llc(Z_R1, 6, Z_R0, Rsrc); // 6 bytes + z_sth(Z_R1, 12, Z_R0, Rdst); // 4 bytes + + z_llc(Z_R1, 5, Z_R0, Rsrc); + z_sth(Z_R1, 10, Z_R0, Rdst); + + z_llc(Z_R1, 4, Z_R0, Rsrc); + z_sth(Z_R1, 8, Z_R0, Rdst); + + z_llc(Z_R1, 3, Z_R0, Rsrc); + z_sth(Z_R1, 6, Z_R0, Rdst); + + z_llc(Z_R1, 2, Z_R0, Rsrc); + z_sth(Z_R1, 4, Z_R0, Rdst); + + z_llc(Z_R1, 1, Z_R0, Rsrc); + z_sth(Z_R1, 2, Z_R0, Rdst); + + z_llc(Z_R1, 0, Z_R0, Rsrc); + z_sth(Z_R1, 0, Z_R0, Rdst); + bind(CodeTable); + + z_chi(Rcnt, 8); // no fixup for small strings. Rdst, Rsrc were not modified. + z_brl(AllDone); + + z_sgfr(Z_R0, Rcnt); // # characters the ptrs have been advanced previously. + z_agr(Rdst, Z_R0); // restore ptr, double the element count for Rdst restore. + z_agr(Rdst, Z_R0); + z_agr(Rsrc, Z_R0); // restore ptr. + } + bind(AllDone); BLOCK_COMMENT("} string_inflate"); + return offset() - block_start; +} +// Inflate byte[] to char[], length known at compile time. +// Restores: src, dst +// Kills: tmp, Z_R0, Z_R1. +// Note: +// len is signed int. Counts # characters, not bytes. +unsigned int MacroAssembler::string_inflate_const(Register src, Register dst, Register tmp, int len) { + assert_different_registers(Z_R0, Z_R1, src, dst, tmp); + + BLOCK_COMMENT("string_inflate_const {"); + int block_start = offset(); + + Register Rix = tmp; // loop index + Register Rsrc = src; // addr(src array) + Register Rdst = dst; // addr(dst array) + Label ScalarShortcut, AllDone; + int nprocessed = 0; + int src_off = 0; // compensate for saved (optimized away) ptr advancement. + int dst_off = 0; // compensate for saved (optimized away) ptr advancement. + bool restore_inputs = false; + bool workreg_clear = false; + + if ((len >= 32) && VM_Version::has_VectorFacility()) { + const int min_vcnt = 32; // Minimum #characters required to use vector instructions. + // Otherwise just do nothing in vector mode. + // Must be multiple of vector register length (16 bytes = 128 bits). + const int log_min_vcnt = exact_log2(min_vcnt); + const int iterations = (len - nprocessed) >> log_min_vcnt; + nprocessed += iterations << log_min_vcnt; + Label VectorLoop; + + if (iterations == 1) { + z_vlm(Z_V20, Z_V21, 0+src_off, Rsrc); // get next 32 characters (single-byte) + z_vuplhb(Z_V22, Z_V20); // V2 <- (expand) V0(high) + z_vupllb(Z_V23, Z_V20); // V3 <- (expand) V0(low) + z_vuplhb(Z_V24, Z_V21); // V4 <- (expand) V1(high) + z_vupllb(Z_V25, Z_V21); // V5 <- (expand) V1(low) + z_vstm(Z_V22, Z_V25, 0+dst_off, Rdst); // store next 32 bytes + + src_off += min_vcnt; + dst_off += min_vcnt*2; + } else { + restore_inputs = true; + + z_lgfi(Rix, len>>log_min_vcnt); + bind(VectorLoop); + z_vlm(Z_V20, Z_V21, 0, Rsrc); // get next 32 characters (single-byte) + add2reg(Rsrc, min_vcnt); + + z_vuplhb(Z_V22, Z_V20); // V2 <- (expand) V0(high) + z_vupllb(Z_V23, Z_V20); // V3 <- (expand) V0(low) + z_vuplhb(Z_V24, Z_V21); // V4 <- (expand) V1(high) + z_vupllb(Z_V25, Z_V21); // V5 <- (expand) V1(low) + z_vstm(Z_V22, Z_V25, 0, Rdst); // store next 32 bytes + add2reg(Rdst, min_vcnt*2); + + z_brct(Rix, VectorLoop); + } + } + + if (((len-nprocessed) >= 16) && VM_Version::has_VectorFacility()) { + const int min_vcnt = 16; // Minimum #characters required to use vector instructions. + // Otherwise just do nothing in vector mode. + // Must be multiple of vector register length (16 bytes = 128 bits). + const int log_min_vcnt = exact_log2(min_vcnt); + const int iterations = (len - nprocessed) >> log_min_vcnt; + nprocessed += iterations << log_min_vcnt; + assert(iterations == 1, "must be!"); + + z_vl(Z_V20, 0+src_off, Z_R0, Rsrc); // get next 16 characters (single-byte) + z_vuplhb(Z_V22, Z_V20); // V2 <- (expand) V0(high) + z_vupllb(Z_V23, Z_V20); // V3 <- (expand) V0(low) + z_vstm(Z_V22, Z_V23, 0+dst_off, Rdst); // store next 32 bytes + + src_off += min_vcnt; + dst_off += min_vcnt*2; + } + + if ((len-nprocessed) > 8) { + const int min_cnt = 8; // Minimum #characters required to use unrolled scalar loop. + // Otherwise just do nothing in unrolled scalar mode. + // Must be multiple of 8. + const int log_min_cnt = exact_log2(min_cnt); + const int iterations = (len - nprocessed) >> log_min_cnt; + nprocessed += iterations << log_min_cnt; + + //---< avoid loop overhead/ptr increment for small # iterations >--- + if (iterations <= 2) { + clear_reg(Z_R0); + clear_reg(Z_R1); + workreg_clear = true; + + z_icmh(Z_R0, 5, 0+src_off, Rsrc); + z_icmh(Z_R1, 5, 4+src_off, Rsrc); + z_icm(Z_R0, 5, 2+src_off, Rsrc); + z_icm(Z_R1, 5, 6+src_off, Rsrc); + z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); + + src_off += min_cnt; + dst_off += min_cnt*2; + } + + if (iterations == 2) { + z_icmh(Z_R0, 5, 0+src_off, Rsrc); + z_icmh(Z_R1, 5, 4+src_off, Rsrc); + z_icm(Z_R0, 5, 2+src_off, Rsrc); + z_icm(Z_R1, 5, 6+src_off, Rsrc); + z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); + + src_off += min_cnt; + dst_off += min_cnt*2; + } + + if (iterations > 2) { + Label UnrolledLoop; + restore_inputs = true; + + clear_reg(Z_R0); + clear_reg(Z_R1); + workreg_clear = true; + + z_lgfi(Rix, iterations); + bind(UnrolledLoop); + z_icmh(Z_R0, 5, 0, Rsrc); + z_icmh(Z_R1, 5, 4, Rsrc); + z_icm(Z_R0, 5, 2, Rsrc); + z_icm(Z_R1, 5, 6, Rsrc); + add2reg(Rsrc, min_cnt); + + z_stmg(Z_R0, Z_R1, 0, Rdst); + add2reg(Rdst, min_cnt*2); + + z_brct(Rix, UnrolledLoop); + } + } + + if ((len-nprocessed) > 0) { + switch (len-nprocessed) { + case 8: + if (!workreg_clear) { + clear_reg(Z_R0); + clear_reg(Z_R1); + } + z_icmh(Z_R0, 5, 0+src_off, Rsrc); + z_icmh(Z_R1, 5, 4+src_off, Rsrc); + z_icm(Z_R0, 5, 2+src_off, Rsrc); + z_icm(Z_R1, 5, 6+src_off, Rsrc); + z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); + break; + case 7: + if (!workreg_clear) { + clear_reg(Z_R0); + clear_reg(Z_R1); + } + clear_reg(Rix); + z_icm(Z_R0, 5, 0+src_off, Rsrc); + z_icm(Z_R1, 5, 2+src_off, Rsrc); + z_icm(Rix, 5, 4+src_off, Rsrc); + z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); + z_llc(Z_R0, 6+src_off, Z_R0, Rsrc); + z_st(Rix, 8+dst_off, Z_R0, Rdst); + z_sth(Z_R0, 12+dst_off, Z_R0, Rdst); + break; + case 6: + if (!workreg_clear) { + clear_reg(Z_R0); + clear_reg(Z_R1); + } + clear_reg(Rix); + z_icm(Z_R0, 5, 0+src_off, Rsrc); + z_icm(Z_R1, 5, 2+src_off, Rsrc); + z_icm(Rix, 5, 4+src_off, Rsrc); + z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); + z_st(Rix, 8+dst_off, Z_R0, Rdst); + break; + case 5: + if (!workreg_clear) { + clear_reg(Z_R0); + clear_reg(Z_R1); + } + z_icm(Z_R0, 5, 0+src_off, Rsrc); + z_icm(Z_R1, 5, 2+src_off, Rsrc); + z_llc(Rix, 4+src_off, Z_R0, Rsrc); + z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); + z_sth(Rix, 8+dst_off, Z_R0, Rdst); + break; + case 4: + if (!workreg_clear) { + clear_reg(Z_R0); + clear_reg(Z_R1); + } + z_icm(Z_R0, 5, 0+src_off, Rsrc); + z_icm(Z_R1, 5, 2+src_off, Rsrc); + z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); + break; + case 3: + if (!workreg_clear) { + clear_reg(Z_R0); + } + z_llc(Z_R1, 2+src_off, Z_R0, Rsrc); + z_icm(Z_R0, 5, 0+src_off, Rsrc); + z_sth(Z_R1, 4+dst_off, Z_R0, Rdst); + z_st(Z_R0, 0+dst_off, Rdst); + break; + case 2: + z_llc(Z_R0, 0+src_off, Z_R0, Rsrc); + z_llc(Z_R1, 1+src_off, Z_R0, Rsrc); + z_sth(Z_R0, 0+dst_off, Z_R0, Rdst); + z_sth(Z_R1, 2+dst_off, Z_R0, Rdst); + break; + case 1: + z_llc(Z_R0, 0+src_off, Z_R0, Rsrc); + z_sth(Z_R0, 0+dst_off, Z_R0, Rdst); + break; + default: + guarantee(false, "Impossible"); + break; + } + src_off += len-nprocessed; + dst_off += (len-nprocessed)*2; + nprocessed = len; + } + + //---< restore modified input registers >--- + if ((nprocessed > 0) && restore_inputs) { + z_agfi(Rsrc, -(nprocessed-src_off)); + if (nprocessed < 1000000000) { // avoid int overflow + z_agfi(Rdst, -(nprocessed*2-dst_off)); + } else { + z_agfi(Rdst, -(nprocessed-dst_off)); + z_agfi(Rdst, -nprocessed); + } + } + + BLOCK_COMMENT("} string_inflate_const"); return offset() - block_start; } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 908ce8d98aa..8fb0731747d 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -198,6 +198,9 @@ class MacroAssembler: public Assembler { // Test a bit in a register. Result is reflected in CC. void testbit(Register r, unsigned int bitPos); + void prefetch_read(Address a); + void prefetch_update(Address a); + // Clear a register, i.e. load const zero into reg. Return len (in bytes) of // generated instruction(s). // whole_reg: Clear 64 bits if true, 32 bits otherwise. @@ -836,7 +839,7 @@ class MacroAssembler: public Assembler { void load_mirror(Register mirror, Register method); //-------------------------- - //--- perations on arrays. + //--- Operations on arrays. //-------------------------- unsigned int Clear_Array(Register cnt_arg, Register base_pointer_arg, Register src_addr, Register src_len); unsigned int Clear_Array_Const(long cnt, Register base); @@ -849,20 +852,34 @@ class MacroAssembler: public Assembler { // Special String Intrinsics Implementation. //------------------------------------------- // Intrinsics for CompactStrings - // Compress char[] to byte[]. odd_reg contains cnt. tmp3 is only needed for precise behavior in failure case. Kills dst. - unsigned int string_compress(Register result, Register src, Register dst, Register odd_reg, - Register even_reg, Register tmp, Register tmp2 = noreg); + // Restores: src, dst + // Uses: cnt + // Kills: tmp, Z_R0, Z_R1. + // Early clobber: result. + // Boolean precise controls accuracy of result value. + unsigned int string_compress(Register result, Register src, Register dst, Register cnt, + Register tmp, bool precise); + + // Inflate byte[] to char[]. + unsigned int string_inflate_trot(Register src, Register dst, Register cnt, Register tmp); + + // Inflate byte[] to char[]. + // Restores: src, dst + // Uses: cnt + // Kills: tmp, Z_R0, Z_R1. + unsigned int string_inflate(Register src, Register dst, Register cnt, Register tmp); + + // Inflate byte[] to char[], length known at compile time. + // Restores: src, dst + // Kills: tmp, Z_R0, Z_R1. + // Note: + // len is signed int. Counts # characters, not bytes. + unsigned int string_inflate_const(Register src, Register dst, Register tmp, int len); // Kills src. unsigned int has_negatives(Register result, Register src, Register cnt, Register odd_reg, Register even_reg, Register tmp); - // Inflate byte[] to char[]. - unsigned int string_inflate_trot(Register src, Register dst, Register cnt, Register tmp); - // Odd_reg contains cnt. Kills src. - unsigned int string_inflate(Register src, Register dst, Register odd_reg, - Register even_reg, Register tmp); - unsigned int string_compare(Register str1, Register str2, Register cnt1, Register cnt2, Register odd_reg, Register even_reg, Register result, int ae); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 15902d9f7aa..fb876ba6180 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -10267,14 +10267,14 @@ instruct indexOf_UL(iRegP haystack, rarg2RegI haycnt, iRegP needle, rarg5RegI ne %} // char[] to byte[] compression -instruct string_compress(iRegP src, rarg5RegP dst, iRegI result, roddRegI len, revenRegI evenReg, iRegI tmp, flagsReg cr) %{ +instruct string_compress(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{ match(Set result (StrCompressedCopy src (Binary dst len))); - effect(TEMP_DEF result, USE_KILL dst, USE_KILL len, TEMP evenReg, TEMP tmp, KILL cr); // R0, R1 are killed, too. + effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too. ins_cost(300); format %{ "String Compress $src->$dst($len) -> $result" %} ins_encode %{ __ string_compress($result$$Register, $src$$Register, $dst$$Register, $len$$Register, - $evenReg$$Register, $tmp$$Register); + $tmp$$Register, false); %} ins_pipe(pipe_class_dummy); %} @@ -10293,13 +10293,25 @@ instruct string_compress(iRegP src, rarg5RegP dst, iRegI result, roddRegI len, r //%} // byte[] to char[] inflation -instruct string_inflate(Universe dummy, rarg5RegP src, iRegP dst, roddRegI len, revenRegI evenReg, iRegI tmp, flagsReg cr) %{ +instruct string_inflate(Universe dummy, iRegP src, iRegP dst, iRegI len, iRegI tmp, flagsReg cr) %{ match(Set dummy (StrInflatedCopy src (Binary dst len))); - effect(USE_KILL src, USE_KILL len, TEMP evenReg, TEMP tmp, KILL cr); // R0, R1 are killed, too. + effect(TEMP tmp, KILL cr); // R0, R1 are killed, too. ins_cost(300); format %{ "String Inflate $src->$dst($len)" %} ins_encode %{ - __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $evenReg$$Register, $tmp$$Register); + __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register); + %} + ins_pipe(pipe_class_dummy); +%} + +// byte[] to char[] inflation +instruct string_inflate_const(Universe dummy, iRegP src, iRegP dst, iRegI tmp, immI len, flagsReg cr) %{ + match(Set dummy (StrInflatedCopy src (Binary dst len))); + effect(TEMP tmp, KILL cr); // R0, R1 are killed, too. + ins_cost(300); + format %{ "String Inflate (constLen) $src->$dst($len)" %} + ins_encode %{ + __ string_inflate_const($src$$Register, $dst$$Register, $tmp$$Register, $len$$constant); %} ins_pipe(pipe_class_dummy); %} @@ -10318,14 +10330,14 @@ instruct has_negatives(rarg5RegP ary1, iRegI len, iRegI result, roddRegI oddReg, %} // encode char[] to byte[] in ISO_8859_1 -instruct encode_iso_array(rarg5RegP src, iRegP dst, iRegI result, roddRegI len, revenRegI evenReg, iRegI tmp, iRegI tmp2, flagsReg cr) %{ +instruct encode_iso_array(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{ match(Set result (EncodeISOArray src (Binary dst len))); - effect(TEMP_DEF result, USE_KILL src, USE_KILL len, TEMP evenReg, TEMP tmp, TEMP tmp2, KILL cr); // R0, R1 are killed, too. + effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too. ins_cost(300); format %{ "Encode array $src->$dst($len) -> $result" %} ins_encode %{ __ string_compress($result$$Register, $src$$Register, $dst$$Register, $len$$Register, - $evenReg$$Register, $tmp$$Register, $tmp2$$Register); + $tmp$$Register, true); %} ins_pipe(pipe_class_dummy); %} From cc3aabe58002fc86b65c9e46f13df319ab8c498e Mon Sep 17 00:00:00 2001 From: Volker Simonis <simonis@openjdk.org> Date: Wed, 22 Nov 2017 17:57:27 +0100 Subject: [PATCH 004/165] 8187280: Remove unused methods from StubQueue Reviewed-by: kvn, kbarrett --- src/hotspot/share/code/stubs.cpp | 31 ------------------------------- src/hotspot/share/code/stubs.hpp | 7 ------- 2 files changed, 38 deletions(-) diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp index 56883bc623d..81717b919ba 100644 --- a/src/hotspot/share/code/stubs.cpp +++ b/src/hotspot/share/code/stubs.cpp @@ -78,7 +78,6 @@ StubQueue::StubQueue(StubInterface* stub_interface, int buffer_size, _queue_begin = 0; _queue_end = 0; _number_of_stubs = 0; - register_queue(this); } @@ -205,36 +204,6 @@ void StubQueue::remove_all(){ } -enum { StubQueueLimit = 10 }; // there are only a few in the world -static StubQueue* registered_stub_queues[StubQueueLimit]; - -void StubQueue::register_queue(StubQueue* sq) { - for (int i = 0; i < StubQueueLimit; i++) { - if (registered_stub_queues[i] == NULL) { - registered_stub_queues[i] = sq; - return; - } - } - ShouldNotReachHere(); -} - - -void StubQueue::queues_do(void f(StubQueue* sq)) { - for (int i = 0; i < StubQueueLimit; i++) { - if (registered_stub_queues[i] != NULL) { - f(registered_stub_queues[i]); - } - } -} - - -void StubQueue::stubs_do(void f(Stub* s)) { - debug_only(verify();) - MutexLockerEx lock(_mutex); - for (Stub* s = first(); s != NULL; s = next(s)) f(s); -} - - void StubQueue::verify() { // verify only if initialized if (_stub_buffer == NULL) return; diff --git a/src/hotspot/share/code/stubs.hpp b/src/hotspot/share/code/stubs.hpp index ab84ffaada9..d50e30d1bd3 100644 --- a/src/hotspot/share/code/stubs.hpp +++ b/src/hotspot/share/code/stubs.hpp @@ -172,8 +172,6 @@ class StubQueue: public CHeapObj<mtCode> { void stub_verify(Stub* s) { _stub_interface->verify(s); } void stub_print(Stub* s) { _stub_interface->print(s); } - static void register_queue(StubQueue*); - public: StubQueue(StubInterface* stub_interface, int buffer_size, Mutex* lock, const char* name); @@ -204,8 +202,6 @@ class StubQueue: public CHeapObj<mtCode> { void deallocate_unused_tail(); // deallocate the unused tail of the underlying CodeBlob // only used from TemplateInterpreter::initialize() // Iteration - static void queues_do(void f(StubQueue* s)); // call f with each StubQueue - void stubs_do(void f(Stub* s)); // call f with all stubs Stub* first() const { return number_of_stubs() > 0 ? stub_at(_queue_begin) : NULL; } Stub* next(Stub* s) const { int i = index_of(s) + stub_size(s); // Only wrap around in the non-contiguous case (see stubss.cpp) @@ -213,9 +209,6 @@ class StubQueue: public CHeapObj<mtCode> { return (i == _queue_end) ? NULL : stub_at(i); } - address stub_code_begin(Stub* s) const { return _stub_interface->code_begin(s); } - address stub_code_end(Stub* s) const { return _stub_interface->code_end(s); } - // Debugging/printing void verify(); // verifies the stub queue void print(); // prints information about the stub queue From a7c84b45b41aa1a39335f0b564c951e1c44ea3b3 Mon Sep 17 00:00:00 2001 From: Dean Long <dlong@openjdk.org> Date: Wed, 22 Nov 2017 09:27:06 -0800 Subject: [PATCH 005/165] 8191688: Assert failed in > 200 tests: failed dependencies, but counter didn't change Reviewed-by: kvn --- src/hotspot/share/c1/c1_GraphBuilder.cpp | 1 + src/hotspot/share/ci/ciMethod.cpp | 4 +++- src/hotspot/share/ci/ciMethod.hpp | 2 ++ src/hotspot/share/opto/bytecodeInfo.cpp | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp index b00476c953d..5cd4fea84a9 100644 --- a/src/hotspot/share/c1/c1_GraphBuilder.cpp +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp @@ -3441,6 +3441,7 @@ const char* GraphBuilder::check_can_parse(ciMethod* callee) const { if ( callee->is_native()) return "native method"; if ( callee->is_abstract()) return "abstract method"; if (!callee->can_be_compiled()) return "not compilable (disabled)"; + if (!callee->can_be_parsed()) return "cannot be parsed"; return NULL; } diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index d878fa93bab..bdaafe80308 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -87,6 +87,7 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) : _balanced_monitors = !_uses_monitors || h_m()->access_flags().is_monitor_matching(); _is_c1_compilable = !h_m()->is_not_c1_compilable(); _is_c2_compilable = !h_m()->is_not_c2_compilable(); + _can_be_parsed = true; _has_reserved_stack_access = h_m()->has_reserved_stack_access(); // Lazy fields, filled in on demand. Require allocation. _code = NULL; @@ -99,12 +100,13 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) : #endif // COMPILER2 ciEnv *env = CURRENT_ENV; - if (env->jvmti_can_hotswap_or_post_breakpoint() && can_be_compiled()) { + if (env->jvmti_can_hotswap_or_post_breakpoint()) { // 6328518 check hotswap conditions under the right lock. MutexLocker locker(Compile_lock); if (Dependencies::check_evol_method(h_m()) != NULL) { _is_c1_compilable = false; _is_c2_compilable = false; + _can_be_parsed = false; } } else { CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index bc77829a163..a9b0352aa25 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -87,6 +87,7 @@ class ciMethod : public ciMetadata { bool _balanced_monitors; bool _is_c1_compilable; bool _is_c2_compilable; + bool _can_be_parsed; bool _can_be_statically_bound; bool _has_reserved_stack_access; @@ -291,6 +292,7 @@ class ciMethod : public ciMetadata { bool has_option(const char *option); bool has_option_value(const char* option, double& value); bool can_be_compiled(); + bool can_be_parsed() const { return _can_be_parsed; } bool can_be_osr_compiled(int entry_bci); void set_not_compilable(const char* reason = NULL); bool has_compiled_code(); diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp index c3d70097e13..2cc9f867b37 100644 --- a/src/hotspot/share/opto/bytecodeInfo.cpp +++ b/src/hotspot/share/opto/bytecodeInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -480,6 +480,7 @@ const char* InlineTree::check_can_parse(ciMethod* callee) { if ( callee->is_abstract()) return "abstract method"; if (!callee->has_balanced_monitors()) return "not compilable (unbalanced monitors)"; if ( callee->get_flow_analysis()->failing()) return "not compilable (flow analysis failed)"; + if (!callee->can_be_parsed()) return "cannot be parsed"; return NULL; } From cd0c6d0faeef2c135cb4e1d2e6ebfa9216d7aef8 Mon Sep 17 00:00:00 2001 From: Christian Tornqvist <ctornqvi@openjdk.org> Date: Wed, 22 Nov 2017 14:31:48 -0500 Subject: [PATCH 006/165] 8191768: Introduce a concurrency factor to be able to scale up or down jtreg concurrency when running Hotspot tests Reviewed-by: erikj --- test/hotspot/jtreg/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/Makefile b/test/hotspot/jtreg/Makefile index f24c3b081fc..3e4ac3117d4 100644 --- a/test/hotspot/jtreg/Makefile +++ b/test/hotspot/jtreg/Makefile @@ -62,8 +62,12 @@ ifeq ($(findstring CYGWIN,$(UNAME_S)), CYGWIN) endif endif +ifndef CONCURRENCY_FACTOR + CONCURRENCY_FACTOR = 1 +endif + # Concurrency based on min(cores / 2, 12) -CONCURRENCY := $(shell expr $(NUM_CORES) / 2) +CONCURRENCY := $(shell awk 'BEGIN { printf "%.0f", $(NUM_CORES) / 2 * $(CONCURRENCY_FACTOR) }') ifeq ($(CONCURRENCY), 0) CONCURRENCY := 1 else ifeq ($(shell expr $(CONCURRENCY) \> 12), 1) From 22d7ce52bb2033adcb0b1b90a03510755af3da45 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl <tschatzl@openjdk.org> Date: Thu, 23 Nov 2017 15:51:06 +0100 Subject: [PATCH 007/165] 8179244: Assert failed in instanceMirrorKlass.inline.hpp Investigate the failure and upon observing that the assert is too strict, disable it and addd a comment. Reviewed-by: ehelin --- .../share/oops/instanceMirrorKlass.inline.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp index cb416918d9e..a00d5b14951 100644 --- a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp @@ -71,10 +71,15 @@ void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) { Devirtualizer<nv>::do_klass(closure, klass); } } else { - // If klass is NULL then this a mirror for a primitive type. - // We don't have to follow them, since they are handled as strong - // roots in Universe::oops_do. - assert(java_lang_Class::is_primitive(obj), "Sanity check"); + // We would like to assert here (as below) that if klass has been NULL, then + // this has been a mirror for a primitive type that we do not need to follow + // as they are always strong roots. + // However, we might get across a klass that just changed during CMS concurrent + // marking if allocation occurred in the old generation. + // This is benign here, as we keep alive all CLDs that were loaded during the + // CMS concurrent phase in the class loading, i.e. they will be iterated over + // and kept alive during remark. + // assert(java_lang_Class::is_primitive(obj), "Sanity check"); } } From c5ce7408b3d78fa3bd2d6d2571629d46e4b9a687 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl <tschatzl@openjdk.org> Date: Thu, 23 Nov 2017 15:51:06 +0100 Subject: [PATCH 008/165] 8182050: assert(_whole_heap.contains(p)) failed: Attempt to access p out of bounds of card marking array's _whole_heap Ignore zero-sized MemRegions to invalidate in G1SATBCardTableLoggingModRefBS::invalidate() Reviewed-by: ehelin, sjohanss --- .../share/gc/g1/g1SATBCardTableModRefBS.cpp | 3 ++ .../jtreg/gc/g1/TestInvalidateArrayCopy.java | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java diff --git a/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp b/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp index 9db03fdfc5f..5c1bc491e5d 100644 --- a/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp +++ b/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp @@ -175,6 +175,9 @@ void G1SATBCardTableLoggingModRefBS::write_ref_field_post_slow(volatile jbyte* b void G1SATBCardTableLoggingModRefBS::invalidate(MemRegion mr) { + if (mr.is_empty()) { + return; + } volatile jbyte* byte = byte_for(mr.start()); jbyte* last_byte = byte_for(mr.last()); Thread* thr = Thread::current(); diff --git a/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java b/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java new file mode 100644 index 00000000000..38e1e0bddd6 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 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 TestInvalidateArrayCopy + * @bug 8182050 + * @summary Check that benign (0-sized) out of heap bounds card table invalidations do not assert. + * @requires vm.gc.G1 + * @requires vm.debug + * @key gc + * @run main/othervm -XX:NewSize=1M -Xlog:gc -XX:MaxNewSize=1m -XX:-UseTLAB -XX:OldSize=63M -XX:MaxHeapSize=64M TestInvalidateArrayCopy + */ + +// The test allocates zero-sized arrays of j.l.O and tries to arraycopy random data into it so +// that the asserting post barrier calls are executed. It assumes that G1 allocates eden regions +// at the top of the heap for this problem to occur. +public class TestInvalidateArrayCopy { + + static final int NumIterations = 1000000; + + // "Random" source data to "copy" into the target. + static Object[] sourceArray = new Object[10]; + + public static void main(String[] args) { + for (int i = 0; i < NumIterations; i++) { + Object[] x = new Object[0]; + // Make sure that the compiler can't optimize out the above allocations. + if (i % (NumIterations / 10) == 0) { + System.out.println(x); + } + System.arraycopy(sourceArray, 0, x, 0, Math.min(x.length, sourceArray.length)); + } + } +} From faff99f2fc94611a88e91df2094fc3f3d3498f11 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl <tschatzl@openjdk.org> Date: Thu, 23 Nov 2017 15:51:06 +0100 Subject: [PATCH 009/165] 8190426: Lazily initialize refinement threads with UseDynamicNumberOfGCThreads Reviewed-by: sangheki, sjohanss --- .../share/gc/g1/g1ConcurrentRefine.cpp | 216 +++++++++++++----- .../share/gc/g1/g1ConcurrentRefine.hpp | 91 ++++++-- .../share/gc/g1/g1ConcurrentRefineThread.cpp | 54 +---- .../share/gc/g1/g1ConcurrentRefineThread.hpp | 24 +- src/hotspot/share/gc/g1/g1RemSet.cpp | 2 +- src/hotspot/share/gc/g1/g1RemSetSummary.cpp | 4 +- 6 files changed, 238 insertions(+), 153 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index 8e39ff06698..a28e0e790b4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -33,6 +33,107 @@ #include "utilities/pair.hpp" #include <math.h> +G1ConcurrentRefineThread* G1ConcurrentRefineThreadControl::create_refinement_thread(uint worker_id, bool initializing) { + G1ConcurrentRefineThread* result = NULL; + if (initializing || !InjectGCWorkerCreationFailure) { + result = new G1ConcurrentRefineThread(_cr, worker_id); + } + if (result == NULL || result->osthread() == NULL) { + log_warning(gc)("Failed to create refinement thread %u, no more %s", + worker_id, + result == NULL ? "memory" : "OS threads"); + } + return result; +} + +G1ConcurrentRefineThreadControl::G1ConcurrentRefineThreadControl() : + _cr(NULL), + _threads(NULL), + _num_max_threads(0) +{ +} + +G1ConcurrentRefineThreadControl::~G1ConcurrentRefineThreadControl() { + for (uint i = 0; i < _num_max_threads; i++) { + G1ConcurrentRefineThread* t = _threads[i]; + if (t != NULL) { + delete t; + } + } + FREE_C_HEAP_ARRAY(G1ConcurrentRefineThread*, _threads); +} + +jint G1ConcurrentRefineThreadControl::initialize(G1ConcurrentRefine* cr, uint num_max_threads) { + assert(cr != NULL, "G1ConcurrentRefine must not be NULL"); + _cr = cr; + _num_max_threads = num_max_threads; + + _threads = NEW_C_HEAP_ARRAY_RETURN_NULL(G1ConcurrentRefineThread*, num_max_threads, mtGC); + if (_threads == NULL) { + vm_shutdown_during_initialization("Could not allocate thread holder array."); + return JNI_ENOMEM; + } + + for (uint i = 0; i < num_max_threads; i++) { + if (UseDynamicNumberOfGCThreads && i != 0 /* Always start first thread. */) { + _threads[i] = NULL; + } else { + _threads[i] = create_refinement_thread(i, true); + if (_threads[i] == NULL) { + vm_shutdown_during_initialization("Could not allocate refinement threads."); + return JNI_ENOMEM; + } + } + } + return JNI_OK; +} + +void G1ConcurrentRefineThreadControl::maybe_activate_next(uint cur_worker_id) { + assert(cur_worker_id < _num_max_threads, + "Activating another thread from %u not allowed since there can be at most %u", + cur_worker_id, _num_max_threads); + if (cur_worker_id == (_num_max_threads - 1)) { + // Already the last thread, there is no more thread to activate. + return; + } + + uint worker_id = cur_worker_id + 1; + G1ConcurrentRefineThread* thread_to_activate = _threads[worker_id]; + if (thread_to_activate == NULL) { + // Still need to create the thread... + _threads[worker_id] = create_refinement_thread(worker_id, false); + thread_to_activate = _threads[worker_id]; + } + if (thread_to_activate != NULL && !thread_to_activate->is_active()) { + thread_to_activate->activate(); + } +} + +void G1ConcurrentRefineThreadControl::print_on(outputStream* st) const { + for (uint i = 0; i < _num_max_threads; ++i) { + if (_threads[i] != NULL) { + _threads[i]->print_on(st); + st->cr(); + } + } +} + +void G1ConcurrentRefineThreadControl::worker_threads_do(ThreadClosure* tc) { + for (uint i = 0; i < _num_max_threads; i++) { + if (_threads[i] != NULL) { + tc->do_thread(_threads[i]); + } + } +} + +void G1ConcurrentRefineThreadControl::stop() { + for (uint i = 0; i < _num_max_threads; i++) { + if (_threads[i] != NULL) { + _threads[i]->stop(); + } + } +} + // Arbitrary but large limits, to simplify some of the zone calculations. // The general idea is to allow expressions like // MIN2(x OP y, max_XXX_zone) @@ -96,7 +197,7 @@ static Thresholds calc_thresholds(size_t green_zone, size_t yellow_zone, uint worker_i) { double yellow_size = yellow_zone - green_zone; - double step = yellow_size / G1ConcurrentRefine::thread_num(); + double step = yellow_size / G1ConcurrentRefine::max_num_threads(); if (worker_i == 0) { // Potentially activate worker 0 more aggressively, to keep // available buffers near green_zone value. When yellow_size is @@ -115,8 +216,7 @@ G1ConcurrentRefine::G1ConcurrentRefine(size_t green_zone, size_t yellow_zone, size_t red_zone, size_t min_yellow_zone_size) : - _threads(NULL), - _n_worker_threads(thread_num()), + _thread_control(), _green_zone(green_zone), _yellow_zone(yellow_zone), _red_zone(red_zone), @@ -125,9 +225,13 @@ G1ConcurrentRefine::G1ConcurrentRefine(size_t green_zone, assert_zone_constraints_gyr(green_zone, yellow_zone, red_zone); } +jint G1ConcurrentRefine::initialize() { + return _thread_control.initialize(this, max_num_threads()); +} + static size_t calc_min_yellow_zone_size() { size_t step = G1ConcRefinementThresholdStep; - uint n_workers = G1ConcurrentRefine::thread_num(); + uint n_workers = G1ConcurrentRefine::max_num_threads(); if ((max_yellow_zone / step) < n_workers) { return max_yellow_zone; } else { @@ -191,77 +295,27 @@ G1ConcurrentRefine* G1ConcurrentRefine::create(jint* ecode) { return NULL; } - cr->_threads = NEW_C_HEAP_ARRAY_RETURN_NULL(G1ConcurrentRefineThread*, cr->_n_worker_threads, mtGC); - if (cr->_threads == NULL) { - *ecode = JNI_ENOMEM; - vm_shutdown_during_initialization("Could not allocate an array for G1ConcurrentRefineThread"); - return NULL; - } - - uint worker_id_offset = DirtyCardQueueSet::num_par_ids(); - - G1ConcurrentRefineThread *next = NULL; - for (uint i = cr->_n_worker_threads - 1; i != UINT_MAX; i--) { - Thresholds thresholds = calc_thresholds(green_zone, yellow_zone, i); - G1ConcurrentRefineThread* t = - new G1ConcurrentRefineThread(cr, - next, - worker_id_offset, - i, - activation_level(thresholds), - deactivation_level(thresholds)); - assert(t != NULL, "Conc refine should have been created"); - if (t->osthread() == NULL) { - *ecode = JNI_ENOMEM; - vm_shutdown_during_initialization("Could not create G1ConcurrentRefineThread"); - return NULL; - } - - assert(t->cr() == cr, "Conc refine thread should refer to this"); - cr->_threads[i] = t; - next = t; - } - - *ecode = JNI_OK; + *ecode = cr->initialize(); return cr; } void G1ConcurrentRefine::stop() { - for (uint i = 0; i < _n_worker_threads; i++) { - _threads[i]->stop(); - } -} - -void G1ConcurrentRefine::update_thread_thresholds() { - for (uint i = 0; i < _n_worker_threads; i++) { - Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, i); - _threads[i]->update_thresholds(activation_level(thresholds), - deactivation_level(thresholds)); - } + _thread_control.stop(); } G1ConcurrentRefine::~G1ConcurrentRefine() { - for (uint i = 0; i < _n_worker_threads; i++) { - delete _threads[i]; - } - FREE_C_HEAP_ARRAY(G1ConcurrentRefineThread*, _threads); } void G1ConcurrentRefine::threads_do(ThreadClosure *tc) { - for (uint i = 0; i < _n_worker_threads; i++) { - tc->do_thread(_threads[i]); - } + _thread_control.worker_threads_do(tc); } -uint G1ConcurrentRefine::thread_num() { +uint G1ConcurrentRefine::max_num_threads() { return G1ConcRefinementThreads; } void G1ConcurrentRefine::print_threads_on(outputStream* st) const { - for (uint i = 0; i < _n_worker_threads; ++i) { - _threads[i]->print_on(st); - st->cr(); - } + _thread_control.print_on(st); } static size_t calc_new_green_zone(size_t green, @@ -326,16 +380,15 @@ void G1ConcurrentRefine::adjust(double update_rs_time, if (G1UseAdaptiveConcRefinement) { update_zones(update_rs_time, update_rs_processed_buffers, goal_ms); - update_thread_thresholds(); // Change the barrier params - if (_n_worker_threads == 0) { + if (max_num_threads() == 0) { // Disable dcqs notification when there are no threads to notify. dcqs.set_process_completed_threshold(INT_MAX); } else { // Worker 0 is the primary; wakeup is via dcqs notification. STATIC_ASSERT(max_yellow_zone <= INT_MAX); - size_t activate = _threads[0]->activation_threshold(); + size_t activate = activation_threshold(0); dcqs.set_process_completed_threshold((int)activate); } dcqs.set_max_completed_queue((int)red_zone()); @@ -349,3 +402,42 @@ void G1ConcurrentRefine::adjust(double update_rs_time, } dcqs.notify_if_necessary(); } + +size_t G1ConcurrentRefine::activation_threshold(uint worker_id) const { + Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, worker_id); + return activation_level(thresholds); +} + +size_t G1ConcurrentRefine::deactivation_threshold(uint worker_id) const { + Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, worker_id); + return deactivation_level(thresholds); +} + +uint G1ConcurrentRefine::worker_id_offset() { + return DirtyCardQueueSet::num_par_ids(); +} + +void G1ConcurrentRefine::maybe_activate_more_threads(uint worker_id, size_t num_cur_buffers) { + if (num_cur_buffers > activation_threshold(worker_id + 1)) { + _thread_control.maybe_activate_next(worker_id); + } +} + +bool G1ConcurrentRefine::do_refinement_step(uint worker_id) { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + + size_t curr_buffer_num = dcqs.completed_buffers_num(); + // If the number of the buffers falls down into the yellow zone, + // that means that the transition period after the evacuation pause has ended. + // Since the value written to the DCQS is the same for all threads, there is no + // need to synchronize. + if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= yellow_zone()) { + dcqs.set_completed_queue_padding(0); + } + + maybe_activate_more_threads(worker_id, curr_buffer_num); + + // Process the next buffer, if there are enough left. + return dcqs.refine_completed_buffer_concurrently(worker_id + worker_id_offset(), + deactivation_threshold(worker_id)); +} diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp index b64d4e3ee80..52783881b9e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp @@ -30,30 +30,63 @@ // Forward decl class CardTableEntryClosure; +class G1ConcurrentRefine; class G1ConcurrentRefineThread; class outputStream; class ThreadClosure; -class G1ConcurrentRefine : public CHeapObj<mtGC> { +// Helper class for refinement thread management. Used to start, stop and +// iterate over them. +class G1ConcurrentRefineThreadControl VALUE_OBJ_CLASS_SPEC { + G1ConcurrentRefine* _cr; + G1ConcurrentRefineThread** _threads; - uint _n_worker_threads; - /* - * The value of the update buffer queue length falls into one of 3 zones: - * green, yellow, red. If the value is in [0, green) nothing is - * done, the buffers are left unprocessed to enable the caching effect of the - * dirtied cards. In the yellow zone [green, yellow) the concurrent refinement - * threads are gradually activated. In [yellow, red) all threads are - * running. If the length becomes red (max queue length) the mutators start - * processing the buffers. - * - * There are some interesting cases (when G1UseAdaptiveConcRefinement - * is turned off): - * 1) green = yellow = red = 0. In this case the mutator will process all - * buffers. Except for those that are created by the deferred updates - * machinery during a collection. - * 2) green = 0. Means no caching. Can be a good way to minimize the - * amount of time spent updating rsets during a collection. - */ + uint _num_max_threads; + + // Create the refinement thread for the given worker id. + // If initializing is true, ignore InjectGCWorkerCreationFailure. + G1ConcurrentRefineThread* create_refinement_thread(uint worker_id, bool initializing); +public: + G1ConcurrentRefineThreadControl(); + ~G1ConcurrentRefineThreadControl(); + + jint initialize(G1ConcurrentRefine* cr, uint num_max_threads); + + // If there is a "successor" thread that can be activated given the current id, + // activate it. + void maybe_activate_next(uint cur_worker_id); + + void print_on(outputStream* st) const; + void worker_threads_do(ThreadClosure* tc); + void stop(); +}; + +// Controls refinement threads and their activation based on the number of completed +// buffers currently available in the global dirty card queue. +// Refinement threads pick work from the queue based on these thresholds. They are activated +// gradually based on the amount of work to do. +// Refinement thread n activates thread n+1 if the instance of this class determines there +// is enough work available. Threads deactivate themselves if the current amount of +// completed buffers falls below their individual threshold. +class G1ConcurrentRefine : public CHeapObj<mtGC> { + G1ConcurrentRefineThreadControl _thread_control; + /* + * The value of the completed dirty card queue length falls into one of 3 zones: + * green, yellow, red. If the value is in [0, green) nothing is + * done, the buffers are left unprocessed to enable the caching effect of the + * dirtied cards. In the yellow zone [green, yellow) the concurrent refinement + * threads are gradually activated. In [yellow, red) all threads are + * running. If the length becomes red (max queue length) the mutators start + * processing the buffers. + * + * There are some interesting cases (when G1UseAdaptiveConcRefinement + * is turned off): + * 1) green = yellow = red = 0. In this case the mutator will process all + * buffers. Except for those that are created by the deferred updates + * machinery during a collection. + * 2) green = 0. Means no caching. Can be a good way to minimize the + * amount of time spent updating remembered sets during a collection. + */ size_t _green_zone; size_t _yellow_zone; size_t _red_zone; @@ -69,24 +102,32 @@ class G1ConcurrentRefine : public CHeapObj<mtGC> { size_t update_rs_processed_buffers, double goal_ms); - // Update thread thresholds to account for updated zone values. - void update_thread_thresholds(); + static uint worker_id_offset(); + void maybe_activate_more_threads(uint worker_id, size_t num_cur_buffers); - public: + jint initialize(); +public: ~G1ConcurrentRefine(); - // Returns a G1ConcurrentRefine instance if succeeded to create/initialize G1ConcurrentRefine and G1ConcurrentRefineThreads. - // Otherwise, returns NULL with error code. + // Returns a G1ConcurrentRefine instance if succeeded to create/initialize the + // G1ConcurrentRefine instance. Otherwise, returns NULL with error code. static G1ConcurrentRefine* create(jint* ecode); void stop(); + // Adjust refinement thresholds based on work done during the pause and the goal time. void adjust(double update_rs_time, size_t update_rs_processed_buffers, double goal_ms); + size_t activation_threshold(uint worker_id) const; + size_t deactivation_threshold(uint worker_id) const; + // Perform a single refinement step. Called by the refinement threads when woken up. + bool do_refinement_step(uint worker_id); + // Iterate over all concurrent refinement threads applying the given closure. void threads_do(ThreadClosure *tc); - static uint thread_num(); + // Maximum number of refinement threads. + static uint max_num_threads(); void print_threads_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp index f62e0c369e8..e1d1f8f4d01 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp @@ -25,32 +25,20 @@ #include "precompiled.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1RemSet.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" -G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, - G1ConcurrentRefineThread *next, - uint worker_id_offset, - uint worker_id, - size_t activate, - size_t deactivate) : +G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) : ConcurrentGCThread(), - _worker_id_offset(worker_id_offset), _worker_id(worker_id), _active(false), - _next(next), _monitor(NULL), _cr(cr), - _vtime_accum(0.0), - _activation_threshold(activate), - _deactivation_threshold(deactivate) + _vtime_accum(0.0) { - // Each thread has its own monitor. The i-th thread is responsible for signaling // to thread i+1 if the number of buffers in the queue exceeds a threshold for this // thread. Monitors are also used to wake up the threads during termination. @@ -67,13 +55,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, create_and_start(); } -void G1ConcurrentRefineThread::update_thresholds(size_t activate, - size_t deactivate) { - assert(deactivate < activate, "precondition"); - _activation_threshold = activate; - _deactivation_threshold = deactivate; -} - void G1ConcurrentRefineThread::wait_for_completed_buffers() { MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); while (!should_terminate() && !is_active()) { @@ -118,9 +99,9 @@ void G1ConcurrentRefineThread::run_service() { } size_t buffers_processed = 0; - DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); - log_debug(gc, refine)("Activated %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT, - _worker_id, _activation_threshold, dcqs.completed_buffers_num()); + log_debug(gc, refine)("Activated worker %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT, + _worker_id, _cr->activation_threshold(_worker_id), + JavaThread::dirty_card_queue_set().completed_buffers_num()); { SuspendibleThreadSetJoiner sts_join; @@ -131,33 +112,18 @@ void G1ConcurrentRefineThread::run_service() { continue; // Re-check for termination after yield delay. } - size_t curr_buffer_num = dcqs.completed_buffers_num(); - // If the number of the buffers falls down into the yellow zone, - // that means that the transition period after the evacuation pause has ended. - if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cr()->yellow_zone()) { - dcqs.set_completed_queue_padding(0); - } - - // Check if we need to activate the next thread. - if ((_next != NULL) && - !_next->is_active() && - (curr_buffer_num > _next->_activation_threshold)) { - _next->activate(); - } - - // Process the next buffer, if there are enough left. - if (!dcqs.refine_completed_buffer_concurrently(_worker_id + _worker_id_offset, _deactivation_threshold)) { - break; // Deactivate, number of buffers fell below threshold. + if (!_cr->do_refinement_step(_worker_id)) { + break; } ++buffers_processed; } } deactivate(); - log_debug(gc, refine)("Deactivated %d, off threshold: " SIZE_FORMAT + log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT ", processed: " SIZE_FORMAT, - _worker_id, _deactivation_threshold, - dcqs.completed_buffers_num(), + _worker_id, _cr->deactivation_threshold(_worker_id), + JavaThread::dirty_card_queue_set().completed_buffers_num(), buffers_processed); if (os::supports_vtime()) { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp index fbc10fcfb4c..8b3694411fa 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp @@ -43,43 +43,29 @@ class G1ConcurrentRefineThread: public ConcurrentGCThread { uint _worker_id; uint _worker_id_offset; - // The refinement threads collection is linked list. A predecessor can activate a successor - // when the number of the rset update buffer crosses a certain threshold. A successor - // would self-deactivate when the number of the buffers falls below the threshold. bool _active; - G1ConcurrentRefineThread* _next; Monitor* _monitor; G1ConcurrentRefine* _cr; - // This thread's activation/deactivation thresholds - size_t _activation_threshold; - size_t _deactivation_threshold; - void wait_for_completed_buffers(); void set_active(bool x) { _active = x; } - bool is_active(); - void activate(); + // Deactivate this thread. void deactivate(); bool is_primary() { return (_worker_id == 0); } void run_service(); void stop_service(); - public: - // Constructor - G1ConcurrentRefineThread(G1ConcurrentRefine* cr, G1ConcurrentRefineThread* next, - uint worker_id_offset, uint worker_id, - size_t activate, size_t deactivate); + G1ConcurrentRefineThread(G1ConcurrentRefine* cg1r, uint worker_id); - void update_thresholds(size_t activate, size_t deactivate); - size_t activation_threshold() const { return _activation_threshold; } + bool is_active(); + // Activate this thread. + void activate(); // Total virtual time so far. double vtime_accum() { return _vtime_accum; } - - G1ConcurrentRefine* cr() { return _cr; } }; #endif // SHARE_VM_GC_G1_G1CONCURRENTREFINETHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index da0281c6130..c20a41e66f0 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -298,7 +298,7 @@ G1RemSet::~G1RemSet() { } uint G1RemSet::num_par_rem_sets() { - return MAX2(DirtyCardQueueSet::num_par_ids() + G1ConcurrentRefine::thread_num(), ParallelGCThreads); + return MAX2(DirtyCardQueueSet::num_par_ids() + G1ConcurrentRefine::max_num_threads(), ParallelGCThreads); } void G1RemSet::initialize(size_t capacity, uint max_regions) { diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index 2ddabe75d03..93a8f713836 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -86,7 +86,7 @@ G1RemSetSummary::G1RemSetSummary() : _num_processed_buf_mutator(0), _num_processed_buf_rs_threads(0), _num_coarsenings(0), - _num_vtimes(G1ConcurrentRefine::thread_num()), + _num_vtimes(G1ConcurrentRefine::max_num_threads()), _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)), _sampling_thread_vtime(0.0f) { @@ -99,7 +99,7 @@ G1RemSetSummary::G1RemSetSummary(G1RemSet* rem_set) : _num_processed_buf_mutator(0), _num_processed_buf_rs_threads(0), _num_coarsenings(0), - _num_vtimes(G1ConcurrentRefine::thread_num()), + _num_vtimes(G1ConcurrentRefine::max_num_threads()), _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)), _sampling_thread_vtime(0.0f) { update(); From d15086130bef9e22f09f592e842a23f3894b455f Mon Sep 17 00:00:00 2001 From: Alexander Harlap <aharlap@openjdk.org> Date: Wed, 22 Nov 2017 18:58:01 -0500 Subject: [PATCH 010/165] 8185591: guarantee(_byte_map[_guard_index] == last_card) failed: card table guard has been modified Properly handle zero count in gen_write_ref_array_post_barrier() Reviewed-by: tschatzl, kbarrett --- src/hotspot/cpu/arm/stubGenerator_arm.cpp | 5 ++++- src/hotspot/cpu/sparc/stubGenerator_sparc.cpp | 5 ++++- src/hotspot/cpu/x86/stubGenerator_x86_64.cpp | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp index 97ef93c2e4f..ec1a6d8279d 100644 --- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp @@ -2968,7 +2968,9 @@ class StubGenerator: public StubCodeGenerator { CardTableModRefBS* ct = barrier_set_cast<CardTableModRefBS>(bs); assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); - Label L_cardtable_loop; + Label L_cardtable_loop, L_done; + + __ cbz_32(count, L_done); // zero count - nothing to do __ add_ptr_scaled_int32(count, addr, count, LogBytesPerHeapOop); __ sub(count, count, BytesPerHeapOop); // last addr @@ -2987,6 +2989,7 @@ class StubGenerator: public StubCodeGenerator { __ strb(zero, Address(addr, 1, post_indexed)); __ subs(count, count, 1); __ b(L_cardtable_loop, ge); + __ BIND(L_done); } break; case BarrierSet::ModRef: diff --git a/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp b/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp index 351555dbe51..d93b294574b 100644 --- a/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp +++ b/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp @@ -898,7 +898,9 @@ class StubGenerator: public StubCodeGenerator { assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); assert_different_registers(addr, count, tmp); - Label L_loop; + Label L_loop, L_done; + + __ cmp_and_br_short(count, 0, Assembler::equal, Assembler::pt, L_done); // zero count - nothing to do __ sll_ptr(count, LogBytesPerHeapOop, count); __ sub(count, BytesPerHeapOop, count); @@ -914,6 +916,7 @@ class StubGenerator: public StubCodeGenerator { __ subcc(count, 1, count); __ brx(Assembler::greaterEqual, false, Assembler::pt, L_loop); __ delayed()->add(addr, 1, addr); + __ BIND(L_done); } break; case BarrierSet::ModRef: diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 7db8ba32981..5b28a4e28d5 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -1264,9 +1264,12 @@ class StubGenerator: public StubCodeGenerator { CardTableModRefBS* ct = barrier_set_cast<CardTableModRefBS>(bs); assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); - Label L_loop; + Label L_loop, L_done; const Register end = count; + __ testl(count, count); + __ jcc(Assembler::zero, L_done); // zero count - nothing to do + __ leaq(end, Address(start, count, TIMES_OOP, 0)); // end == start+count*oop_size __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive __ shrptr(start, CardTableModRefBS::card_shift); @@ -1280,6 +1283,7 @@ class StubGenerator: public StubCodeGenerator { __ movb(Address(start, count, Address::times_1), 0); __ decrement(count); __ jcc(Assembler::greaterEqual, L_loop); + __ BIND(L_done); } break; default: From 81032ed30fce158d6b0eb72b0c1ac8eca2515eac Mon Sep 17 00:00:00 2001 From: Calvin Cheung <ccheung@openjdk.org> Date: Wed, 22 Nov 2017 16:08:39 -0800 Subject: [PATCH 011/165] 8191653: Test failures in BootAppendTests - missing jdk.internal.vm.compiler module Backout the fix for 8190975 Reviewed-by: iklam, kvn --- .../runtime/SharedArchiveFile/BootAppendTests.java | 6 +++--- .../lang/String/concat/WithSecurityManager.java | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java index 88b996c2c59..c6614fa4d31 100644 --- a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java +++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java @@ -169,7 +169,7 @@ public class BootAppendTests { CDSOptions opts = (new CDSOptions()) .setXShareMode(mode).setUseVersion(false) .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion", - "--limit-modules=java.base,jdk.internal.vm.compiler", "-cp", appJar) + "--limit-modules=java.base", "-cp", appJar) .addSuffix("-Xlog:class+load=info", APP_CLASS, BOOT_APPEND_MODULE_CLASS_NAME); @@ -198,7 +198,7 @@ public class BootAppendTests { CDSOptions opts = (new CDSOptions()) .setXShareMode(mode).setUseVersion(false) .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion", - "--limit-modules=java.base,jdk.internal.vm.compiler", "-cp", appJar) + "--limit-modules=java.base", "-cp", appJar) .addSuffix("-Xlog:class+load=info", APP_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME); @@ -226,7 +226,7 @@ public class BootAppendTests { CDSOptions opts = (new CDSOptions()) .setXShareMode(mode).setUseVersion(false) .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion", - "--limit-modules=java.base,jdk.internal.vm.compiler", "-cp", appJar) + "--limit-modules=java.base", "-cp", appJar) .addSuffix("-Xlog:class+load=info", APP_CLASS, BOOT_APPEND_CLASS_NAME); diff --git a/test/jdk/java/lang/String/concat/WithSecurityManager.java b/test/jdk/java/lang/String/concat/WithSecurityManager.java index bfde6852fa1..2835a3813f4 100644 --- a/test/jdk/java/lang/String/concat/WithSecurityManager.java +++ b/test/jdk/java/lang/String/concat/WithSecurityManager.java @@ -38,13 +38,13 @@ import java.security.Permission; * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT WithSecurityManager * @run main/othervm -Xverify:all -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT WithSecurityManager * - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=BC_SB WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=BC_SB_SIZED WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=MH_SB_SIZED WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT WithSecurityManager - * @run main/othervm -Xverify:all --limit-modules=java.base,jdk.internal.vm.compiler -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=BC_SB WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=BC_SB_SIZED WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=MH_SB_SIZED WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT WithSecurityManager + * @run main/othervm -Xverify:all --limit-modules=java.base -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT WithSecurityManager */ public class WithSecurityManager { public static void main(String[] args) throws Throwable { From 0dff96ff0bf4a1331b3b012933266284d6c2e917 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" <dcubed@openjdk.org> Date: Wed, 22 Nov 2017 17:54:50 -0800 Subject: [PATCH 012/165] 8167108: inconsistent handling of SR_lock can lead to crashes Add Thread Safe Memory Reclamation (Thread-SMR) mechanism. Co-authored-by: Erik Osterlund <erik.osterlund@oracle.com> Co-authored-by: Robbin Ehn <robbin.ehn@oracle.com> Reviewed-by: coleenp, dcubed, dholmes, eosterlund, gthornbr, kbarrett, rehn, sspitsyn, stefank --- src/hotspot/os/linux/os_linux.cpp | 11 +- src/hotspot/os/posix/os_posix.cpp | 7 +- src/hotspot/os/windows/os_windows.cpp | 7 +- src/hotspot/share/gc/g1/dirtyCardQueue.cpp | 5 +- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 5 +- src/hotspot/share/gc/g1/satbMarkQueue.cpp | 15 +- .../share/gc/parallel/mutableNUMASpace.cpp | 3 +- src/hotspot/share/gc/shared/collectedHeap.cpp | 6 +- src/hotspot/share/gc/shared/gcLocker.cpp | 9 +- .../gc/shared/threadLocalAllocBuffer.cpp | 5 +- src/hotspot/share/jvmci/jvmciRuntime.cpp | 10 +- src/hotspot/share/logging/logTag.hpp | 1 + src/hotspot/share/opto/idealGraphPrinter.cpp | 6 +- src/hotspot/share/prims/jni.cpp | 4 +- src/hotspot/share/prims/jvm.cpp | 212 ++- src/hotspot/share/prims/jvmtiEnter.xsl | 35 +- src/hotspot/share/prims/jvmtiEnv.cpp | 271 ++-- src/hotspot/share/prims/jvmtiEnvBase.cpp | 180 ++- src/hotspot/share/prims/jvmtiEnvBase.hpp | 71 +- .../share/prims/jvmtiEnvThreadState.cpp | 3 +- .../share/prims/jvmtiEventController.cpp | 14 +- src/hotspot/share/prims/jvmtiExport.cpp | 106 +- src/hotspot/share/prims/jvmtiExport.hpp | 8 + src/hotspot/share/prims/jvmtiImpl.cpp | 4 +- .../share/prims/jvmtiRedefineClasses.cpp | 1 + src/hotspot/share/prims/jvmtiTagMap.cpp | 4 +- src/hotspot/share/prims/jvmtiThreadState.hpp | 28 +- .../share/prims/jvmtiThreadState.inline.hpp | 29 +- src/hotspot/share/prims/unsafe.cpp | 29 +- src/hotspot/share/prims/whitebox.cpp | 3 +- src/hotspot/share/runtime/biasedLocking.cpp | 125 +- src/hotspot/share/runtime/deoptimization.cpp | 3 +- src/hotspot/share/runtime/globals.hpp | 6 + src/hotspot/share/runtime/handshake.cpp | 59 +- src/hotspot/share/runtime/java.cpp | 4 + src/hotspot/share/runtime/memprofiler.cpp | 30 +- src/hotspot/share/runtime/os.cpp | 15 +- src/hotspot/share/runtime/safepoint.cpp | 389 +++--- src/hotspot/share/runtime/synchronizer.cpp | 4 +- src/hotspot/share/runtime/synchronizer.hpp | 3 +- src/hotspot/share/runtime/thread.cpp | 1241 +++++++++++++++-- src/hotspot/share/runtime/thread.hpp | 162 ++- src/hotspot/share/runtime/thread.inline.hpp | 96 +- src/hotspot/share/runtime/threadSMR.cpp | 121 ++ src/hotspot/share/runtime/threadSMR.hpp | 257 ++++ .../share/runtime/threadSMR.inline.hpp | 62 + src/hotspot/share/runtime/vm_operations.cpp | 48 +- src/hotspot/share/runtime/vm_operations.hpp | 10 +- src/hotspot/share/services/heapDumper.cpp | 4 +- src/hotspot/share/services/management.cpp | 63 +- src/hotspot/share/services/threadService.cpp | 53 +- src/hotspot/share/services/threadService.hpp | 31 +- src/hotspot/share/utilities/vmError.cpp | 19 +- .../runtime/ErrorHandling/ErrorHandler.java | 12 +- ...dThreadsListHandleInErrorHandlingTest.java | 124 ++ .../ThreadsListHandleInErrorHandlingTest.java | 121 ++ .../Thread/CountStackFramesAtExit.java | 111 ++ .../jtreg/runtime/Thread/InterruptAtExit.java | 106 ++ .../runtime/Thread/IsInterruptedAtExit.java | 107 ++ .../jtreg/runtime/Thread/ResumeAtExit.java | 107 ++ .../jtreg/runtime/Thread/SetNameAtExit.java | 107 ++ .../runtime/Thread/SetPriorityAtExit.java | 116 ++ .../jtreg/runtime/Thread/StopAtExit.java | 119 ++ .../jtreg/runtime/Thread/SuspendAtExit.java | 109 ++ .../runtime/Thread/TestThreadDumpSMRInfo.java | 93 ++ .../handshake/HandshakeWalkExitTest.java | 8 +- .../handshake/HandshakeWalkOneExitTest.java | 78 ++ 67 files changed, 4220 insertions(+), 995 deletions(-) create mode 100644 src/hotspot/share/runtime/threadSMR.cpp create mode 100644 src/hotspot/share/runtime/threadSMR.hpp create mode 100644 src/hotspot/share/runtime/threadSMR.inline.hpp create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java create mode 100644 test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java create mode 100644 test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/StopAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java create mode 100644 test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java create mode 100644 test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 548e7823f7f..9b52aba4574 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -59,6 +59,7 @@ #include "runtime/stubRoutines.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadCritical.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/timer.hpp" #include "semaphore_posix.hpp" #include "services/attachListener.hpp" @@ -1646,7 +1647,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { // // Dynamic loader will make all stacks executable after // this function returns, and will not do that again. - assert(Threads::first() == NULL, "no Java threads should exist yet."); +#ifdef ASSERT + ThreadsListHandle tlh; + assert(tlh.length() == 0, "no Java threads should exist yet."); +#endif } else { warning("You have loaded library %s which might have disabled stack guard. " "The VM will try to fix the stack guard now.\n" @@ -1874,16 +1878,13 @@ void * os::Linux::dll_load_in_vmthread(const char *filename, char *ebuf, // may have been queued at the same time. if (!_stack_is_executable) { - JavaThread *jt = Threads::first(); - - while (jt) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { if (!jt->stack_guard_zone_unused() && // Stack not yet fully initialized jt->stack_guards_enabled()) { // No pending stack overflow exceptions if (!os::guard_memory((char *)jt->stack_end(), jt->stack_guard_zone_size())) { warning("Attempt to reguard stack yellow zone failed."); } } - jt = jt->next(); } } diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index fed874d30eb..5d393bf1e18 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -478,8 +478,7 @@ int os::sleep(Thread* thread, jlong millis, bool interruptible) { // interrupt support void os::interrupt(Thread* thread) { - assert(Thread::current() == thread || Threads_lock->owned_by_self(), - "possibility of dangling Thread pointer"); + debug_only(Thread::check_for_dangling_thread_pointer(thread);) OSThread* osthread = thread->osthread(); @@ -499,12 +498,10 @@ void os::interrupt(Thread* thread) { ParkEvent * ev = thread->_ParkEvent ; if (ev != NULL) ev->unpark() ; - } bool os::is_interrupted(Thread* thread, bool clear_interrupted) { - assert(Thread::current() == thread || Threads_lock->owned_by_self(), - "possibility of dangling Thread pointer"); + debug_only(Thread::check_for_dangling_thread_pointer(thread);) OSThread* osthread = thread->osthread(); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index c52cddd391c..2742c53614d 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3490,9 +3490,7 @@ OSReturn os::get_native_priority(const Thread* const thread, void os::hint_no_preempt() {} void os::interrupt(Thread* thread) { - assert(!thread->is_Java_thread() || Thread::current() == thread || - Threads_lock->owned_by_self(), - "possibility of dangling Thread pointer"); + debug_only(Thread::check_for_dangling_thread_pointer(thread);) OSThread* osthread = thread->osthread(); osthread->set_interrupted(true); @@ -3513,8 +3511,7 @@ void os::interrupt(Thread* thread) { bool os::is_interrupted(Thread* thread, bool clear_interrupted) { - assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(), - "possibility of dangling Thread pointer"); + debug_only(Thread::check_for_dangling_thread_pointer(thread);) OSThread* osthread = thread->osthread(); // There is no synchronization between the setting of the interrupt diff --git a/src/hotspot/share/gc/g1/dirtyCardQueue.cpp b/src/hotspot/share/gc/g1/dirtyCardQueue.cpp index 11c2da764e4..ae6a476ff59 100644 --- a/src/hotspot/share/gc/g1/dirtyCardQueue.cpp +++ b/src/hotspot/share/gc/g1/dirtyCardQueue.cpp @@ -32,6 +32,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" // Closure used for updating remembered sets and recording references that // point into the collection set while the mutator is running. @@ -319,7 +320,7 @@ void DirtyCardQueueSet::abandon_logs() { clear(); // Since abandon is done only at safepoints, we can safely manipulate // these queues. - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { t->dirty_card_queue().reset(); } shared_dirty_card_queue()->reset(); @@ -338,7 +339,7 @@ void DirtyCardQueueSet::concatenate_logs() { int save_max_completed_queue = _max_completed_queue; _max_completed_queue = max_jint; assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { concatenate_log(t->dirty_card_queue()); } concatenate_log(_shared_dirty_card_queue); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index a62a48eeac5..7b6d4055558 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -81,6 +81,7 @@ #include "runtime/atomic.hpp" #include "runtime/init.hpp" #include "runtime/orderAccess.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -2653,11 +2654,9 @@ G1CollectedHeap::doConcurrentMark() { size_t G1CollectedHeap::pending_card_num() { size_t extra_cards = 0; - JavaThread *curr = Threads::first(); - while (curr != NULL) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *curr = jtiwh.next(); ) { DirtyCardQueue& dcq = curr->dirty_card_queue(); extra_cards += dcq.size(); - curr = curr->next(); } DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); size_t buffer_size = dcqs.buffer_size(); diff --git a/src/hotspot/share/gc/g1/satbMarkQueue.cpp b/src/hotspot/share/gc/g1/satbMarkQueue.cpp index ddca44b038c..178cb450c9b 100644 --- a/src/hotspot/share/gc/g1/satbMarkQueue.cpp +++ b/src/hotspot/share/gc/g1/satbMarkQueue.cpp @@ -32,6 +32,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" SATBMarkQueue::SATBMarkQueue(SATBMarkQueueSet* qset, bool permanent) : @@ -214,7 +215,7 @@ void SATBMarkQueueSet::dump_active_states(bool expected_active) { log_error(gc, verify)("Expected SATB active state: %s", expected_active ? "ACTIVE" : "INACTIVE"); log_error(gc, verify)("Actual SATB active states:"); log_error(gc, verify)(" Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE"); - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { log_error(gc, verify)(" Thread \"%s\" queue: %s", t->name(), t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE"); } log_error(gc, verify)(" Shared queue: %s", shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE"); @@ -228,7 +229,7 @@ void SATBMarkQueueSet::verify_active_states(bool expected_active) { } // Verify thread queue states - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { if (t->satb_mark_queue().is_active() != expected_active) { dump_active_states(expected_active); guarantee(false, "Thread SATB queue has an unexpected active state"); @@ -249,14 +250,14 @@ void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active) verify_active_states(expected_active); #endif // ASSERT _all_active = active; - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { t->satb_mark_queue().set_active(active); } shared_satb_queue()->set_active(active); } void SATBMarkQueueSet::filter_thread_buffers() { - for(JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { t->satb_mark_queue().filter(); } shared_satb_queue()->filter(); @@ -309,7 +310,7 @@ void SATBMarkQueueSet::print_all(const char* msg) { i += 1; } - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { jio_snprintf(buffer, SATB_PRINTER_BUFFER_SIZE, "Thread: %s", t->name()); t->satb_mark_queue().print(buffer); } @@ -341,8 +342,8 @@ void SATBMarkQueueSet::abandon_partial_marking() { } assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); // So we can safely manipulate these queues. - for (JavaThread* t = Threads::first(); t; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { t->satb_mark_queue().reset(); } - shared_satb_queue()->reset(); + shared_satb_queue()->reset(); } diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 120ff95bae5..7ccd428936a 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -29,6 +29,7 @@ #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "utilities/align.hpp" MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment), _must_use_large_pages(false) { @@ -287,7 +288,7 @@ bool MutableNUMASpace::update_layout(bool force) { FREE_C_HEAP_ARRAY(int, lgrp_ids); if (changed) { - for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { thread->set_lgrp_id(-1); } } diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index b2a0dc53ceb..e4961555019 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -40,6 +40,7 @@ #include "oops/oop.inline.hpp" #include "runtime/init.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "services/heapDumper.hpp" #include "utilities/align.hpp" @@ -540,10 +541,11 @@ void CollectedHeap::ensure_parsability(bool retire_tlabs) { const bool deferred = _defer_initial_card_mark; // The main thread starts allocating via a TLAB even before it // has added itself to the threads list at vm boot-up. - assert(!use_tlab || Threads::first() != NULL, + JavaThreadIteratorWithHandle jtiwh; + assert(!use_tlab || jtiwh.length() > 0, "Attempt to fill tlabs before main thread has been added" " to threads list is doomed to failure!"); - for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + for (; JavaThread *thread = jtiwh.next(); ) { if (use_tlab) thread->tlab().make_parsable(retire_tlabs); #if COMPILER2_OR_JVMCI // The deferred store barriers must all have been flushed to the diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index 4b72cd80bfd..644d91116ba 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -29,6 +29,7 @@ #include "logging/log.hpp" #include "runtime/atomic.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" volatile jint GCLocker::_jni_lock_count = 0; volatile bool GCLocker::_needs_gc = false; @@ -45,14 +46,16 @@ void GCLocker::verify_critical_count() { assert(!needs_gc() || _debug_jni_lock_count == _jni_lock_count, "must agree"); int count = 0; // Count the number of threads with critical operations in progress - for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) { + JavaThreadIteratorWithHandle jtiwh; + for (; JavaThread *thr = jtiwh.next(); ) { if (thr->in_critical()) { count++; } } if (_jni_lock_count != count) { log_error(gc, verify)("critical counts don't match: %d != %d", _jni_lock_count, count); - for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) { + jtiwh.rewind(); + for (; JavaThread *thr = jtiwh.next(); ) { if (thr->in_critical()) { log_error(gc, verify)(INTPTR_FORMAT " in_critical %d", p2i(thr), thr->in_critical()); } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index e8828abbe40..6634a0cc906 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -30,6 +30,7 @@ #include "memory/universe.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "utilities/copy.hpp" // Thread-Local Edens support @@ -48,7 +49,7 @@ void ThreadLocalAllocBuffer::clear_before_allocation() { void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { global_stats()->initialize(); - for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { thread->tlab().accumulate_statistics(); thread->tlab().initialize_statistics(); } @@ -130,7 +131,7 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) { void ThreadLocalAllocBuffer::resize_all_tlabs() { if (ResizeTLAB) { - for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { thread->tlab().resize(); } } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 134ea685c83..ab5e3542fa9 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -42,6 +42,7 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/reflection.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/threadSMR.hpp" #include "utilities/debug.hpp" #include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" @@ -598,12 +599,13 @@ JRT_ENTRY(jint, JVMCIRuntime::identity_hash_code(JavaThread* thread, oopDesc* ob JRT_END JRT_ENTRY(jboolean, JVMCIRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted)) - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate. - // This locking requires thread_in_vm which is why this method cannot be JRT_LEAF. Handle receiverHandle(thread, receiver); - MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock); + // A nested ThreadsListHandle may require the Threads_lock which + // requires thread_in_vm which is why this method cannot be JRT_LEAF. + ThreadsListHandle tlh; + JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle()); - if (receiverThread == NULL) { + if (receiverThread == NULL || (EnableThreadSMRExtraValidityChecks && !tlh.includes(receiverThread))) { // The other thread may exit during this process, which is ok so return false. return JNI_FALSE; } else { diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index e53e7cb4774..1956f27a078 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -121,6 +121,7 @@ LOG_TAG(safepoint) \ LOG_TAG(scavenge) \ LOG_TAG(scrub) \ + LOG_TAG(smr) \ LOG_TAG(stacktrace) \ LOG_TAG(stackwalk) \ LOG_TAG(start) \ diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp index 0d5fbdda39c..cb78db147b2 100644 --- a/src/hotspot/share/opto/idealGraphPrinter.cpp +++ b/src/hotspot/share/opto/idealGraphPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2017, 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 @@ -29,6 +29,7 @@ #include "opto/machnode.hpp" #include "opto/parse.hpp" #include "runtime/threadCritical.hpp" +#include "runtime/threadSMR.hpp" #ifndef PRODUCT @@ -91,8 +92,7 @@ IdealGraphPrinter *IdealGraphPrinter::printer() { } void IdealGraphPrinter::clean_up() { - JavaThread *p; - for (p = Threads::first(); p; p = p->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *p = jtiwh.next(); ) { if (p->is_Compiler_thread()) { CompilerThread *c = (CompilerThread *)p; IdealGraphPrinter *printer = c->ideal_graph_printer(); diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 26aedaaebe5..06d0f91a6b1 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -4119,7 +4119,7 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae thread->initialize_thread_current(); if (!os::create_attached_thread(thread)) { - delete thread; + thread->smr_delete(); return JNI_ERR; } // Enable stack overflow checks @@ -4250,7 +4250,7 @@ jint JNICALL jni_DetachCurrentThread(JavaVM *vm) { // (platform-dependent) methods where we do alternate stack // maintenance work?) thread->exit(false, JavaThread::jni_detach); - delete thread; + thread->smr_delete(); HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_OK); return JNI_OK; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 12d9ec0881f..6307f437a61 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -66,6 +66,7 @@ #include "runtime/perfData.hpp" #include "runtime/reflection.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vm_operations.hpp" #include "runtime/vm_version.hpp" @@ -2737,16 +2738,12 @@ void jio_print(const char* s) { // java.lang.Thread ////////////////////////////////////////////////////////////////////////////// -// In most of the JVM Thread support functions we need to be sure to lock the Threads_lock -// to prevent the target thread from exiting after we have a pointer to the C++ Thread or -// OSThread objects. The exception to this rule is when the target object is the thread -// doing the operation, in which case we know that the thread won't exit until the -// operation is done (all exits being voluntary). There are a few cases where it is -// rather silly to do operations on yourself, like resuming yourself or asking whether -// you are alive. While these can still happen, they are not subject to deadlocks if -// the lock is held while the operation occurs (this is not the case for suspend, for -// instance), and are very unlikely. Because IsAlive needs to be fast and its -// implementation is local to this file, we always lock Threads_lock for that one. +// In most of the JVM thread support functions we need to access the +// thread through a ThreadsListHandle to prevent it from exiting and +// being reclaimed while we try to operate on it. The exceptions to this +// rule are when operating on the current thread, or if the monitor of +// the target java.lang.Thread is locked at the Java level - in both +// cases the target cannot exit. static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); @@ -2821,7 +2818,7 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) if (native_thread->osthread() == NULL) { // No one should hold a reference to the 'native_thread'. - delete native_thread; + native_thread->smr_delete(); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, @@ -2835,41 +2832,45 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JVM_END + // JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints // before the quasi-asynchronous exception is delivered. This is a little obtrusive, // but is thought to be reliable and simple. In the case, where the receiver is the -// same thread as the sender, no safepoint is needed. +// same thread as the sender, no VM_Operation is needed. JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable)) JVMWrapper("JVM_StopThread"); + // A nested ThreadsListHandle will grab the Threads_lock so create + // tlh before we resolve throwable. + ThreadsListHandle tlh(thread); oop java_throwable = JNIHandles::resolve(throwable); if (java_throwable == NULL) { THROW(vmSymbols::java_lang_NullPointerException()); } - oop java_thread = JNIHandles::resolve_non_null(jthread); - JavaThread* receiver = java_lang_Thread::thread(java_thread); - Events::log_exception(JavaThread::current(), + oop java_thread = NULL; + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread); + Events::log_exception(thread, "JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]", p2i(receiver), p2i((address)java_thread), p2i(throwable)); - // First check if thread is alive - if (receiver != NULL) { - // Check if exception is getting thrown at self (use oop equality, since the - // target object might exit) - if (java_thread == thread->threadObj()) { + + if (is_alive) { + // jthread refers to a live JavaThread. + if (thread == receiver) { + // Exception is getting thrown at self so no VM_Operation needed. THROW_OOP(java_throwable); } else { - // Enques a VM_Operation to stop all threads and then deliver the exception... - Thread::send_async_exception(java_thread, JNIHandles::resolve(throwable)); + // Use a VM_Operation to throw the exception. + Thread::send_async_exception(java_thread, java_throwable); } - } - else { + } else { // Either: // - target thread has not been started before being stopped, or // - target thread already terminated // We could read the threadStatus to determine which case it is // but that is overkill as it doesn't matter. We must set the // stillborn flag for the first case, and if the thread has already - // exited setting this flag has no affect + // exited setting this flag has no effect. java_lang_Thread::set_stillborn(java_thread); } JVM_END @@ -2885,12 +2886,12 @@ JVM_END JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_SuspendThread"); - oop java_thread = JNIHandles::resolve_non_null(jthread); - JavaThread* receiver = java_lang_Thread::thread(java_thread); - - if (receiver != NULL) { - // thread has run and has not exited (still on threads list) + ThreadsListHandle tlh(thread); + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL); + if (is_alive) { + // jthread refers to a live JavaThread. { MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag); if (receiver->is_external_suspend()) { @@ -2922,30 +2923,49 @@ JVM_END JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_ResumeThread"); - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate. - // We need to *always* get the threads lock here, since this operation cannot be allowed during - // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other - // threads randomly resumes threads, then a thread might not be suspended when the safepoint code - // looks at it. - MutexLocker ml(Threads_lock); - JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); - if (thr != NULL) { - // the thread has run and is not in the process of exiting - thr->java_resume(); + + ThreadsListHandle tlh(thread); + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL); + if (is_alive) { + // jthread refers to a live JavaThread. + + // This is the original comment for this Threads_lock grab: + // We need to *always* get the threads lock here, since this operation cannot be allowed during + // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other + // threads randomly resumes threads, then a thread might not be suspended when the safepoint code + // looks at it. + // + // The above comment dates back to when we had both internal and + // external suspend APIs that shared a common underlying mechanism. + // External suspend is now entirely cooperative and doesn't share + // anything with internal suspend. That said, there are some + // assumptions in the VM that an external resume grabs the + // Threads_lock. We can't drop the Threads_lock grab here until we + // resolve the assumptions that exist elsewhere. + // + MutexLocker ml(Threads_lock); + receiver->java_resume(); } JVM_END JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio)) JVMWrapper("JVM_SetThreadPriority"); - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate - MutexLocker ml(Threads_lock); - oop java_thread = JNIHandles::resolve_non_null(jthread); + + ThreadsListHandle tlh(thread); + oop java_thread = NULL; + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread); java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio); - JavaThread* thr = java_lang_Thread::thread(java_thread); - if (thr != NULL) { // Thread not yet started; priority pushed down when it is - Thread::set_priority(thr, (ThreadPriority)prio); + + if (is_alive) { + // jthread refers to a live JavaThread. + Thread::set_priority(receiver, (ThreadPriority)prio); } + // Implied else: If the JavaThread hasn't started yet, then the + // priority set in the java.lang.Thread object above will be pushed + // down when it does start. JVM_END @@ -3016,67 +3036,39 @@ JVM_END JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_CountStackFrames"); - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate - oop java_thread = JNIHandles::resolve_non_null(jthread); - bool throw_illegal_thread_state = false; + uint32_t debug_bits = 0; + ThreadsListHandle tlh(thread); + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL); int count = 0; - - { - MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); - // We need to re-resolve the java_thread, since a GC might have happened during the - // acquire of the lock - JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); - - if (thr == NULL) { - // do nothing - } else if(! thr->is_external_suspend() || ! thr->frame_anchor()->walkable()) { - // Check whether this java thread has been suspended already. If not, throws - // IllegalThreadStateException. We defer to throw that exception until - // Threads_lock is released since loading exception class has to leave VM. - // The correct way to test a thread is actually suspended is - // wait_for_ext_suspend_completion(), but we can't call that while holding - // the Threads_lock. The above tests are sufficient for our purposes - // provided the walkability of the stack is stable - which it isn't - // 100% but close enough for most practical purposes. - throw_illegal_thread_state = true; - } else { - // Count all java activation, i.e., number of vframes - for(vframeStream vfst(thr); !vfst.at_end(); vfst.next()) { - // Native frames are not counted + if (is_alive) { + // jthread refers to a live JavaThread. + if (receiver->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) { + // Count all java activation, i.e., number of vframes. + for (vframeStream vfst(receiver); !vfst.at_end(); vfst.next()) { + // Native frames are not counted. if (!vfst.method()->is_native()) count++; - } + } + } else { + THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(), + "this thread is not suspended"); } } + // Implied else: if JavaThread is not alive simply return a count of 0. - if (throw_illegal_thread_state) { - THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(), - "this thread is not suspended"); - } return count; JVM_END -// Consider: A better way to implement JVM_Interrupt() is to acquire -// Threads_lock to resolve the jthread into a Thread pointer, fetch -// Thread->platformevent, Thread->native_thr, Thread->parker, etc., -// drop Threads_lock, and the perform the unpark() and thr_kill() operations -// outside the critical section. Threads_lock is hot so we want to minimize -// the hold-time. A cleaner interface would be to decompose interrupt into -// two steps. The 1st phase, performed under Threads_lock, would return -// a closure that'd be invoked after Threads_lock was dropped. -// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and -// admit spurious wakeups. JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_Interrupt"); - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate - oop java_thread = JNIHandles::resolve_non_null(jthread); - MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); - // We need to re-resolve the java_thread, since a GC might have happened during the - // acquire of the lock - JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); - if (thr != NULL) { - Thread::interrupt(thr); + ThreadsListHandle tlh(thread); + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL); + if (is_alive) { + // jthread refers to a live JavaThread. + Thread::interrupt(receiver); } JVM_END @@ -3084,16 +3076,14 @@ JVM_END JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted)) JVMWrapper("JVM_IsInterrupted"); - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate - oop java_thread = JNIHandles::resolve_non_null(jthread); - MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock); - // We need to re-resolve the java_thread, since a GC might have happened during the - // acquire of the lock - JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)); - if (thr == NULL) { - return JNI_FALSE; + ThreadsListHandle tlh(thread); + JavaThread* receiver = NULL; + bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL); + if (is_alive) { + // jthread refers to a live JavaThread. + return (jboolean) Thread::is_interrupted(receiver, clear_interrupted != 0); } else { - return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0); + return JNI_FALSE; } JVM_END @@ -3122,14 +3112,16 @@ JVM_END JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name)) JVMWrapper("JVM_SetNativeThreadName"); - ResourceMark rm(THREAD); + + // We don't use a ThreadsListHandle here because the current thread + // must be alive. oop java_thread = JNIHandles::resolve_non_null(jthread); JavaThread* thr = java_lang_Thread::thread(java_thread); - // Thread naming only supported for the current thread, doesn't work for - // target threads. - if (Thread::current() == thr && !thr->has_attached_via_jni()) { + if (thread == thr && !thr->has_attached_via_jni()) { + // Thread naming is only supported for the current thread and // we don't set the name of an attached thread to avoid stepping - // on other programs + // on other programs. + ResourceMark rm(thread); const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name)); os::set_native_thread_name(thread_name); } @@ -3671,6 +3663,8 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject thread_handle_array->append(h); } + // The JavaThread references in thread_handle_array are validated + // in VM_ThreadDump::doit(). Handle stacktraces = ThreadService::dump_stack_traces(thread_handle_array, num_threads, CHECK_NULL); return (jobjectArray)JNIHandles::make_local(env, stacktraces()); diff --git a/src/hotspot/share/prims/jvmtiEnter.xsl b/src/hotspot/share/prims/jvmtiEnter.xsl index fbb8724c7ab..406ddc642c0 100644 --- a/src/hotspot/share/prims/jvmtiEnter.xsl +++ b/src/hotspot/share/prims/jvmtiEnter.xsl @@ -45,6 +45,7 @@ # include "prims/jvmtiEnter.hpp" # include "prims/jvmtiRawMonitor.hpp" # include "prims/jvmtiUtil.hpp" +# include "runtime/threadSMR.hpp" </xsl:text> @@ -769,47 +770,27 @@ static jvmtiError JNICALL <xsl:template match="jthread" mode="dochecksbody"> <xsl:param name="name"/> - <xsl:text> oop thread_oop = JNIHandles::resolve_external_guard(</xsl:text> + <xsl:text> err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), </xsl:text> <xsl:value-of select="$name"/> - <xsl:text>); - if (thread_oop == NULL) { + <xsl:text>, &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { </xsl:text> <xsl:apply-templates select=".." mode="traceError"> - <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param> - <xsl:with-param name="comment"> - jthread resolved to NULL - jthread = " PTR_FORMAT "</xsl:with-param> + <xsl:with-param name="err">err</xsl:with-param> + <xsl:with-param name="comment"> - jthread did not convert to a JavaThread - jthread = " PTR_FORMAT "</xsl:with-param> <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param> </xsl:apply-templates> <xsl:text> } - if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { </xsl:text> - <xsl:apply-templates select=".." mode="traceError"> - <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param> - <xsl:with-param name="comment"> - oop is not a thread - jthread = " PTR_FORMAT "</xsl:with-param> - <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param> - </xsl:apply-templates> - <xsl:text> - } - java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == NULL) { -</xsl:text> - <xsl:apply-templates select=".." mode="traceError"> - <xsl:with-param name="err"> - <xsl:text>JVMTI_ERROR_THREAD_NOT_ALIVE</xsl:text> - </xsl:with-param> - <xsl:with-param name="comment"> - not a Java thread - jthread = " PTR_FORMAT "</xsl:with-param> - <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param> - </xsl:apply-templates> - <xsl:text> - } -</xsl:text> </xsl:template> <xsl:template match="jthread" mode="dochecks"> <xsl:param name="name"/> <!-- If we convert and test threads --> <xsl:if test="count(@impl)=0 or not(contains(@impl,'noconvert'))"> - <xsl:text> JavaThread* java_thread; + <xsl:text> JavaThread* java_thread = NULL; + ThreadsListHandle tlh(this_thread); </xsl:text> <xsl:choose> <xsl:when test="count(@null)=0"> diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index b67e23f45ad..bff8274a3ea 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -62,6 +62,7 @@ #include "runtime/reflectionUtils.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/timerTrace.hpp" #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" @@ -162,7 +163,6 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) { *data_ptr = (state == NULL) ? NULL : state->env_thread_state(this)->get_agent_thread_local_storage_data(); } else { - // jvmti_GetThreadLocalStorage is "in native" and doesn't transition // the thread to _thread_in_vm. However, when the TLS for a thread // other than the current thread is required we need to transition @@ -172,17 +172,13 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) { VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread) debug_only(VMNativeEntryWrapper __vew;) - oop thread_oop = JNIHandles::resolve_external_guard(thread); - if (thread_oop == NULL) { - return JVMTI_ERROR_INVALID_THREAD; - } - if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { - return JVMTI_ERROR_INVALID_THREAD; - } - JavaThread* java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == NULL) { - return JVMTI_ERROR_THREAD_NOT_ALIVE; + JavaThread* java_thread = NULL; + ThreadsListHandle tlh(current_thread); + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { + return err; } + JvmtiThreadState* state = java_thread->jvmti_thread_state(); *data_ptr = (state == NULL) ? NULL : state->env_thread_state(this)->get_agent_thread_local_storage_data(); @@ -518,42 +514,60 @@ JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_c // event_thread - NULL is a valid value, must be checked jvmtiError JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread, ...) { - JavaThread* java_thread = NULL; - if (event_thread != NULL) { - oop thread_oop = JNIHandles::resolve_external_guard(event_thread); - if (thread_oop == NULL) { - return JVMTI_ERROR_INVALID_THREAD; + if (event_thread == NULL) { + // Can be called at Agent_OnLoad() time with event_thread == NULL + // when Thread::current() does not work yet so we cannot create a + // ThreadsListHandle that is common to both thread-specific and + // global code paths. + + // event_type must be valid + if (!JvmtiEventController::is_valid_event_type(event_type)) { + return JVMTI_ERROR_INVALID_EVENT_TYPE; } - if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { - return JVMTI_ERROR_INVALID_THREAD; + + bool enabled = (mode == JVMTI_ENABLE); + + // assure that needed capabilities are present + if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) { + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; } - java_thread = java_lang_Thread::thread(thread_oop); - if (java_thread == NULL) { - return JVMTI_ERROR_THREAD_NOT_ALIVE; + + if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { + record_class_file_load_hook_enabled(); } - } + JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled); + } else { + // We have a specified event_thread. - // event_type must be valid - if (!JvmtiEventController::is_valid_event_type(event_type)) { - return JVMTI_ERROR_INVALID_EVENT_TYPE; - } + JavaThread* java_thread = NULL; + ThreadsListHandle tlh; + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { + return err; + } - // global events cannot be controlled at thread level. - if (java_thread != NULL && JvmtiEventController::is_global_event(event_type)) { - return JVMTI_ERROR_ILLEGAL_ARGUMENT; - } + // event_type must be valid + if (!JvmtiEventController::is_valid_event_type(event_type)) { + return JVMTI_ERROR_INVALID_EVENT_TYPE; + } - bool enabled = (mode == JVMTI_ENABLE); + // global events cannot be controlled at thread level. + if (JvmtiEventController::is_global_event(event_type)) { + return JVMTI_ERROR_ILLEGAL_ARGUMENT; + } - // assure that needed capabilities are present - if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) { - return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; - } + bool enabled = (mode == JVMTI_ENABLE); - if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { - record_class_file_load_hook_enabled(); + // assure that needed capabilities are present + if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) { + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + + if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) { + record_class_file_load_hook_enabled(); + } + JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled); } - JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled); return JVMTI_ERROR_NONE; } /* end SetEventNotificationMode */ @@ -817,35 +831,45 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { // thread_state_ptr - pre-checked for NULL jvmtiError JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) { - jint state; - oop thread_oop; - JavaThread* thr; + JavaThread* current_thread = JavaThread::current(); + JavaThread* java_thread = NULL; + oop thread_oop = NULL; + ThreadsListHandle tlh(current_thread); if (thread == NULL) { - thread_oop = JavaThread::current()->threadObj(); - } else { - thread_oop = JNIHandles::resolve_external_guard(thread); - } + java_thread = current_thread; + thread_oop = java_thread->threadObj(); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { - return JVMTI_ERROR_INVALID_THREAD; + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } + } else { + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop); + if (err != JVMTI_ERROR_NONE) { + // We got an error code so we don't have a JavaThread *, but + // only return an error from here if we didn't get a valid + // thread_oop. + if (thread_oop == NULL) { + return err; + } + // We have a valid thread_oop so we can return some thread state. + } } // get most state bits - state = (jint)java_lang_Thread::get_thread_status(thread_oop); + jint state = (jint)java_lang_Thread::get_thread_status(thread_oop); - // add more state bits - thr = java_lang_Thread::thread(thread_oop); - if (thr != NULL) { - JavaThreadState jts = thr->thread_state(); + if (java_thread != NULL) { + // We have a JavaThread* so add more state bits. + JavaThreadState jts = java_thread->thread_state(); - if (thr->is_being_ext_suspended()) { + if (java_thread->is_being_ext_suspended()) { state |= JVMTI_THREAD_STATE_SUSPENDED; } if (jts == _thread_in_native) { state |= JVMTI_THREAD_STATE_IN_NATIVE; } - OSThread* osThread = thr->osthread(); + OSThread* osThread = java_thread->osthread(); if (osThread != NULL && osThread->interrupted()) { state |= JVMTI_THREAD_STATE_INTERRUPTED; } @@ -891,7 +915,6 @@ JvmtiEnv::GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) { thread_objs[i] = Handle(tle.get_threadObj(i)); } - // have to make global handles outside of Threads_lock jthread *jthreads = new_jthreadArray(nthreads, thread_objs); NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY); @@ -935,19 +958,12 @@ JvmtiEnv::SuspendThread(JavaThread* java_thread) { jvmtiError JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { int needSafepoint = 0; // > 0 if we need a safepoint + ThreadsListHandle tlh; for (int i = 0; i < request_count; i++) { - JavaThread *java_thread = get_JavaThread(request_list[i]); - if (java_thread == NULL) { - results[i] = JVMTI_ERROR_INVALID_THREAD; - continue; - } - // the thread has not yet run or has exited (not on threads list) - if (java_thread->threadObj() == NULL) { - results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; - continue; - } - if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) { - results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE; + JavaThread *java_thread = NULL; + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { + results[i] = err; continue; } // don't allow hidden thread suspend request. @@ -1018,10 +1034,12 @@ JvmtiEnv::ResumeThread(JavaThread* java_thread) { // results - pre-checked for NULL jvmtiError JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) { + ThreadsListHandle tlh; for (int i = 0; i < request_count; i++) { - JavaThread *java_thread = get_JavaThread(request_list[i]); - if (java_thread == NULL) { - results[i] = JVMTI_ERROR_INVALID_THREAD; + JavaThread* java_thread = NULL; + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { + results[i] = err; continue; } // don't allow hidden thread resume request. @@ -1039,7 +1057,7 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt continue; } - results[i] = JVMTI_ERROR_NONE; // indicate successful suspend + results[i] = JVMTI_ERROR_NONE; // indicate successful resume } // per-thread resume results returned via results parameter return JVMTI_ERROR_NONE; @@ -1064,20 +1082,14 @@ JvmtiEnv::StopThread(JavaThread* java_thread, jobject exception) { // thread - NOT pre-checked jvmtiError JvmtiEnv::InterruptThread(jthread thread) { - oop thread_oop = JNIHandles::resolve_external_guard(thread); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) - return JVMTI_ERROR_INVALID_THREAD; - + // TODO: this is very similar to JVM_Interrupt(); share code in future JavaThread* current_thread = JavaThread::current(); - - // Todo: this is a duplicate of JVM_Interrupt; share code in future - // Ensure that the C++ Thread and OSThread structures aren't freed before we operate - MutexLockerEx ml(current_thread->threadObj() == thread_oop ? NULL : Threads_lock); - // We need to re-resolve the java_thread, since a GC might have happened during the - // acquire of the lock - - JavaThread* java_thread = java_lang_Thread::thread(JNIHandles::resolve_external_guard(thread)); - NULL_CHECK(java_thread, JVMTI_ERROR_THREAD_NOT_ALIVE); + JavaThread* java_thread = NULL; + ThreadsListHandle tlh(current_thread); + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL); + if (err != JVMTI_ERROR_NONE) { + return err; + } Thread::interrupt(java_thread); @@ -1094,16 +1106,28 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) { HandleMark hm; JavaThread* current_thread = JavaThread::current(); + ThreadsListHandle tlh(current_thread); // if thread is NULL the current thread is used - oop thread_oop; + oop thread_oop = NULL; if (thread == NULL) { thread_oop = current_thread->threadObj(); + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { + return JVMTI_ERROR_INVALID_THREAD; + } } else { - thread_oop = JNIHandles::resolve_external_guard(thread); + JavaThread* java_thread = NULL; + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop); + if (err != JVMTI_ERROR_NONE) { + // We got an error code so we don't have a JavaThread *, but + // only return an error from here if we didn't get a valid + // thread_oop. + if (thread_oop == NULL) { + return err; + } + // We have a valid thread_oop so we can return some thread info. + } } - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) - return JVMTI_ERROR_INVALID_THREAD; Handle thread_obj(current_thread, thread_oop); Handle name; @@ -1272,17 +1296,31 @@ JvmtiEnv::GetCurrentContendedMonitor(JavaThread* java_thread, jobject* monitor_p // arg - NULL is a valid value, must be checked jvmtiError JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) { - oop thread_oop = JNIHandles::resolve_external_guard(thread); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { + JavaThread* current_thread = JavaThread::current(); + + JavaThread* java_thread = NULL; + oop thread_oop = NULL; + ThreadsListHandle tlh(current_thread); + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop); + if (err != JVMTI_ERROR_NONE) { + // We got an error code so we don't have a JavaThread *, but + // only return an error from here if we didn't get a valid + // thread_oop. + if (thread_oop == NULL) { + return err; + } + // We have a valid thread_oop. + } + + if (java_thread != NULL) { + // 'thread' refers to an existing JavaThread. return JVMTI_ERROR_INVALID_THREAD; } + if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { return JVMTI_ERROR_INVALID_PRIORITY; } - //Thread-self - JavaThread* current_thread = JavaThread::current(); - Handle thread_hndl(current_thread, thread_oop); { MutexLocker mu(Threads_lock); // grab Threads_lock @@ -1292,7 +1330,9 @@ JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* ar // At this point it may be possible that no osthread was created for the // JavaThread due to lack of memory. if (new_thread == NULL || new_thread->osthread() == NULL) { - if (new_thread) delete new_thread; + if (new_thread != NULL) { + new_thread->smr_delete(); + } return JVMTI_ERROR_OUT_OF_MEMORY; } @@ -1394,36 +1434,53 @@ JvmtiEnv::GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jth int ngroups = 0; int hidden_threads = 0; - ResourceMark rm; - HandleMark hm; + ResourceMark rm(current_thread); + HandleMark hm(current_thread); Handle group_hdl(current_thread, group_obj); - { MutexLocker mu(Threads_lock); + { // Cannot allow thread or group counts to change. + MutexLocker mu(Threads_lock); nthreads = java_lang_ThreadGroup::nthreads(group_hdl()); ngroups = java_lang_ThreadGroup::ngroups(group_hdl()); if (nthreads > 0) { + ThreadsListHandle tlh(current_thread); objArrayOop threads = java_lang_ThreadGroup::threads(group_hdl()); assert(nthreads <= threads->length(), "too many threads"); thread_objs = NEW_RESOURCE_ARRAY(Handle,nthreads); for (int i=0, j=0; i<nthreads; i++) { oop thread_obj = threads->obj_at(i); assert(thread_obj != NULL, "thread_obj is NULL"); - JavaThread *javathread = java_lang_Thread::thread(thread_obj); - // Filter out hidden java threads. - if (javathread != NULL && javathread->is_hidden_from_external_view()) { - hidden_threads++; - continue; + JavaThread *java_thread = NULL; + jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &java_thread); + if (err == JVMTI_ERROR_NONE) { + // Have a valid JavaThread*. + if (java_thread->is_hidden_from_external_view()) { + // Filter out hidden java threads. + hidden_threads++; + continue; + } + } else { + // We couldn't convert thread_obj into a JavaThread*. + if (err == JVMTI_ERROR_INVALID_THREAD) { + // The thread_obj does not refer to a java.lang.Thread object + // so skip it. + hidden_threads++; + continue; + } + // We have a valid thread_obj, but no JavaThread*; the caller + // can still have limited use for the thread_obj. } thread_objs[j++] = Handle(current_thread, thread_obj); } nthreads -= hidden_threads; - } + } // ThreadsListHandle is destroyed here. + if (ngroups > 0) { objArrayOop groups = java_lang_ThreadGroup::groups(group_hdl()); - assert(ngroups <= groups->length(), "too many threads"); + assert(ngroups <= groups->length(), "too many groups"); group_objs = NEW_RESOURCE_ARRAY(Handle,ngroups); for (int i=0; i<ngroups; i++) { oop group_obj = groups->obj_at(i); @@ -1556,7 +1613,7 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) { } // Check if java_thread is fully suspended - if (!is_thread_fully_suspended(java_thread, true /* wait for suspend completion */, &debug_bits)) { + if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) { return JVMTI_ERROR_THREAD_NOT_SUSPENDED; } // Check to see if a PopFrame was already in progress @@ -1686,8 +1743,8 @@ JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) { return JVMTI_ERROR_THREAD_NOT_ALIVE; } - if (!JvmtiEnv::is_thread_fully_suspended(java_thread, true, &debug_bits)) { - return JVMTI_ERROR_THREAD_NOT_SUSPENDED; + if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) { + return JVMTI_ERROR_THREAD_NOT_SUSPENDED; } if (TraceJVMTICalls) { diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp index 2e8918c57e1..18d59fbc2e8 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.cpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp @@ -44,6 +44,7 @@ #include "runtime/objectMonitor.inline.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vframe_hp.hpp" #include "runtime/vmThread.hpp" @@ -487,37 +488,6 @@ JvmtiEnvBase::set_event_callbacks(const jvmtiEventCallbacks* callbacks, } } -// Called from JVMTI entry points which perform stack walking. If the -// associated JavaThread is the current thread, then wait_for_suspend -// is not used. Otherwise, it determines if we should wait for the -// "other" thread to complete external suspension. (NOTE: in future -// releases the suspension mechanism should be reimplemented so this -// is not necessary.) -// -bool -JvmtiEnvBase::is_thread_fully_suspended(JavaThread* thr, bool wait_for_suspend, uint32_t *bits) { - // "other" threads require special handling - if (thr != JavaThread::current()) { - if (wait_for_suspend) { - // We are allowed to wait for the external suspend to complete - // so give the other thread a chance to get suspended. - if (!thr->wait_for_ext_suspend_completion(SuspendRetryCount, - SuspendRetryDelay, bits)) { - // didn't make it so let the caller know - return false; - } - } - // We aren't allowed to wait for the external suspend to complete - // so if the other thread isn't externally suspended we need to - // let the caller know. - else if (!thr->is_ext_suspend_completed_with_lock(bits)) { - return false; - } - } - - return true; -} - // In the fullness of time, all users of the method should instead // directly use allocate, besides being cleaner and faster, this will @@ -560,19 +530,6 @@ JvmtiEnvBase::new_jthreadGroupArray(int length, Handle *handles) { return (jthreadGroup *) new_jobjectArray(length,handles); } - -JavaThread * -JvmtiEnvBase::get_JavaThread(jthread jni_thread) { - oop t = JNIHandles::resolve_external_guard(jni_thread); - if (t == NULL || !t->is_a(SystemDictionary::Thread_klass())) { - return NULL; - } - // The following returns NULL if the thread has not yet run or is in - // process of exiting - return java_lang_Thread::thread(t); -} - - // return the vframe on the specified thread and depth, NULL if no such frame vframe* JvmtiEnvBase::vframeFor(JavaThread* java_thread, jint depth) { @@ -670,7 +627,7 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre uint32_t debug_bits = 0; #endif assert((SafepointSynchronize::is_at_safepoint() || - is_thread_fully_suspended(java_thread, false, &debug_bits)), + java_thread->is_thread_fully_suspended(false, &debug_bits)), "at safepoint or target thread is suspended"); oop obj = NULL; ObjectMonitor *mon = java_thread->current_waiting_monitor(); @@ -709,7 +666,7 @@ JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_th uint32_t debug_bits = 0; #endif assert((SafepointSynchronize::is_at_safepoint() || - is_thread_fully_suspended(java_thread, false, &debug_bits)), + java_thread->is_thread_fully_suspended(false, &debug_bits)), "at safepoint or target thread is suspended"); if (java_thread->has_last_Java_frame()) { @@ -831,7 +788,7 @@ JvmtiEnvBase::get_stack_trace(JavaThread *java_thread, uint32_t debug_bits = 0; #endif assert((SafepointSynchronize::is_at_safepoint() || - is_thread_fully_suspended(java_thread, false, &debug_bits)), + java_thread->is_thread_fully_suspended(false, &debug_bits)), "at safepoint or target thread is suspended"); int count = 0; if (java_thread->has_last_Java_frame()) { @@ -914,7 +871,7 @@ JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth, uint32_t debug_bits = 0; #endif assert((SafepointSynchronize::is_at_safepoint() || - is_thread_fully_suspended(java_thread, false, &debug_bits)), + java_thread->is_thread_fully_suspended(false, &debug_bits)), "at safepoint or target thread is suspended"); Thread* current_thread = Thread::current(); ResourceMark rm(current_thread); @@ -976,7 +933,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec // first derive the object's owner and entry_count (if any) { // Revoke any biases before querying the mark word - if (SafepointSynchronize::is_at_safepoint()) { + if (at_safepoint) { BiasedLocking::revoke_at_safepoint(hobj); } else { BiasedLocking::revoke_and_rebias(hobj, false, calling_thread); @@ -1008,11 +965,11 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec } if (owner != NULL) { + // Use current thread since function can be called from a + // JavaThread or the VMThread. + ThreadsListHandle tlh; // This monitor is owned so we have to find the owning JavaThread. - // Since owning_thread_from_monitor_owner() grabs a lock, GC can - // move our object at this point. However, our owner value is safe - // since it is either the Lock word on a stack or a JavaThread *. - owning_thread = Threads::owning_thread_from_monitor_owner(owner, !at_safepoint); + owning_thread = Threads::owning_thread_from_monitor_owner(tlh.list(), owner); // Cannot assume (owning_thread != NULL) here because this function // may not have been called at a safepoint and the owning_thread // might not be suspended. @@ -1021,7 +978,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec // or it has to be suspended. Any of these conditions will prevent both // contending and waiting threads from modifying the state of // the monitor. - if (!at_safepoint && !JvmtiEnv::is_thread_fully_suspended(owning_thread, true, &debug_bits)) { + if (!at_safepoint && !owning_thread->is_thread_fully_suspended(true, &debug_bits)) { // Don't worry! This return of JVMTI_ERROR_THREAD_NOT_SUSPENDED // will not make it back to the JVM/TI agent. The error code will // get intercepted in JvmtiEnv::GetObjectMonitorUsage() which @@ -1033,7 +990,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec ret.owner = (jthread)jni_reference(calling_thread, th); } // implied else: no owner - } + } // ThreadsListHandle is destroyed here. if (owning_thread != NULL) { // monitor is owned // The recursions field of a monitor does not reflect recursions @@ -1084,13 +1041,15 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec if (ret.waiter_count > 0) { // we have contending and/or waiting threads HandleMark hm; + // Use current thread since function can be called from a + // JavaThread or the VMThread. + ThreadsListHandle tlh; if (nWant > 0) { // we have contending threads ResourceMark rm; // get_pending_threads returns only java thread so we do not need to - // check for non java threads. - GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads( - nWant, (address)mon, !at_safepoint); + // check for non java threads. + GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(tlh.list(), nWant, (address)mon); if (wantList->length() < nWant) { // robustness: the pending list has gotten smaller nWant = wantList->length(); @@ -1101,7 +1060,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec // thread could potentially change the state of the monitor by // entering it. The JVM/TI spec doesn't allow this. if (owning_thread == NULL && !at_safepoint & - !JvmtiEnv::is_thread_fully_suspended(pending_thread, true, &debug_bits)) { + !pending_thread->is_thread_fully_suspended(true, &debug_bits)) { if (ret.owner != NULL) { destroy_jni_reference(calling_thread, ret.owner); } @@ -1139,7 +1098,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec waiter = mon->next_waiter(waiter); } } - } + } // ThreadsListHandle is destroyed here. // Adjust count. nWant and nWait count values may be less than original. ret.waiter_count = nWant + nWait; @@ -1291,14 +1250,23 @@ VM_GetThreadListStackTraces::doit() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); ResourceMark rm; + ThreadsListHandle tlh; for (int i = 0; i < _thread_count; ++i) { jthread jt = _thread_list[i]; - oop thread_oop = JNIHandles::resolve_external_guard(jt); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { - set_result(JVMTI_ERROR_INVALID_THREAD); - return; + JavaThread* java_thread = NULL; + oop thread_oop = NULL; + jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), jt, &java_thread, &thread_oop); + if (err != JVMTI_ERROR_NONE) { + // We got an error code so we don't have a JavaThread *, but + // only return an error from here if we didn't get a valid + // thread_oop. + if (thread_oop == NULL) { + set_result(err); + return; + } + // We have a valid thread_oop. } - fill_frames(jt, java_lang_Thread::thread(thread_oop), thread_oop); + fill_frames(jt, java_thread, thread_oop); } allocate_and_fill_stacks(_thread_count); } @@ -1309,7 +1277,7 @@ VM_GetAllStackTraces::doit() { ResourceMark rm; _final_thread_count = 0; - for (JavaThread *jt = Threads::first(); jt != NULL; jt = jt->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { oop thread_oop = jt->threadObj(); if (thread_oop != NULL && !jt->is_exiting() && @@ -1404,9 +1372,7 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState } // Check if java_thread is fully suspended - if (!is_thread_fully_suspended(java_thread, - true /* wait for suspend completion */, - &debug_bits)) { + if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) { return JVMTI_ERROR_THREAD_NOT_SUSPENDED; } @@ -1521,3 +1487,79 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje return JVMTI_ERROR_NONE; } +void +VM_UpdateForPopTopFrame::doit() { + JavaThread* jt = _state->get_thread(); + ThreadsListHandle tlh; + if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { + _state->update_for_pop_top_frame(); + } else { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + } +} + +void +VM_SetFramePop::doit() { + JavaThread* jt = _state->get_thread(); + ThreadsListHandle tlh; + if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { + int frame_number = _state->count_frames() - _depth; + _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); + } else { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + } +} + +void +VM_GetOwnedMonitorInfo::doit() { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + ThreadsListHandle tlh; + if (_java_thread != NULL && tlh.includes(_java_thread) + && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) { + _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread, + _owned_monitors_list); + } +} + +void +VM_GetCurrentContendedMonitor::doit() { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + ThreadsListHandle tlh; + if (_java_thread != NULL && tlh.includes(_java_thread) + && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) { + _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr); + } +} + +void +VM_GetStackTrace::doit() { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + ThreadsListHandle tlh; + if (_java_thread != NULL && tlh.includes(_java_thread) + && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) { + _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread, + _start_depth, _max_count, + _frame_buffer, _count_ptr); + } +} + +void +VM_GetFrameCount::doit() { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + JavaThread* jt = _state->get_thread(); + ThreadsListHandle tlh; + if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { + _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr); + } +} + +void +VM_GetFrameLocation::doit() { + _result = JVMTI_ERROR_THREAD_NOT_ALIVE; + ThreadsListHandle tlh; + if (_java_thread != NULL && tlh.includes(_java_thread) + && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) { + _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth, + _method_ptr, _location_ptr); + } +} diff --git a/src/hotspot/share/prims/jvmtiEnvBase.hpp b/src/hotspot/share/prims/jvmtiEnvBase.hpp index c5786aca4af..84e3f54ae6d 100644 --- a/src/hotspot/share/prims/jvmtiEnvBase.hpp +++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp @@ -280,9 +280,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> { jthread * new_jthreadArray(int length, Handle *handles); jthreadGroup * new_jthreadGroupArray(int length, Handle *handles); - // convert from JNIHandle to JavaThread * - JavaThread * get_JavaThread(jthread jni_thread); - // convert to a jni jclass from a non-null Klass* jclass get_jni_class_non_null(Klass* k); @@ -297,11 +294,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> { public: // get a field descriptor for the specified class and field static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd); - // test for suspend - most (all?) of these should go away - static bool is_thread_fully_suspended(JavaThread *thread, - bool wait_for_suspend, - uint32_t *bits); - // JVMTI API helper functions which are called at safepoint or thread is suspended. jvmtiError get_frame_count(JvmtiThreadState *state, jint *count_ptr); @@ -360,14 +352,7 @@ public: } VMOp_Type type() const { return VMOp_UpdateForPopTopFrame; } jvmtiError result() { return _result; } - void doit() { - JavaThread* jt = _state->get_thread(); - if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { - _state->update_for_pop_top_frame(); - } else { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - } - } + void doit(); }; // VM operation to set frame pop. @@ -390,15 +375,7 @@ public: bool allow_nested_vm_operations() const { return true; } VMOp_Type type() const { return VMOp_SetFramePop; } jvmtiError result() { return _result; } - void doit() { - JavaThread* jt = _state->get_thread(); - if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { - int frame_number = _state->count_frames() - _depth; - _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number); - } else { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - } - } + void doit(); }; @@ -422,14 +399,7 @@ public: _result = JVMTI_ERROR_NONE; } VMOp_Type type() const { return VMOp_GetOwnedMonitorInfo; } - void doit() { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - if (Threads::includes(_java_thread) && !_java_thread->is_exiting() - && _java_thread->threadObj() != NULL) { - _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread, - _owned_monitors_list); - } - } + void doit(); jvmtiError result() { return _result; } }; @@ -476,13 +446,7 @@ public: } VMOp_Type type() const { return VMOp_GetCurrentContendedMonitor; } jvmtiError result() { return _result; } - void doit() { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - if (Threads::includes(_java_thread) && !_java_thread->is_exiting() && - _java_thread->threadObj() != NULL) { - _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr); - } - } + void doit(); }; // VM operation to get stack trace at safepoint. @@ -509,15 +473,7 @@ public: } jvmtiError result() { return _result; } VMOp_Type type() const { return VMOp_GetStackTrace; } - void doit() { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - if (Threads::includes(_java_thread) && !_java_thread->is_exiting() - && _java_thread->threadObj() != NULL) { - _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread, - _start_depth, _max_count, - _frame_buffer, _count_ptr); - } - } + void doit(); }; // forward declaration @@ -607,13 +563,7 @@ public: } VMOp_Type type() const { return VMOp_GetFrameCount; } jvmtiError result() { return _result; } - void doit() { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - JavaThread* jt = _state->get_thread(); - if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) { - _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr); - } - } + void doit(); }; // VM operation to frame location at safepoint. @@ -637,14 +587,7 @@ public: } VMOp_Type type() const { return VMOp_GetFrameLocation; } jvmtiError result() { return _result; } - void doit() { - _result = JVMTI_ERROR_THREAD_NOT_ALIVE; - if (Threads::includes(_java_thread) && !_java_thread->is_exiting() && - _java_thread->threadObj() != NULL) { - _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth, - _method_ptr, _location_ptr); - } - } + void doit(); }; diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp index d7ffd105596..643c4409274 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -35,6 +35,7 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/javaCalls.hpp" #include "runtime/signature.hpp" +#include "runtime/thread.inline.hpp" #include "runtime/vframe.hpp" #include "runtime/vm_operations.hpp" diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index cdad3264c82..3c919c61106 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -33,7 +33,8 @@ #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiThreadState.inline.hpp" #include "runtime/frame.hpp" -#include "runtime/thread.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vframe_hp.hpp" #include "runtime/vmThread.hpp" @@ -580,13 +581,10 @@ JvmtiEventControllerPrivate::recompute_enabled() { // filtered events and there weren't last time if ( (any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) != 0 && (was_any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) == 0) { - { - MutexLocker mu(Threads_lock); //hold the Threads_lock for the iteration - for (JavaThread *tp = Threads::first(); tp != NULL; tp = tp->next()) { - // state_for_while_locked() makes tp->is_exiting() check - JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing - } - }// release Threads_lock + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *tp = jtiwh.next(); ) { + // state_for_while_locked() makes tp->is_exiting() check + JvmtiThreadState::state_for_while_locked(tp); // create the thread state if missing + } } // compute and set thread-filtered events diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 7d8de627808..87d0f2bf40a 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -53,6 +53,7 @@ #include "runtime/objectMonitor.inline.hpp" #include "runtime/os.inline.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "services/serviceUtil.hpp" #include "utilities/macros.hpp" @@ -721,6 +722,108 @@ JvmtiExport::get_all_native_method_prefixes(int* count_ptr) { } } +// Convert an external thread reference to a JavaThread found on the +// specified ThreadsList. The ThreadsListHandle in the caller "protects" +// the returned JavaThread *. +// +// If thread_oop_p is not NULL, then the caller wants to use the oop +// after this call so the oop is returned. On success, *jt_pp is set +// to the converted JavaThread * and JVMTI_ERROR_NONE is returned. +// On error, returns various JVMTI_ERROR_* values. +// +jvmtiError +JvmtiExport::cv_external_thread_to_JavaThread(ThreadsList * t_list, + jthread thread, + JavaThread ** jt_pp, + oop * thread_oop_p) { + assert(t_list != NULL, "must have a ThreadsList"); + assert(jt_pp != NULL, "must have a return JavaThread pointer"); + // thread_oop_p is optional so no assert() + + oop thread_oop = JNIHandles::resolve_external_guard(thread); + if (thread_oop == NULL) { + // NULL jthread, GC'ed jthread or a bad JNI handle. + return JVMTI_ERROR_INVALID_THREAD; + } + // Looks like an oop at this point. + + if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { + // The oop is not a java.lang.Thread. + return JVMTI_ERROR_INVALID_THREAD; + } + // Looks like a java.lang.Thread oop at this point. + + if (thread_oop_p != NULL) { + // Return the oop to the caller; the caller may still want + // the oop even if this function returns an error. + *thread_oop_p = thread_oop; + } + + JavaThread * java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + // The java.lang.Thread does not contain a JavaThread * so it has + // not yet run or it has died. + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + // Looks like a live JavaThread at this point. + + // We do not check the EnableThreadSMRExtraValidityChecks option + // for this includes() call because JVM/TI's spec is tighter. + if (!t_list->includes(java_thread)) { + // Not on the JavaThreads list so it is not alive. + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + + // Return a live JavaThread that is "protected" by the + // ThreadsListHandle in the caller. + *jt_pp = java_thread; + + return JVMTI_ERROR_NONE; +} + +// Convert an oop to a JavaThread found on the specified ThreadsList. +// The ThreadsListHandle in the caller "protects" the returned +// JavaThread *. +// +// On success, *jt_pp is set to the converted JavaThread * and +// JVMTI_ERROR_NONE is returned. On error, returns various +// JVMTI_ERROR_* values. +// +jvmtiError +JvmtiExport::cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop, + JavaThread ** jt_pp) { + assert(t_list != NULL, "must have a ThreadsList"); + assert(thread_oop != NULL, "must have an oop"); + assert(jt_pp != NULL, "must have a return JavaThread pointer"); + + if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { + // The oop is not a java.lang.Thread. + return JVMTI_ERROR_INVALID_THREAD; + } + // Looks like a java.lang.Thread oop at this point. + + JavaThread * java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + // The java.lang.Thread does not contain a JavaThread * so it has + // not yet run or it has died. + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + // Looks like a live JavaThread at this point. + + // We do not check the EnableThreadSMRExtraValidityChecks option + // for this includes() call because JVM/TI's spec is tighter. + if (!t_list->includes(java_thread)) { + // Not on the JavaThreads list so it is not alive. + return JVMTI_ERROR_THREAD_NOT_ALIVE; + } + + // Return a live JavaThread that is "protected" by the + // ThreadsListHandle in the caller. + *jt_pp = java_thread; + + return JVMTI_ERROR_NONE; +} + class JvmtiClassFileLoadHookPoster : public StackObj { private: Symbol* _h_name; @@ -2685,8 +2788,7 @@ void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) { return; } - // Runs at safepoint. So no need to acquire Threads_lock. - for (JavaThread *jthr = Threads::first(); jthr != NULL; jthr = jthr->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) { JvmtiThreadState *state = jthr->jvmti_thread_state(); if (state != NULL) { JvmtiVMObjectAllocEventCollector *collector; diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp index 7bae70b626e..3421150cab2 100644 --- a/src/hotspot/share/prims/jvmtiExport.hpp +++ b/src/hotspot/share/prims/jvmtiExport.hpp @@ -399,6 +399,14 @@ class JvmtiExport : public AllStatic { // SetNativeMethodPrefix support static char** get_all_native_method_prefixes(int* count_ptr) NOT_JVMTI_RETURN_(NULL); + + // JavaThread lifecycle support: + static jvmtiError cv_external_thread_to_JavaThread(ThreadsList * t_list, + jthread thread, + JavaThread ** jt_pp, + oop * thread_oop_p); + static jvmtiError cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop, + JavaThread ** jt_pp); }; // Support class used by JvmtiDynamicCodeEventCollector and others. It diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index 4869a653144..82cdabdbd9b 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -46,6 +46,7 @@ #include "runtime/serviceThread.hpp" #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vframe_hp.hpp" #include "runtime/vm_operations.hpp" @@ -878,10 +879,9 @@ bool JvmtiSuspendControl::resume(JavaThread *java_thread) { void JvmtiSuspendControl::print() { #ifndef PRODUCT - MutexLocker mu(Threads_lock); LogStreamHandle(Trace, jvmti) log_stream; log_stream.print("Suspended Threads: ["); - for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { #ifdef JVMTI_TRACE const char *name = JvmtiTrace::safe_get_thread_name(thread); #else diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index c73842e0500..abca485f97b 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -43,6 +43,7 @@ #include "oops/oop.inline.hpp" #include "prims/jvmtiImpl.hpp" #include "prims/jvmtiRedefineClasses.hpp" +#include "prims/jvmtiThreadState.inline.hpp" #include "prims/resolvedMethodTable.hpp" #include "prims/methodComparator.hpp" #include "runtime/deoptimization.hpp" diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index 3a45b2c8eff..6ea720a1e44 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -45,6 +45,8 @@ #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/reflectionUtils.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" @@ -3174,7 +3176,7 @@ inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread, // stack to find all references and local JNI refs. inline bool VM_HeapWalkOperation::collect_stack_roots() { JNILocalRootsClosure blk; - for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { oop threadObj = thread->threadObj(); if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { // Collect the simple root for this thread before we diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 9dfb9b85309..6633c1c570f 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -336,34 +336,10 @@ class JvmtiThreadState : public CHeapObj<mtInternal> { // already holding JvmtiThreadState_lock - retrieve or create JvmtiThreadState // Can return NULL if JavaThread is exiting. - inline static JvmtiThreadState *state_for_while_locked(JavaThread *thread) { - assert(JvmtiThreadState_lock->is_locked(), "sanity check"); - - JvmtiThreadState *state = thread->jvmti_thread_state(); - if (state == NULL) { - if (thread->is_exiting()) { - // don't add a JvmtiThreadState to a thread that is exiting - return NULL; - } - - state = new JvmtiThreadState(thread); - } - return state; - } - + static JvmtiThreadState *state_for_while_locked(JavaThread *thread); // retrieve or create JvmtiThreadState // Can return NULL if JavaThread is exiting. - inline static JvmtiThreadState *state_for(JavaThread *thread) { - JvmtiThreadState *state = thread->jvmti_thread_state(); - if (state == NULL) { - MutexLocker mu(JvmtiThreadState_lock); - // check again with the lock held - state = state_for_while_locked(thread); - } else { - CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); - } - return state; - } + static JvmtiThreadState *state_for(JavaThread *thread); // JVMTI ForceEarlyReturn support diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp index 1b9926fb1da..e3859a3334d 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2017, 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 @@ -68,4 +68,31 @@ void JvmtiThreadState::set_head_env_thread_state(JvmtiEnvThreadState* ets) { _head_env_thread_state = ets; } +inline JvmtiThreadState* JvmtiThreadState::state_for_while_locked(JavaThread *thread) { + assert(JvmtiThreadState_lock->is_locked(), "sanity check"); + + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + if (thread->is_exiting()) { + // don't add a JvmtiThreadState to a thread that is exiting + return NULL; + } + + state = new JvmtiThreadState(thread); + } + return state; +} + +inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread) { + JvmtiThreadState *state = thread->jvmti_thread_state(); + if (state == NULL) { + MutexLocker mu(JvmtiThreadState_lock); + // check again with the lock held + state = state_for_while_locked(thread); + } else { + CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); + } + return state; +} + #endif // SHARE_VM_PRIMS_JVMTITHREADSTATE_INLINE_HPP diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index c6796d53a3a..455eaaf5873 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -39,6 +39,8 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/orderAccess.inline.hpp" #include "runtime/reflection.hpp" +#include "runtime/thread.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vm_version.hpp" #include "services/threadService.hpp" #include "trace/tracing.hpp" @@ -937,8 +939,12 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) Parker* p = NULL; if (jthread != NULL) { - oop java_thread = JNIHandles::resolve_non_null(jthread); + ThreadsListHandle tlh; + JavaThread* thr = NULL; + oop java_thread = NULL; + (void) tlh.cv_internal_thread_to_JavaThread(jthread, &thr, &java_thread); if (java_thread != NULL) { + // This is a valid oop. jlong lp = java_lang_Thread::park_event(java_thread); if (lp != 0) { // This cast is OK even though the jlong might have been read @@ -946,22 +952,19 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) // always be zero anyway and the value set is always the same p = (Parker*)addr_from_java(lp); } else { - // Grab lock if apparently null or using older version of library - MutexLocker mu(Threads_lock); - java_thread = JNIHandles::resolve_non_null(jthread); - - if (java_thread != NULL) { - JavaThread* thr = java_lang_Thread::thread(java_thread); - if (thr != NULL) { - p = thr->parker(); - if (p != NULL) { // Bind to Java thread for next time. - java_lang_Thread::set_park_event(java_thread, addr_to_java(p)); - } + // Not cached in the java.lang.Thread oop yet (could be an + // older version of library). + if (thr != NULL) { + // The JavaThread is alive. + p = thr->parker(); + if (p != NULL) { + // Cache the Parker in the java.lang.Thread oop for next time. + java_lang_Thread::set_park_event(java_thread, addr_to_java(p)); } } } } - } + } // ThreadsListHandle is destroyed here. if (p != NULL) { HOTSPOT_THREAD_UNPARK((uintptr_t) p); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 84510d0da19..dd3e52d2012 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -55,6 +55,7 @@ #include "runtime/os.hpp" #include "runtime/sweeper.hpp" #include "runtime/thread.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vm_version.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" @@ -665,7 +666,7 @@ class VM_WhiteBoxDeoptimizeFrames : public VM_WhiteBoxOperation { int result() const { return _result; } void doit() { - for (JavaThread* t = Threads::first(); t != NULL; t = t->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { if (t->has_last_Java_frame()) { for (StackFrameStream fst(t, UseBiasedLocking); !fst.is_done(); fst.next()) { frame* f = fst.current(); diff --git a/src/hotspot/share/runtime/biasedLocking.cpp b/src/hotspot/share/runtime/biasedLocking.cpp index de93a4b370a..a1e70a97478 100644 --- a/src/hotspot/share/runtime/biasedLocking.cpp +++ b/src/hotspot/share/runtime/biasedLocking.cpp @@ -32,6 +32,7 @@ #include "runtime/basicLock.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/task.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" @@ -214,12 +215,8 @@ static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_ if (requesting_thread == biased_thread) { thread_is_alive = true; } else { - for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) { - if (cur_thread == biased_thread) { - thread_is_alive = true; - break; - } - } + ThreadsListHandle tlh; + thread_is_alive = tlh.includes(biased_thread); } if (!thread_is_alive) { if (allow_rebias) { @@ -390,72 +387,76 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o, Klass* k_o = o->klass(); Klass* klass = k_o; - if (bulk_rebias) { - // Use the epoch in the klass of the object to implicitly revoke - // all biases of objects of this data type and force them to be - // reacquired. However, we also need to walk the stacks of all - // threads and update the headers of lightweight locked objects - // with biases to have the current epoch. + { + JavaThreadIteratorWithHandle jtiwh; - // If the prototype header doesn't have the bias pattern, don't - // try to update the epoch -- assume another VM operation came in - // and reset the header to the unbiased state, which will - // implicitly cause all existing biases to be revoked - if (klass->prototype_header()->has_bias_pattern()) { - int prev_epoch = klass->prototype_header()->bias_epoch(); - klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch()); - int cur_epoch = klass->prototype_header()->bias_epoch(); + if (bulk_rebias) { + // Use the epoch in the klass of the object to implicitly revoke + // all biases of objects of this data type and force them to be + // reacquired. However, we also need to walk the stacks of all + // threads and update the headers of lightweight locked objects + // with biases to have the current epoch. - // Now walk all threads' stacks and adjust epochs of any biased - // and locked objects of this data type we encounter - for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { + // If the prototype header doesn't have the bias pattern, don't + // try to update the epoch -- assume another VM operation came in + // and reset the header to the unbiased state, which will + // implicitly cause all existing biases to be revoked + if (klass->prototype_header()->has_bias_pattern()) { + int prev_epoch = klass->prototype_header()->bias_epoch(); + klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch()); + int cur_epoch = klass->prototype_header()->bias_epoch(); + + // Now walk all threads' stacks and adjust epochs of any biased + // and locked objects of this data type we encounter + for (; JavaThread *thr = jtiwh.next(); ) { + GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr); + for (int i = 0; i < cached_monitor_info->length(); i++) { + MonitorInfo* mon_info = cached_monitor_info->at(i); + oop owner = mon_info->owner(); + markOop mark = owner->mark(); + if ((owner->klass() == k_o) && mark->has_bias_pattern()) { + // We might have encountered this object already in the case of recursive locking + assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment"); + owner->set_mark(mark->set_bias_epoch(cur_epoch)); + } + } + } + } + + // At this point we're done. All we have to do is potentially + // adjust the header of the given object to revoke its bias. + revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL); + } else { + if (log_is_enabled(Info, biasedlocking)) { + ResourceMark rm; + log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name()); + } + + // Disable biased locking for this data type. Not only will this + // cause future instances to not be biased, but existing biased + // instances will notice that this implicitly caused their biases + // to be revoked. + klass->set_prototype_header(markOopDesc::prototype()); + + // Now walk all threads' stacks and forcibly revoke the biases of + // any locked and biased objects of this data type we encounter. + for (; JavaThread *thr = jtiwh.next(); ) { GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr); for (int i = 0; i < cached_monitor_info->length(); i++) { MonitorInfo* mon_info = cached_monitor_info->at(i); oop owner = mon_info->owner(); markOop mark = owner->mark(); if ((owner->klass() == k_o) && mark->has_bias_pattern()) { - // We might have encountered this object already in the case of recursive locking - assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment"); - owner->set_mark(mark->set_bias_epoch(cur_epoch)); + revoke_bias(owner, false, true, requesting_thread, NULL); } } } + + // Must force the bias of the passed object to be forcibly revoked + // as well to ensure guarantees to callers + revoke_bias(o, false, true, requesting_thread, NULL); } - - // At this point we're done. All we have to do is potentially - // adjust the header of the given object to revoke its bias. - revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL); - } else { - if (log_is_enabled(Info, biasedlocking)) { - ResourceMark rm; - log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name()); - } - - // Disable biased locking for this data type. Not only will this - // cause future instances to not be biased, but existing biased - // instances will notice that this implicitly caused their biases - // to be revoked. - klass->set_prototype_header(markOopDesc::prototype()); - - // Now walk all threads' stacks and forcibly revoke the biases of - // any locked and biased objects of this data type we encounter. - for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { - GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr); - for (int i = 0; i < cached_monitor_info->length(); i++) { - MonitorInfo* mon_info = cached_monitor_info->at(i); - oop owner = mon_info->owner(); - markOop mark = owner->mark(); - if ((owner->klass() == k_o) && mark->has_bias_pattern()) { - revoke_bias(owner, false, true, requesting_thread, NULL); - } - } - } - - // Must force the bias of the passed object to be forcibly revoked - // as well to ensure guarantees to callers - revoke_bias(o, false, true, requesting_thread, NULL); - } + } // ThreadsListHandle is destroyed here. log_info(biasedlocking)("* Ending bulk revocation"); @@ -481,7 +482,7 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o, static void clean_up_cached_monitor_info() { // Walk the thread list clearing out the cached monitors - for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { thr->set_cached_monitor_info(NULL); } } @@ -768,7 +769,7 @@ void BiasedLocking::preserve_marks() { ResourceMark rm; Thread* cur = Thread::current(); - for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { if (thread->has_last_Java_frame()) { RegisterMap rm(thread); for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index fb93fbdd712..c61f41d2933 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -50,6 +50,7 @@ #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" #include "runtime/thread.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vframeArray.hpp" #include "runtime/vframe_hp.hpp" @@ -1297,7 +1298,7 @@ void Deoptimization::revoke_biases_of_monitors(CodeBlob* cb) { assert(SafepointSynchronize::is_at_safepoint(), "must only be called from safepoint"); GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>(); - for (JavaThread* jt = Threads::first(); jt != NULL ; jt = jt->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { if (jt->has_last_Java_frame()) { StackFrameStream sfs(jt, true); while (!sfs.is_done()) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index bdf69c29da6..90a8b0c78b6 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2484,6 +2484,12 @@ public: LP64_ONLY(range(-1, max_intx/MICROUNITS)) \ NOT_LP64(range(-1, max_intx)) \ \ + diagnostic(bool, EnableThreadSMRExtraValidityChecks, true, \ + "Enable Thread SMR extra validity checks") \ + \ + diagnostic(bool, EnableThreadSMRStatistics, true, \ + "Enable Thread SMR Statistics") \ + \ product(bool, Inline, true, \ "Enable inlining") \ \ diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index f81c235c13d..0b816b25ed3 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -37,8 +37,6 @@ #include "utilities/formatBuffer.hpp" #include "utilities/preserveException.hpp" -#define ALL_JAVA_THREADS(X) for (JavaThread* X = Threads::first(); X; X = X->next()) - class HandshakeOperation: public StackObj { public: virtual void do_handshake(JavaThread* thread) = 0; @@ -94,8 +92,7 @@ bool VM_Handshake::handshake_has_timed_out(jlong start_time) { void VM_Handshake::handle_timeout() { LogStreamHandle(Warning, handshake) log_stream; - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - ALL_JAVA_THREADS(thr) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { if (thr->has_handshake()) { log_stream.print("Thread " PTR_FORMAT " has not cleared its handshake op", p2i(thr)); thr->print_thread_state_on(&log_stream); @@ -117,8 +114,8 @@ class VM_HandshakeOneThread: public VM_Handshake { TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake)); { - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - if (Threads::includes(_target)) { + ThreadsListHandle tlh; + if (tlh.includes(_target)) { set_handshake(_target); _thread_alive = true; } @@ -139,9 +136,24 @@ class VM_HandshakeOneThread: public VM_Handshake { handle_timeout(); } + // We need to re-think this with SMR ThreadsList. + // There is an assumption in the code that the Threads_lock should be + // locked during certain phases. MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - _target->handshake_process_by_vmthread(); - + ThreadsListHandle tlh; + if (tlh.includes(_target)) { + // Warning _target's address might be re-used. + // handshake_process_by_vmthread will check the semaphore for us again. + // Since we can't have more then one handshake in flight a reuse of + // _target's address should be okay since the new thread will not have + // an operation. + _target->handshake_process_by_vmthread(); + } else { + // We can't warn here since the thread does cancel_handshake after + // it has been removed from the ThreadsList. So we should just keep + // looping here until while below returns false. If we have a bug, + // then we hang here, which is good for debugging. + } } while (!poll_for_completed_thread()); } @@ -157,15 +169,15 @@ class VM_HandshakeAllThreads: public VM_Handshake { void doit() { TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake)); - int number_of_threads_issued = -1; - int number_of_threads_completed = 0; - { - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - number_of_threads_issued = Threads::number_of_threads(); + int number_of_threads_issued = 0; + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { + set_handshake(thr); + number_of_threads_issued++; + } - ALL_JAVA_THREADS(thr) { - set_handshake(thr); - } + if (number_of_threads_issued < 1) { + log_debug(handshake)("No threads to handshake."); + return; } if (!UseMembar) { @@ -174,6 +186,7 @@ class VM_HandshakeAllThreads: public VM_Handshake { log_debug(handshake)("Threads signaled, begin processing blocked threads by VMThtread"); const jlong start_time = os::elapsed_counter(); + int number_of_threads_completed = 0; do { // Check if handshake operation has timed out if (handshake_has_timed_out(start_time)) { @@ -184,13 +197,19 @@ class VM_HandshakeAllThreads: public VM_Handshake { // Observing a blocked state may of course be transient but the processing is guarded // by semaphores and we optimistically begin by working on the blocked threads { + // We need to re-think this with SMR ThreadsList. + // There is an assumption in the code that the Threads_lock should + // be locked during certain phases. MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - ALL_JAVA_THREADS(thr) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { + // A new thread on the ThreadsList will not have an operation, + // hence it is skipped in handshake_process_by_vmthread. thr->handshake_process_by_vmthread(); } } while (poll_for_completed_thread()) { + // Includes canceled operations by exiting threads. number_of_threads_completed++; } @@ -212,7 +231,7 @@ public: _thread_cl(cl), _target_thread(target), _all_threads(false), _thread_alive(false) {} void doit() { - ALL_JAVA_THREADS(t) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) { if (_all_threads || t == _target_thread) { if (t == _target_thread) { _thread_alive = true; @@ -298,8 +317,8 @@ void HandshakeState::cancel_inner(JavaThread* thread) { assert(thread->thread_state() == _thread_in_vm, "must be in vm state"); #ifdef DEBUG { - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - assert(!Threads::includes(thread), "java thread must not be on threads list"); + ThreadsListHandle tlh; + assert(!tlh.includes(_target), "java thread must not be on threads list"); } #endif HandshakeOperation* op = _operation; diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index 11d84f00ee8..9c19095f142 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -356,6 +356,8 @@ void print_statistics() { if (PrintNMTStatistics) { MemTracker::final_report(tty); } + + Threads::log_smr_statistics(); } #else // PRODUCT MODE STATISTICS @@ -396,6 +398,8 @@ void print_statistics() { if (LogTouchedMethods && PrintTouchedMethodsAtExit) { Method::print_touched_methods(tty); } + + Threads::log_smr_statistics(); } #endif diff --git a/src/hotspot/share/runtime/memprofiler.cpp b/src/hotspot/share/runtime/memprofiler.cpp index 77a1f183daa..396285ac8c8 100644 --- a/src/hotspot/share/runtime/memprofiler.cpp +++ b/src/hotspot/share/runtime/memprofiler.cpp @@ -36,6 +36,7 @@ #include "runtime/os.hpp" #include "runtime/task.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" #ifndef PRODUCT @@ -51,8 +52,6 @@ class MemProfilerTask : public PeriodicTask { void MemProfilerTask::task() { - // Get thread lock to provide mutual exclusion, and so we can iterate safely over the thread list. - MutexLocker mu(Threads_lock); MemProfiler::do_trace(); } @@ -109,20 +108,21 @@ void MemProfiler::do_trace() { // Calculate thread local sizes size_t handles_memory_usage = VMThread::vm_thread()->handle_area()->size_in_bytes(); size_t resource_memory_usage = VMThread::vm_thread()->resource_area()->size_in_bytes(); - JavaThread *cur = Threads::first(); - while (cur != NULL) { - handles_memory_usage += cur->handle_area()->size_in_bytes(); - resource_memory_usage += cur->resource_area()->size_in_bytes(); - cur = cur->next(); - } + { + JavaThreadIteratorWithHandle jtiwh; + for (; JavaThread *cur = jtiwh.next(); ) { + handles_memory_usage += cur->handle_area()->size_in_bytes(); + resource_memory_usage += cur->resource_area()->size_in_bytes(); + } - // Print trace line in log - fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",", - os::elapsedTime(), - Threads::number_of_threads(), - InstanceKlass::number_of_instance_classes(), - Universe::heap()->used() / K, - Universe::heap()->capacity() / K); + // Print trace line in log + fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",", + os::elapsedTime(), + jtiwh.length(), + InstanceKlass::number_of_instance_classes(), + Universe::heap()->used() / K, + Universe::heap()->capacity() / K); + } fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 0306faa5515..58ce94f6fa1 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -54,6 +54,7 @@ #include "runtime/os.inline.hpp" #include "runtime/stubRoutines.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vm_version.hpp" #include "services/attachListener.hpp" #include "services/mallocTracker.hpp" @@ -197,15 +198,7 @@ char* os::iso8601_time(char* buffer, size_t buffer_length, bool utc) { } OSReturn os::set_priority(Thread* thread, ThreadPriority p) { -#ifdef ASSERT - if (!(!thread->is_Java_thread() || - Thread::current() == thread || - Threads_lock->owned_by_self() - || thread->is_Compiler_thread() - )) { - assert(false, "possibility of dangling Thread pointer"); - } -#endif + debug_only(Thread::check_for_dangling_thread_pointer(thread);) if (p >= MinPriority && p <= MaxPriority) { int priority = java_to_os_priority[p]; @@ -1100,7 +1093,7 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { } #endif - for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { // Check for privilege stack if (thread->privileged_stack_top() != NULL && thread->privileged_stack_top()->contains(addr)) { @@ -1126,7 +1119,6 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { if (verbose) thread->print_on(st); return; } - } // Check if in metaspace and print types that have vptrs (only method now) @@ -1665,7 +1657,6 @@ void os::initialize_initial_active_processor_count() { } void os::SuspendedThreadTask::run() { - assert(Threads_lock->owned_by_self() || (_thread == VMThread::vm_thread()), "must have threads lock to call this"); internal_do_task(); _done = true; } diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index 291ee50b170..23d56c8e0d5 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -59,6 +59,7 @@ #include "runtime/sweeper.hpp" #include "runtime/synchronizer.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/timerTrace.hpp" #include "services/runtimeService.hpp" #include "trace/tracing.hpp" @@ -174,7 +175,7 @@ void SafepointSynchronize::begin() { if (SafepointMechanism::uses_thread_local_poll()) { // Arming the per thread poll while having _state != _not_synchronized means safepointing log_trace(safepoint)("Setting thread local yield flag for threads"); - for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) { // Make sure the threads start polling, it is time to yield. SafepointMechanism::arm_local_poll(cur); // release store, global state -> local state } @@ -200,133 +201,137 @@ void SafepointSynchronize::begin() { // Consider using active_processor_count() ... but that call is expensive. int ncpus = os::processor_count() ; + unsigned int iterations = 0; + { + JavaThreadIteratorWithHandle jtiwh; #ifdef ASSERT - for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { - assert(cur->safepoint_state()->is_running(), "Illegal initial state"); - // Clear the visited flag to ensure that the critical counts are collected properly. - cur->set_visited_for_critical_count(false); - } + for (; JavaThread *cur = jtiwh.next(); ) { + assert(cur->safepoint_state()->is_running(), "Illegal initial state"); + // Clear the visited flag to ensure that the critical counts are collected properly. + cur->set_visited_for_critical_count(false); + } #endif // ASSERT - if (SafepointTimeout) - safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS; + if (SafepointTimeout) + safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS; - // Iterate through all threads until it have been determined how to stop them all at a safepoint - unsigned int iterations = 0; - int steps = 0 ; - while(still_running > 0) { - for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { - assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended"); - ThreadSafepointState *cur_state = cur->safepoint_state(); - if (cur_state->is_running()) { - cur_state->examine_state_of_thread(); - if (!cur_state->is_running()) { - still_running--; - // consider adjusting steps downward: - // steps = 0 - // steps -= NNN - // steps >>= 1 - // steps = MIN(steps, 2000-100) - // if (iterations != 0) steps -= NNN - } - LogTarget(Trace, safepoint) lt; - if (lt.is_enabled()) { - ResourceMark rm; - LogStream ls(lt); - cur_state->print_on(&ls); + // Iterate through all threads until it have been determined how to stop them all at a safepoint + int steps = 0 ; + while(still_running > 0) { + jtiwh.rewind(); + for (; JavaThread *cur = jtiwh.next(); ) { + assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended"); + ThreadSafepointState *cur_state = cur->safepoint_state(); + if (cur_state->is_running()) { + cur_state->examine_state_of_thread(); + if (!cur_state->is_running()) { + still_running--; + // consider adjusting steps downward: + // steps = 0 + // steps -= NNN + // steps >>= 1 + // steps = MIN(steps, 2000-100) + // if (iterations != 0) steps -= NNN + } + LogTarget(Trace, safepoint) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + cur_state->print_on(&ls); + } } } - } - if (iterations == 0) { - initial_running = still_running; - if (PrintSafepointStatistics) { - begin_statistics(nof_threads, still_running); - } - } - - if (still_running > 0) { - // Check for if it takes to long - if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) { - print_safepoint_timeout(_spinning_timeout); + if (iterations == 0) { + initial_running = still_running; + if (PrintSafepointStatistics) { + begin_statistics(nof_threads, still_running); + } } - // Spin to avoid context switching. - // There's a tension between allowing the mutators to run (and rendezvous) - // vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that - // a mutator might otherwise use profitably to reach a safepoint. Excessive - // spinning by the VM thread on a saturated system can increase rendezvous latency. - // Blocking or yielding incur their own penalties in the form of context switching - // and the resultant loss of $ residency. - // - // Further complicating matters is that yield() does not work as naively expected - // on many platforms -- yield() does not guarantee that any other ready threads - // will run. As such we revert to naked_short_sleep() after some number of iterations. - // nakes_short_sleep() is implemented as a short unconditional sleep. - // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping - // can actually increase the time it takes the VM thread to detect that a system-wide - // stop-the-world safepoint has been reached. In a pathological scenario such as that - // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe. - // In that case the mutators will be stalled waiting for the safepoint to complete and the - // the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread - // will eventually wake up and detect that all mutators are safe, at which point - // we'll again make progress. - // - // Beware too that that the VMThread typically runs at elevated priority. - // Its default priority is higher than the default mutator priority. - // Obviously, this complicates spinning. - // - // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0). - // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will. - // - // See the comments in synchronizer.cpp for additional remarks on spinning. - // - // In the future we might: - // 1. Modify the safepoint scheme to avoid potentially unbounded spinning. - // This is tricky as the path used by a thread exiting the JVM (say on - // on JNI call-out) simply stores into its state field. The burden - // is placed on the VM thread, which must poll (spin). - // 2. Find something useful to do while spinning. If the safepoint is GC-related - // we might aggressively scan the stacks of threads that are already safe. - // 3. Use Solaris schedctl to examine the state of the still-running mutators. - // If all the mutators are ONPROC there's no reason to sleep or yield. - // 4. YieldTo() any still-running mutators that are ready but OFFPROC. - // 5. Check system saturation. If the system is not fully saturated then - // simply spin and avoid sleep/yield. - // 6. As still-running mutators rendezvous they could unpark the sleeping - // VMthread. This works well for still-running mutators that become - // safe. The VMthread must still poll for mutators that call-out. - // 7. Drive the policy on time-since-begin instead of iterations. - // 8. Consider making the spin duration a function of the # of CPUs: - // Spin = (((ncpus-1) * M) + K) + F(still_running) - // Alternately, instead of counting iterations of the outer loop - // we could count the # of threads visited in the inner loop, above. - // 9. On windows consider using the return value from SwitchThreadTo() - // to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions. - - if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) { - guarantee (PageArmed == 0, "invariant") ; - PageArmed = 1 ; - os::make_polling_page_unreadable(); - } - - // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or - // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus) - ++steps ; - if (ncpus > 1 && steps < SafepointSpinBeforeYield) { - SpinPause() ; // MP-Polite spin - } else - if (steps < DeferThrSuspendLoopCount) { - os::naked_yield() ; - } else { - os::naked_short_sleep(1); + if (still_running > 0) { + // Check for if it takes to long + if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) { + print_safepoint_timeout(_spinning_timeout); } - iterations ++ ; + // Spin to avoid context switching. + // There's a tension between allowing the mutators to run (and rendezvous) + // vs spinning. As the VM thread spins, wasting cycles, it consumes CPU that + // a mutator might otherwise use profitably to reach a safepoint. Excessive + // spinning by the VM thread on a saturated system can increase rendezvous latency. + // Blocking or yielding incur their own penalties in the form of context switching + // and the resultant loss of $ residency. + // + // Further complicating matters is that yield() does not work as naively expected + // on many platforms -- yield() does not guarantee that any other ready threads + // will run. As such we revert to naked_short_sleep() after some number of iterations. + // nakes_short_sleep() is implemented as a short unconditional sleep. + // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping + // can actually increase the time it takes the VM thread to detect that a system-wide + // stop-the-world safepoint has been reached. In a pathological scenario such as that + // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe. + // In that case the mutators will be stalled waiting for the safepoint to complete and the + // the VMthread will be sleeping, waiting for the mutators to rendezvous. The VMthread + // will eventually wake up and detect that all mutators are safe, at which point + // we'll again make progress. + // + // Beware too that that the VMThread typically runs at elevated priority. + // Its default priority is higher than the default mutator priority. + // Obviously, this complicates spinning. + // + // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0). + // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will. + // + // See the comments in synchronizer.cpp for additional remarks on spinning. + // + // In the future we might: + // 1. Modify the safepoint scheme to avoid potentially unbounded spinning. + // This is tricky as the path used by a thread exiting the JVM (say on + // on JNI call-out) simply stores into its state field. The burden + // is placed on the VM thread, which must poll (spin). + // 2. Find something useful to do while spinning. If the safepoint is GC-related + // we might aggressively scan the stacks of threads that are already safe. + // 3. Use Solaris schedctl to examine the state of the still-running mutators. + // If all the mutators are ONPROC there's no reason to sleep or yield. + // 4. YieldTo() any still-running mutators that are ready but OFFPROC. + // 5. Check system saturation. If the system is not fully saturated then + // simply spin and avoid sleep/yield. + // 6. As still-running mutators rendezvous they could unpark the sleeping + // VMthread. This works well for still-running mutators that become + // safe. The VMthread must still poll for mutators that call-out. + // 7. Drive the policy on time-since-begin instead of iterations. + // 8. Consider making the spin duration a function of the # of CPUs: + // Spin = (((ncpus-1) * M) + K) + F(still_running) + // Alternately, instead of counting iterations of the outer loop + // we could count the # of threads visited in the inner loop, above. + // 9. On windows consider using the return value from SwitchThreadTo() + // to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions. + + if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) { + guarantee (PageArmed == 0, "invariant") ; + PageArmed = 1 ; + os::make_polling_page_unreadable(); + } + + // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or + // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus) + ++steps ; + if (ncpus > 1 && steps < SafepointSpinBeforeYield) { + SpinPause() ; // MP-Polite spin + } else + if (steps < DeferThrSuspendLoopCount) { + os::naked_yield() ; + } else { + os::naked_short_sleep(1); + } + + iterations ++ ; + } + assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long"); } - assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long"); - } + } // ThreadsListHandle destroyed here. assert(still_running == 0, "sanity check"); if (PrintSafepointStatistics) { @@ -341,7 +346,7 @@ void SafepointSynchronize::begin() { sync_event.set_iterations(iterations); sync_event.commit(); } - } //EventSafepointStateSync + } // EventSafepointStateSynchronization destroyed here. // wait until all threads are stopped { @@ -393,8 +398,8 @@ void SafepointSynchronize::begin() { } // EventSafepointWaitBlocked #ifdef ASSERT - for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) { - // make sure all the threads were visited + // Make sure all the threads were visited. + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) { assert(cur->was_visited_for_critical_count(), "missed a thread"); } #endif // ASSERT @@ -452,81 +457,86 @@ void SafepointSynchronize::end() { end_statistics(os::javaTimeNanos()); } + { + JavaThreadIteratorWithHandle jtiwh; #ifdef ASSERT - // A pending_exception cannot be installed during a safepoint. The threads - // may install an async exception after they come back from a safepoint into - // pending_exception after they unblock. But that should happen later. - for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) { - assert (!(cur->has_pending_exception() && - cur->safepoint_state()->is_at_poll_safepoint()), - "safepoint installed a pending exception"); - } + // A pending_exception cannot be installed during a safepoint. The threads + // may install an async exception after they come back from a safepoint into + // pending_exception after they unblock. But that should happen later. + for (; JavaThread *cur = jtiwh.next(); ) { + assert (!(cur->has_pending_exception() && + cur->safepoint_state()->is_at_poll_safepoint()), + "safepoint installed a pending exception"); + } #endif // ASSERT - if (PageArmed) { - assert(SafepointMechanism::uses_global_page_poll(), "sanity"); - // Make polling safepoint aware - os::make_polling_page_readable(); - PageArmed = 0 ; - } - - if (SafepointMechanism::uses_global_page_poll()) { - // Remove safepoint check from interpreter - Interpreter::ignore_safepoints(); - } - - { - MutexLocker mu(Safepoint_lock); - - assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization"); - - if (SafepointMechanism::uses_thread_local_poll()) { - _state = _not_synchronized; - OrderAccess::storestore(); // global state -> local state - for (JavaThread *current = Threads::first(); current; current = current->next()) { - ThreadSafepointState* cur_state = current->safepoint_state(); - cur_state->restart(); // TSS _running - SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page - } - log_debug(safepoint)("Leaving safepoint region"); - } else { - // Set to not synchronized, so the threads will not go into the signal_thread_blocked method - // when they get restarted. - _state = _not_synchronized; - OrderAccess::fence(); - - log_debug(safepoint)("Leaving safepoint region"); - - // Start suspended threads - for (JavaThread *current = Threads::first(); current; current = current->next()) { - // A problem occurring on Solaris is when attempting to restart threads - // the first #cpus - 1 go well, but then the VMThread is preempted when we get - // to the next one (since it has been running the longest). We then have - // to wait for a cpu to become available before we can continue restarting - // threads. - // FIXME: This causes the performance of the VM to degrade when active and with - // large numbers of threads. Apparently this is due to the synchronous nature - // of suspending threads. - // - // TODO-FIXME: the comments above are vestigial and no longer apply. - // Furthermore, using solaris' schedctl in this particular context confers no benefit - if (VMThreadHintNoPreempt) { - os::hint_no_preempt(); - } - ThreadSafepointState* cur_state = current->safepoint_state(); - assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint"); - cur_state->restart(); - assert(cur_state->is_running(), "safepoint state has not been reset"); - } + if (PageArmed) { + assert(SafepointMechanism::uses_global_page_poll(), "sanity"); + // Make polling safepoint aware + os::make_polling_page_readable(); + PageArmed = 0 ; } - RuntimeService::record_safepoint_end(); + if (SafepointMechanism::uses_global_page_poll()) { + // Remove safepoint check from interpreter + Interpreter::ignore_safepoints(); + } - // Release threads lock, so threads can be created/destroyed again. It will also starts all threads - // blocked in signal_thread_blocked - Threads_lock->unlock(); + { + MutexLocker mu(Safepoint_lock); + + assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization"); + + if (SafepointMechanism::uses_thread_local_poll()) { + _state = _not_synchronized; + OrderAccess::storestore(); // global state -> local state + jtiwh.rewind(); + for (; JavaThread *current = jtiwh.next(); ) { + ThreadSafepointState* cur_state = current->safepoint_state(); + cur_state->restart(); // TSS _running + SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page + } + log_debug(safepoint)("Leaving safepoint region"); + } else { + // Set to not synchronized, so the threads will not go into the signal_thread_blocked method + // when they get restarted. + _state = _not_synchronized; + OrderAccess::fence(); + + log_debug(safepoint)("Leaving safepoint region"); + + // Start suspended threads + jtiwh.rewind(); + for (; JavaThread *current = jtiwh.next(); ) { + // A problem occurring on Solaris is when attempting to restart threads + // the first #cpus - 1 go well, but then the VMThread is preempted when we get + // to the next one (since it has been running the longest). We then have + // to wait for a cpu to become available before we can continue restarting + // threads. + // FIXME: This causes the performance of the VM to degrade when active and with + // large numbers of threads. Apparently this is due to the synchronous nature + // of suspending threads. + // + // TODO-FIXME: the comments above are vestigial and no longer apply. + // Furthermore, using solaris' schedctl in this particular context confers no benefit + if (VMThreadHintNoPreempt) { + os::hint_no_preempt(); + } + ThreadSafepointState* cur_state = current->safepoint_state(); + assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint"); + cur_state->restart(); + assert(cur_state->is_running(), "safepoint state has not been reset"); + } + } + + RuntimeService::record_safepoint_end(); + + // Release threads lock, so threads can be created/destroyed again. + // It will also release all threads blocked in signal_thread_blocked. + Threads_lock->unlock(); + } + } // ThreadsListHandle destroyed here. - } Universe::heap()->safepoint_synchronize_end(); // record this time so VMThread can keep track how much time has elapsed // since last safepoint. @@ -915,12 +925,11 @@ void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason tty->print_cr("# SafepointSynchronize::begin: Threads which did not reach the safepoint:"); ThreadSafepointState *cur_state; ResourceMark rm; - for (JavaThread *cur_thread = Threads::first(); cur_thread; - cur_thread = cur_thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) { cur_state = cur_thread->safepoint_state(); if (cur_thread->thread_state() != _thread_blocked && - ((reason == _spinning_timeout && cur_state->is_running()) || + ((reason == _spinning_timeout && cur_state->is_running()) || (reason == _blocking_timeout && !cur_state->has_called_back()))) { tty->print("# "); cur_thread->print(); @@ -1427,7 +1436,7 @@ void SafepointSynchronize::print_state() { tty->print_cr("State: %s", (_state == _synchronizing) ? "synchronizing" : "synchronized"); - for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) { cur->safepoint_state()->print(); } } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index d54b0d8b30a..6b8898959c8 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -894,7 +894,7 @@ ObjectSynchronizer::LockOwnership ObjectSynchronizer::query_lock_ownership } // FIXME: jvmti should call this -JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) { +JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_obj) { if (UseBiasedLocking) { if (SafepointSynchronize::is_at_safepoint()) { BiasedLocking::revoke_at_safepoint(h_obj); @@ -923,7 +923,7 @@ JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) { if (owner != NULL) { // owning_thread_from_monitor_owner() may also return NULL here - return Threads::owning_thread_from_monitor_owner(owner, doLock); + return Threads::owning_thread_from_monitor_owner(t_list, owner); } // Unlocked case, header in place diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index ba3ac60a267..415c222ab6f 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -32,6 +32,7 @@ #include "runtime/perfData.hpp" class ObjectMonitor; +class ThreadsList; struct DeflateMonitorCounters { int nInuse; // currently associated with objects @@ -125,7 +126,7 @@ class ObjectSynchronizer : AllStatic { static bool current_thread_holds_lock(JavaThread* thread, Handle h_obj); static LockOwnership query_lock_ownership(JavaThread * self, Handle h_obj); - static JavaThread* get_lock_owner(Handle h_obj, bool doLock); + static JavaThread* get_lock_owner(ThreadsList * t_list, Handle h_obj); // JNI detach support static void release_monitors_owned_by_thread(TRAPS); diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 48c757fbd83..097f453854b 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -71,12 +71,12 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/jniPeriodicChecker.hpp" -#include "runtime/timerTrace.hpp" #include "runtime/memprofiler.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/orderAccess.inline.hpp" #include "runtime/osThread.hpp" +#include "runtime/prefetch.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/safepointMechanism.inline.hpp" #include "runtime/sharedRuntime.hpp" @@ -86,6 +86,9 @@ #include "runtime/task.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadCritical.hpp" +#include "runtime/threadSMR.inline.hpp" +#include "runtime/timer.hpp" +#include "runtime/timerTrace.hpp" #include "runtime/vframe.hpp" #include "runtime/vframeArray.hpp" #include "runtime/vframe_hp.hpp" @@ -104,6 +107,7 @@ #include "utilities/events.hpp" #include "utilities/macros.hpp" #include "utilities/preserveException.hpp" +#include "utilities/resourceHash.hpp" #include "utilities/vmError.hpp" #if INCLUDE_ALL_GCS #include "gc/cms/concurrentMarkSweepThread.hpp" @@ -195,13 +199,19 @@ void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) { void Thread::operator delete(void* p) { if (UseBiasedLocking) { - void* real_malloc_addr = ((Thread*) p)->_real_malloc_address; - FreeHeap(real_malloc_addr); + FreeHeap(((Thread*) p)->_real_malloc_address); } else { FreeHeap(p); } } +void JavaThread::smr_delete() { + if (_on_thread_list) { + Threads::smr_delete(this); + } else { + delete this; + } +} // Base class for all threads: VMThread, WatcherThread, ConcurrentMarkSweepThread, // JavaThread @@ -227,6 +237,9 @@ Thread::Thread() { // This initial value ==> never claimed. _oops_do_parity = 0; + _threads_hazard_ptr = NULL; + _nested_threads_hazard_ptr = NULL; + _nested_threads_hazard_ptr_cnt = 0; // the handle mark links itself to last_handle_mark new HandleMark(this); @@ -398,9 +411,15 @@ void Thread::run() { } #ifdef ASSERT -// Private method to check for dangling thread pointer -void check_for_dangling_thread_pointer(Thread *thread) { - assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(), +// A JavaThread is considered "dangling" if it is not the current +// thread, has been added the Threads list, the system is not at a +// safepoint and the Thread is not "protected". +// +void Thread::check_for_dangling_thread_pointer(Thread *thread) { + assert(!thread->is_Java_thread() || Thread::current() == thread || + !((JavaThread *) thread)->on_thread_list() || + SafepointSynchronize::is_at_safepoint() || + Threads::is_a_protected_JavaThread_with_lock((JavaThread *) thread), "possibility of dangling Thread pointer"); } #endif @@ -732,6 +751,37 @@ bool JavaThread::wait_for_ext_suspend_completion(int retries, int delay, return false; } +// Called from API entry points which perform stack walking. If the +// associated JavaThread is the current thread, then wait_for_suspend +// is not used. Otherwise, it determines if we should wait for the +// "other" thread to complete external suspension. (NOTE: in future +// releases the suspension mechanism should be reimplemented so this +// is not necessary.) +// +bool +JavaThread::is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits) { + if (this != JavaThread::current()) { + // "other" threads require special handling. + if (wait_for_suspend) { + // We are allowed to wait for the external suspend to complete + // so give the other thread a chance to get suspended. + if (!wait_for_ext_suspend_completion(SuspendRetryCount, + SuspendRetryDelay, bits)) { + // Didn't make it so let the caller know. + return false; + } + } + // We aren't allowed to wait for the external suspend to complete + // so if the other thread isn't externally suspended we need to + // let the caller know. + else if (!is_ext_suspend_completed_with_lock(bits)) { + return false; + } + } + + return true; +} + #ifndef PRODUCT void JavaThread::record_jump(address target, address instr, const char* file, int line) { @@ -810,9 +860,33 @@ void Thread::print_on(outputStream* st) const { ext().print_on(st); osthread()->print_on(st); } + if (_threads_hazard_ptr != NULL) { + st->print("_threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr)); + } + if (_nested_threads_hazard_ptr != NULL) { + print_nested_threads_hazard_ptrs_on(st); + } + st->print(" "); debug_only(if (WizardMode) print_owned_locks_on(st);) } +void Thread::print_nested_threads_hazard_ptrs_on(outputStream* st) const { + assert(_nested_threads_hazard_ptr != NULL, "must be set to print"); + + if (EnableThreadSMRStatistics) { + st->print(", _nested_threads_hazard_ptr_cnt=%u", _nested_threads_hazard_ptr_cnt); + } + st->print(", _nested_threads_hazard_ptrs="); + for (NestedThreadsList* node = _nested_threads_hazard_ptr; node != NULL; + node = node->next()) { + if (node != _nested_threads_hazard_ptr) { + // First node does not need a comma-space separator. + st->print(", "); + } + st->print(INTPTR_FORMAT, p2i(node->t_list())); + } +} + // Thread::print_on_error() is called by fatal error handler. Don't use // any lock or allocate memory. void Thread::print_on_error(outputStream* st, char* buf, int buflen) const { @@ -834,6 +908,13 @@ void Thread::print_on_error(outputStream* st, char* buf, int buflen) const { if (osthread()) { st->print(" [id=%d]", osthread()->thread_id()); } + + if (_threads_hazard_ptr != NULL) { + st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr)); + } + if (_nested_threads_hazard_ptr != NULL) { + print_nested_threads_hazard_ptrs_on(st); + } } void Thread::print_value_on(outputStream* st) const { @@ -871,8 +952,8 @@ bool Thread::owns_locks_but_compiled_lock() const { #ifndef PRODUCT -// The flag: potential_vm_operation notifies if this particular safepoint state could potential -// invoke the vm-thread (i.e., and oop allocation). In that case, we also have to make sure that +// The flag: potential_vm_operation notifies if this particular safepoint state could potentially +// invoke the vm-thread (e.g., an oop allocation). In that case, we also have to make sure that // no threads which allow_vm_block's are held void Thread::check_for_valid_safepoint_state(bool potential_vm_operation) { // Check if current thread is allowed to block at a safepoint @@ -1399,10 +1480,11 @@ bool jvmci_counters_include(JavaThread* thread) { void JavaThread::collect_counters(typeArrayOop array) { if (JVMCICounterSize > 0) { MutexLocker tl(Threads_lock); + JavaThreadIteratorWithHandle jtiwh; for (int i = 0; i < array->length(); i++) { array->long_at_put(i, _jvmci_old_thread_counters[i]); } - for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) { + for (; JavaThread *tp = jtiwh.next(); ) { if (jvmci_counters_include(tp)) { for (int i = 0; i < array->length(); i++) { array->long_at_put(i, array->long_at(i) + tp->_jvmci_counters[i]); @@ -1435,6 +1517,7 @@ void JavaThread::initialize() { clear_must_deopt_id(); set_monitor_chunks(NULL); set_next(NULL); + _on_thread_list = false; set_thread_state(_thread_new); _terminated = _not_terminated; _privileged_stack_top = NULL; @@ -1715,12 +1798,12 @@ void JavaThread::thread_main_inner() { DTRACE_THREAD_PROBE(stop, this); this->exit(false); - delete this; + this->smr_delete(); } static void ensure_join(JavaThread* thread) { - // We do not need to grap the Threads_lock, since we are operating on ourself. + // We do not need to grab the Threads_lock, since we are operating on ourself. Handle threadObj(thread, thread->threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); @@ -1742,6 +1825,15 @@ static void ensure_join(JavaThread* thread) { void JavaThread::exit(bool destroy_vm, ExitType exit_type) { assert(this == JavaThread::current(), "thread consistency check"); + elapsedTimer _timer_exit_phase1; + elapsedTimer _timer_exit_phase2; + elapsedTimer _timer_exit_phase3; + elapsedTimer _timer_exit_phase4; + + if (log_is_enabled(Debug, os, thread, timer)) { + _timer_exit_phase1.start(); + } + HandleMark hm(this); Handle uncaught_exception(this, this->pending_exception()); this->clear_pending_exception(); @@ -1841,12 +1933,20 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) { // before_exit() has already posted JVMTI THREAD_END events } + if (log_is_enabled(Debug, os, thread, timer)) { + _timer_exit_phase1.stop(); + _timer_exit_phase2.start(); + } // Notify waiters on thread object. This has to be done after exit() is called // on the thread (if the thread is the last thread in a daemon ThreadGroup the // group should have the destroyed bit set before waiters are notified). ensure_join(this); assert(!this->has_pending_exception(), "ensure_join should have cleared"); + if (log_is_enabled(Debug, os, thread, timer)) { + _timer_exit_phase2.stop(); + _timer_exit_phase3.start(); + } // 6282335 JNI DetachCurrentThread spec states that all Java monitors // held by this thread must be released. The spec does not distinguish // between JNI-acquired and regular Java monitors. We can only see @@ -1914,12 +2014,26 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) { exit_type == JavaThread::normal_exit ? "exiting" : "detaching", os::current_thread_id()); + if (log_is_enabled(Debug, os, thread, timer)) { + _timer_exit_phase3.stop(); + _timer_exit_phase4.start(); + } // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread Threads::remove(this); - // If someone set a handshake on us just as we entered exit path, we simple cancel it. - if (ThreadLocalHandshakes) { - cancel_handshake(); + if (log_is_enabled(Debug, os, thread, timer)) { + _timer_exit_phase4.stop(); + ResourceMark rm(this); + log_debug(os, thread, timer)("name='%s'" + ", exit-phase1=" JLONG_FORMAT + ", exit-phase2=" JLONG_FORMAT + ", exit-phase3=" JLONG_FORMAT + ", exit-phase4=" JLONG_FORMAT, + get_thread_name(), + _timer_exit_phase1.milliseconds(), + _timer_exit_phase2.milliseconds(), + _timer_exit_phase3.milliseconds(), + _timer_exit_phase4.milliseconds()); } } @@ -1980,7 +2094,7 @@ void JavaThread::cleanup_failed_attach_current_thread() { #endif // INCLUDE_ALL_GCS Threads::remove(this); - delete this; + this->smr_delete(); } @@ -2235,10 +2349,9 @@ void JavaThread::send_thread_stop(oop java_throwable) { // + Target thread will not enter any new monitors // void JavaThread::java_suspend() { - { MutexLocker mu(Threads_lock); - if (!Threads::includes(this) || is_exiting() || this->threadObj() == NULL) { - return; - } + ThreadsListHandle tlh; + if (!tlh.includes(this) || threadObj() == NULL || is_exiting()) { + return; } { MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag); @@ -2327,14 +2440,8 @@ int JavaThread::java_suspend_self() { // verify the JavaThread has not yet been published in the Threads::list, and // hence doesn't need protection from concurrent access at this stage void JavaThread::verify_not_published() { - if (!Threads_lock->owned_by_self()) { - MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); - assert(!Threads::includes(this), - "java thread shouldn't have been published yet!"); - } else { - assert(!Threads::includes(this), - "java thread shouldn't have been published yet!"); - } + ThreadsListHandle tlh; + assert(!tlh.includes(this), "JavaThread shouldn't have been published yet!"); } #endif @@ -2451,7 +2558,8 @@ void JavaThread::java_resume() { // Sanity check: thread is gone, has started exiting or the thread // was not externally suspended. - if (!Threads::includes(this) || is_exiting() || !is_external_suspend()) { + ThreadsListHandle tlh; + if (!tlh.includes(this) || is_exiting() || !is_external_suspend()) { return; } @@ -2925,6 +3033,13 @@ void JavaThread::print_on_error(outputStream* st, char *buf, int buflen) const { st->print(", stack(" PTR_FORMAT "," PTR_FORMAT ")", p2i(stack_end()), p2i(stack_base())); st->print("]"); + + if (_threads_hazard_ptr != NULL) { + st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr)); + } + if (_nested_threads_hazard_ptr != NULL) { + print_nested_threads_hazard_ptrs_on(st); + } return; } @@ -3318,23 +3433,136 @@ void CodeCacheSweeperThread::nmethods_do(CodeBlobClosure* cf) { // ======= Threads ======== // The Threads class links together all active threads, and provides -// operations over all threads. It is protected by its own Mutex -// lock, which is also used in other contexts to protect thread -// operations from having the thread being operated on from exiting -// and going away unexpectedly (e.g., safepoint synchronization) +// operations over all threads. It is protected by the Threads_lock, +// which is also used in other global contexts like safepointing. +// ThreadsListHandles are used to safely perform operations on one +// or more threads without the risk of the thread exiting during the +// operation. +// +// Note: The Threads_lock is currently more widely used than we +// would like. We are actively migrating Threads_lock uses to other +// mechanisms in order to reduce Threads_lock contention. + +JavaThread* Threads::_thread_list = NULL; +int Threads::_number_of_threads = 0; +int Threads::_number_of_non_daemon_threads = 0; +int Threads::_return_code = 0; +int Threads::_thread_claim_parity = 0; +size_t JavaThread::_stack_size_at_create = 0; +// Safe Memory Reclamation (SMR) support: +Monitor* Threads::_smr_delete_lock = + new Monitor(Monitor::special, "smr_delete_lock", + false /* allow_vm_block */, + Monitor::_safepoint_check_never); +// The '_cnt', '_max' and '_times" fields are enabled via +// -XX:+EnableThreadSMRStatistics: + +// # of parallel threads in _smr_delete_lock->wait(). +// Impl note: Hard to imagine > 64K waiting threads so this could be 16-bit, +// but there is no nice 16-bit _FORMAT support. +uint Threads::_smr_delete_lock_wait_cnt = 0; + +// Max # of parallel threads in _smr_delete_lock->wait(). +// Impl note: See _smr_delete_lock_wait_cnt note. +uint Threads::_smr_delete_lock_wait_max = 0; + +// Flag to indicate when an _smr_delete_lock->notify() is needed. +// Impl note: See _smr_delete_lock_wait_cnt note. +volatile uint Threads::_smr_delete_notify = 0; + +// # of threads deleted over VM lifetime. +// Impl note: Atomically incremented over VM lifetime so use unsigned for more +// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc +// isn't available everywhere (or is it?). +volatile uint Threads::_smr_deleted_thread_cnt = 0; + +// Max time in millis to delete a thread. +// Impl note: 16-bit might be too small on an overloaded machine. Use +// unsigned since this is a time value. Set via Atomic::cmpxchg() in a +// loop for correctness. +volatile uint Threads::_smr_deleted_thread_time_max = 0; + +// Cumulative time in millis to delete threads. +// Impl note: Atomically added to over VM lifetime so use unsigned for more +// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc +// isn't available everywhere (or is it?). +volatile uint Threads::_smr_deleted_thread_times = 0; + +ThreadsList* volatile Threads::_smr_java_thread_list = new ThreadsList(0); + +// # of ThreadsLists allocated over VM lifetime. +// Impl note: We allocate a new ThreadsList for every thread create and +// every thread delete so we need a bigger type than the +// _smr_deleted_thread_cnt field. +uint64_t Threads::_smr_java_thread_list_alloc_cnt = 1; + +// # of ThreadsLists freed over VM lifetime. +// Impl note: See _smr_java_thread_list_alloc_cnt note. +uint64_t Threads::_smr_java_thread_list_free_cnt = 0; + +// Max size ThreadsList allocated. +// Impl note: Max # of threads alive at one time should fit in unsigned 32-bit. +uint Threads::_smr_java_thread_list_max = 0; + +// Max # of nested ThreadsLists for a thread. +// Impl note: Hard to imagine > 64K nested ThreadsLists so this could be +// 16-bit, but there is no nice 16-bit _FORMAT support. +uint Threads::_smr_nested_thread_list_max = 0; + +// # of ThreadsListHandles deleted over VM lifetime. +// Impl note: Atomically incremented over VM lifetime so use unsigned for +// more range. There will be fewer ThreadsListHandles than threads so +// unsigned 32-bit should be fine. +volatile uint Threads::_smr_tlh_cnt = 0; + +// Max time in millis to delete a ThreadsListHandle. +// Impl note: 16-bit might be too small on an overloaded machine. Use +// unsigned since this is a time value. Set via Atomic::cmpxchg() in a +// loop for correctness. +volatile uint Threads::_smr_tlh_time_max = 0; + +// Cumulative time in millis to delete ThreadsListHandles. +// Impl note: Atomically added to over VM lifetime so use unsigned for more +// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc +// isn't available everywhere (or is it?). +volatile uint Threads::_smr_tlh_times = 0; + +ThreadsList* Threads::_smr_to_delete_list = NULL; + +// # of parallel ThreadsLists on the to-delete list. +// Impl note: Hard to imagine > 64K ThreadsLists needing to be deleted so +// this could be 16-bit, but there is no nice 16-bit _FORMAT support. +uint Threads::_smr_to_delete_list_cnt = 0; + +// Max # of parallel ThreadsLists on the to-delete list. +// Impl note: See _smr_to_delete_list_cnt note. +uint Threads::_smr_to_delete_list_max = 0; -JavaThread* Threads::_thread_list = NULL; -int Threads::_number_of_threads = 0; -int Threads::_number_of_non_daemon_threads = 0; -int Threads::_return_code = 0; -int Threads::_thread_claim_parity = 0; -size_t JavaThread::_stack_size_at_create = 0; #ifdef ASSERT -bool Threads::_vm_complete = false; +bool Threads::_vm_complete = false; #endif +static inline void *prefetch_and_load_ptr(void **addr, intx prefetch_interval) { + Prefetch::read((void*)addr, prefetch_interval); + return *addr; +} + +// Possibly the ugliest for loop the world has seen. C++ does not allow +// multiple types in the declaration section of the for loop. In this case +// we are only dealing with pointers and hence can cast them. It looks ugly +// but macros are ugly and therefore it's fine to make things absurdly ugly. +#define DO_JAVA_THREADS(LIST, X) \ + for (JavaThread *MACRO_scan_interval = (JavaThread*)(uintptr_t)PrefetchScanIntervalInBytes, \ + *MACRO_list = (JavaThread*)(LIST), \ + **MACRO_end = ((JavaThread**)((ThreadsList*)MACRO_list)->threads()) + ((ThreadsList*)MACRO_list)->length(), \ + **MACRO_current_p = (JavaThread**)((ThreadsList*)MACRO_list)->threads(), \ + *X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval); \ + MACRO_current_p != MACRO_end; \ + MACRO_current_p++, \ + X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval)) + // All JavaThreads -#define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next()) +#define ALL_JAVA_THREADS(X) DO_JAVA_THREADS(get_smr_java_thread_list(), X) // All JavaThreads + all non-JavaThreads (i.e., every thread in the system) void Threads::threads_do(ThreadClosure* tc) { @@ -3435,6 +3663,214 @@ static void call_initPhase3(TRAPS) { vmSymbols::void_method_signature(), CHECK); } +// Safe Memory Reclamation (SMR) support: +// + +// Acquire a stable ThreadsList. +// +ThreadsList *Threads::acquire_stable_list(Thread *self, bool is_ThreadsListSetter) { + assert(self != NULL, "sanity check"); + // acquire_stable_list_nested_path() will grab the Threads_lock + // so let's make sure the ThreadsListHandle is in a safe place. + // ThreadsListSetter cannot make this check on this code path. + debug_only(if (!is_ThreadsListSetter && StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);) + + if (self->get_threads_hazard_ptr() == NULL) { + // The typical case is first. + return acquire_stable_list_fast_path(self); + } + + // The nested case is rare. + return acquire_stable_list_nested_path(self); +} + +// Fast path (and lock free) way to acquire a stable ThreadsList. +// +ThreadsList *Threads::acquire_stable_list_fast_path(Thread *self) { + assert(self != NULL, "sanity check"); + assert(self->get_threads_hazard_ptr() == NULL, "sanity check"); + assert(self->get_nested_threads_hazard_ptr() == NULL, + "cannot have a nested hazard ptr with a NULL regular hazard ptr"); + + ThreadsList* threads; + + // Stable recording of a hazard ptr for SMR. This code does not use + // locks so its use of the _smr_java_thread_list & _threads_hazard_ptr + // fields is racy relative to code that uses those fields with locks. + // OrderAccess and Atomic functions are used to deal with those races. + // + while (true) { + threads = get_smr_java_thread_list(); + + // Publish a tagged hazard ptr to denote that the hazard ptr is not + // yet verified as being stable. Due to the fence after the hazard + // ptr write, it will be sequentially consistent w.r.t. the + // sequentially consistent writes of the ThreadsList, even on + // non-multiple copy atomic machines where stores can be observed + // in different order from different observer threads. + ThreadsList* unverified_threads = Thread::tag_hazard_ptr(threads); + self->set_threads_hazard_ptr(unverified_threads); + + // If _smr_java_thread_list has changed, we have lost a race with + // Threads::add() or Threads::remove() and have to try again. + if (get_smr_java_thread_list() != threads) { + continue; + } + + // We try to remove the tag which will verify the hazard ptr as + // being stable. This exchange can race with a scanning thread + // which might invalidate the tagged hazard ptr to keep it from + // being followed to access JavaThread ptrs. If we lose the race, + // we simply retry. If we win the race, then the stable hazard + // ptr is officially published. + if (self->cmpxchg_threads_hazard_ptr(threads, unverified_threads) == unverified_threads) { + break; + } + } + + // A stable hazard ptr has been published letting other threads know + // that the ThreadsList and the JavaThreads reachable from this list + // are protected and hence they should not be deleted until everyone + // agrees it is safe to do so. + + return threads; +} + +// Acquire a nested stable ThreadsList; this is rare so it uses +// Threads_lock. +// +ThreadsList *Threads::acquire_stable_list_nested_path(Thread *self) { + assert(self != NULL, "sanity check"); + assert(self->get_threads_hazard_ptr() != NULL, + "cannot have a NULL regular hazard ptr when acquiring a nested hazard ptr"); + + // The thread already has a hazard ptr (ThreadsList ref) so we need + // to create a nested ThreadsListHandle with the current ThreadsList + // since it might be different than our current hazard ptr. The need + // for a nested ThreadsListHandle is rare so we do this while holding + // the Threads_lock so we don't race with the scanning code; the code + // is so much simpler this way. + + NestedThreadsList* node; + { + // Only grab the Threads_lock if we don't already own it. + MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock); + node = new NestedThreadsList(get_smr_java_thread_list()); + // We insert at the front of the list to match up with the delete + // in release_stable_list(). + node->set_next(self->get_nested_threads_hazard_ptr()); + self->set_nested_threads_hazard_ptr(node); + if (EnableThreadSMRStatistics) { + self->inc_nested_threads_hazard_ptr_cnt(); + if (self->nested_threads_hazard_ptr_cnt() > _smr_nested_thread_list_max) { + _smr_nested_thread_list_max = self->nested_threads_hazard_ptr_cnt(); + } + } + } + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::acquire_stable_list: add NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list())); + + return node->t_list(); +} + +// Release a stable ThreadsList. +// +void Threads::release_stable_list(Thread *self) { + assert(self != NULL, "sanity check"); + // release_stable_list_nested_path() will grab the Threads_lock + // so let's make sure the ThreadsListHandle is in a safe place. + debug_only(if (StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);) + + if (self->get_nested_threads_hazard_ptr() == NULL) { + // The typical case is first. + release_stable_list_fast_path(self); + return; + } + + // The nested case is rare. + release_stable_list_nested_path(self); +} + +// Fast path way to release a stable ThreadsList. The release portion +// is lock-free, but the wake up portion is not. +// +void Threads::release_stable_list_fast_path(Thread *self) { + assert(self != NULL, "sanity check"); + assert(self->get_threads_hazard_ptr() != NULL, "sanity check"); + assert(self->get_nested_threads_hazard_ptr() == NULL, + "cannot have a nested hazard ptr when releasing a regular hazard ptr"); + + // After releasing the hazard ptr, other threads may go ahead and + // free up some memory temporarily used by a ThreadsList snapshot. + self->set_threads_hazard_ptr(NULL); + + // We use double-check locking to reduce traffic on the system + // wide smr_delete_lock. + if (Threads::smr_delete_notify()) { + // An exiting thread might be waiting in smr_delete(); we need to + // check with smr_delete_lock to be sure. + release_stable_list_wake_up((char *) "regular hazard ptr"); + } +} + +// Release a nested stable ThreadsList; this is rare so it uses +// Threads_lock. +// +void Threads::release_stable_list_nested_path(Thread *self) { + assert(self != NULL, "sanity check"); + assert(self->get_nested_threads_hazard_ptr() != NULL, "sanity check"); + assert(self->get_threads_hazard_ptr() != NULL, + "must have a regular hazard ptr to have nested hazard ptrs"); + + // We have a nested ThreadsListHandle so we have to release it first. + // The need for a nested ThreadsListHandle is rare so we do this while + // holding the Threads_lock so we don't race with the scanning code; + // the code is so much simpler this way. + + NestedThreadsList *node; + { + // Only grab the Threads_lock if we don't already own it. + MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock); + // We remove from the front of the list to match up with the insert + // in acquire_stable_list(). + node = self->get_nested_threads_hazard_ptr(); + self->set_nested_threads_hazard_ptr(node->next()); + if (EnableThreadSMRStatistics) { + self->dec_nested_threads_hazard_ptr_cnt(); + } + } + + // An exiting thread might be waiting in smr_delete(); we need to + // check with smr_delete_lock to be sure. + release_stable_list_wake_up((char *) "nested hazard ptr"); + + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list: delete NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list())); + + delete node; +} + +// Wake up portion of the release stable ThreadsList protocol; +// uses the smr_delete_lock(). +// +void Threads::release_stable_list_wake_up(char *log_str) { + assert(log_str != NULL, "sanity check"); + + // Note: smr_delete_lock is held in smr_delete() for the entire + // hazard ptr search so that we do not lose this notify() if + // the exiting thread has to wait. That code path also holds + // Threads_lock (which was grabbed before smr_delete_lock) so that + // threads_do() can be called. This means the system can't start a + // safepoint which means this thread can't take too long to get to + // a safepoint because of being blocked on smr_delete_lock. + // + MonitorLockerEx ml(Threads::smr_delete_lock(), Monitor::_no_safepoint_check_flag); + if (Threads::smr_delete_notify()) { + // Notify any exiting JavaThreads that are waiting in smr_delete() + // that we've released a ThreadsList. + ml.notify_all(); + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list notified %s", os::current_thread_id(), log_str); + } +} + void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) { TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime)); @@ -3616,7 +4052,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { if (!main_thread->set_as_starting_thread()) { vm_shutdown_during_initialization( "Failed necessary internal allocation. Out of swap space"); - delete main_thread; + main_thread->smr_delete(); *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again return JNI_ENOMEM; } @@ -3631,7 +4067,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // Initialize global modules jint status = init_globals(); if (status != JNI_OK) { - delete main_thread; + main_thread->smr_delete(); *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again return status; } @@ -4037,23 +4473,6 @@ void Threads::create_vm_init_libraries() { } } -JavaThread* Threads::find_java_thread_from_java_tid(jlong java_tid) { - assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); - - JavaThread* java_thread = NULL; - // Sequential search for now. Need to do better optimization later. - for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { - oop tobj = thread->threadObj(); - if (!thread->is_exiting() && - tobj != NULL && - java_tid == java_lang_Thread::thread_id(tobj)) { - java_thread = thread; - break; - } - } - return java_thread; -} - // Last thread running calls java.lang.Shutdown.shutdown() void JavaThread::invoke_shutdown_hooks() { @@ -4179,6 +4598,11 @@ bool Threads::destroy_vm() { notify_vm_shutdown(); + // We are after VM_Exit::set_vm_exited() so we can't call + // thread->smr_delete() or we will block on the Threads_lock. + // Deleting the shutdown thread here is safe because another + // JavaThread cannot have an active ThreadsListHandle for + // this JavaThread. delete thread; #if INCLUDE_JVMCI @@ -4212,6 +4636,501 @@ jboolean Threads::is_supported_jni_version(jint version) { return JNI_FALSE; } +// Hash table of pointers found by a scan. Used for collecting hazard +// pointers (ThreadsList references). Also used for collecting JavaThreads +// that are indirectly referenced by hazard ptrs. An instance of this +// class only contains one type of pointer. +// +class ThreadScanHashtable : public CHeapObj<mtThread> { + private: + static bool ptr_equals(void * const& s1, void * const& s2) { + return s1 == s2; + } + + static unsigned int ptr_hash(void * const& s1) { + // 2654435761 = 2^32 * Phi (golden ratio) + return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u); + } + + int _table_size; + // ResourceHashtable SIZE is specified at compile time so our + // dynamic _table_size is unused for now; 1031 is the first prime + // after 1024. + typedef ResourceHashtable<void *, int, &ThreadScanHashtable::ptr_hash, + &ThreadScanHashtable::ptr_equals, 1031, + ResourceObj::C_HEAP, mtThread> PtrTable; + PtrTable * _ptrs; + + public: + // ResourceHashtable is passed to various functions and populated in + // different places so we allocate it using C_HEAP to make it immune + // from any ResourceMarks that happen to be in the code paths. + ThreadScanHashtable(int table_size) : _table_size(table_size), _ptrs(new (ResourceObj::C_HEAP, mtThread) PtrTable()) {} + + ~ThreadScanHashtable() { delete _ptrs; } + + bool has_entry(void *pointer) { + int *val_ptr = _ptrs->get(pointer); + return val_ptr != NULL && *val_ptr == 1; + } + + void add_entry(void *pointer) { + _ptrs->put(pointer, 1); + } +}; + +// Closure to gather JavaThreads indirectly referenced by hazard ptrs +// (ThreadsList references) into a hash table. This closure handles part 2 +// of the dance - adding all the JavaThreads referenced by the hazard +// pointer (ThreadsList reference) to the hash table. +// +class AddThreadHazardPointerThreadClosure : public ThreadClosure { + private: + ThreadScanHashtable *_table; + + public: + AddThreadHazardPointerThreadClosure(ThreadScanHashtable *table) : _table(table) {} + + virtual void do_thread(Thread *thread) { + if (!_table->has_entry((void*)thread)) { + // The same JavaThread might be on more than one ThreadsList or + // more than one thread might be using the same ThreadsList. In + // either case, we only need a single entry for a JavaThread. + _table->add_entry((void*)thread); + } + } +}; + +// Closure to gather JavaThreads indirectly referenced by hazard ptrs +// (ThreadsList references) into a hash table. This closure handles part 1 +// of the dance - hazard ptr chain walking and dispatch to another +// closure. +// +class ScanHazardPtrGatherProtectedThreadsClosure : public ThreadClosure { + private: + ThreadScanHashtable *_table; + public: + ScanHazardPtrGatherProtectedThreadsClosure(ThreadScanHashtable *table) : _table(table) {} + + virtual void do_thread(Thread *thread) { + assert_locked_or_safepoint(Threads_lock); + + if (thread == NULL) return; + + // This code races with Threads::acquire_stable_list() which is + // lock-free so we have to handle some special situations. + // + ThreadsList *current_list = NULL; + while (true) { + current_list = thread->get_threads_hazard_ptr(); + // No hazard ptr so nothing more to do. + if (current_list == NULL) { + assert(thread->get_nested_threads_hazard_ptr() == NULL, + "cannot have a nested hazard ptr with a NULL regular hazard ptr"); + return; + } + + // If the hazard ptr is verified as stable (since it is not tagged), + // then it is safe to use. + if (!Thread::is_hazard_ptr_tagged(current_list)) break; + + // The hazard ptr is tagged as not yet verified as being stable + // so we are racing with acquire_stable_list(). This exchange + // attempts to invalidate the hazard ptr. If we win the race, + // then we can ignore this unstable hazard ptr and the other + // thread will retry the attempt to publish a stable hazard ptr. + // If we lose the race, then we retry our attempt to look at the + // hazard ptr. + if (thread->cmpxchg_threads_hazard_ptr(NULL, current_list) == current_list) return; + } + + // The current JavaThread has a hazard ptr (ThreadsList reference) + // which might be _smr_java_thread_list or it might be an older + // ThreadsList that has been removed but not freed. In either case, + // the hazard ptr is protecting all the JavaThreads on that + // ThreadsList. + AddThreadHazardPointerThreadClosure add_cl(_table); + current_list->threads_do(&add_cl); + + // Any NestedThreadsLists are also protecting JavaThreads so + // gather those also; the ThreadsLists may be different. + for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr(); + node != NULL; node = node->next()) { + node->t_list()->threads_do(&add_cl); + } + } +}; + +// Closure to print JavaThreads that have a hazard ptr (ThreadsList +// reference) that contains an indirect reference to a specific JavaThread. +// +class ScanHazardPtrPrintMatchingThreadsClosure : public ThreadClosure { + private: + JavaThread *_thread; + public: + ScanHazardPtrPrintMatchingThreadsClosure(JavaThread *thread) : _thread(thread) {} + + virtual void do_thread(Thread *thread) { + assert_locked_or_safepoint(Threads_lock); + + if (thread == NULL) return; + ThreadsList *current_list = thread->get_threads_hazard_ptr(); + if (current_list == NULL) { + assert(thread->get_nested_threads_hazard_ptr() == NULL, + "cannot have a nested hazard ptr with a NULL regular hazard ptr"); + return; + } + // If the hazard ptr is unverified, then ignore it. + if (Thread::is_hazard_ptr_tagged(current_list)) return; + + // The current JavaThread has a hazard ptr (ThreadsList reference) + // which might be _smr_java_thread_list or it might be an older + // ThreadsList that has been removed but not freed. In either case, + // the hazard ptr is protecting all the JavaThreads on that + // ThreadsList, but we only care about matching a specific JavaThread. + DO_JAVA_THREADS(current_list, p) { + if (p == _thread) { + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread)); + break; + } + } + + // Any NestedThreadsLists are also protecting JavaThreads so + // check those also; the ThreadsLists may be different. + for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr(); + node != NULL; node = node->next()) { + DO_JAVA_THREADS(node->t_list(), p) { + if (p == _thread) { + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a nested hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread)); + return; + } + } + } + } +}; + +// Return true if the specified JavaThread is protected by a hazard +// pointer (ThreadsList reference). Otherwise, returns false. +// +bool Threads::is_a_protected_JavaThread(JavaThread *thread) { + assert_locked_or_safepoint(Threads_lock); + + // Hash table size should be first power of two higher than twice + // the length of the Threads list. + int hash_table_size = MIN2(_number_of_threads, 32) << 1; + hash_table_size--; + hash_table_size |= hash_table_size >> 1; + hash_table_size |= hash_table_size >> 2; + hash_table_size |= hash_table_size >> 4; + hash_table_size |= hash_table_size >> 8; + hash_table_size |= hash_table_size >> 16; + hash_table_size++; + + // Gather a hash table of the JavaThreads indirectly referenced by + // hazard ptrs. + ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size); + ScanHazardPtrGatherProtectedThreadsClosure scan_cl(scan_table); + Threads::threads_do(&scan_cl); + + bool thread_is_protected = false; + if (scan_table->has_entry((void*)thread)) { + thread_is_protected = true; + } + delete scan_table; + return thread_is_protected; +} + +// Safely delete a JavaThread when it is no longer in use by a +// ThreadsListHandle. +// +void Threads::smr_delete(JavaThread *thread) { + assert(!Threads_lock->owned_by_self(), "sanity"); + + bool has_logged_once = false; + elapsedTimer timer; + if (EnableThreadSMRStatistics) { + timer.start(); + } + + while (true) { + { + // No safepoint check because this JavaThread is not on the + // Threads list. + MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag); + // Cannot use a MonitorLockerEx helper here because we have + // to drop the Threads_lock first if we wait. + Threads::smr_delete_lock()->lock_without_safepoint_check(); + // Set the smr_delete_notify flag after we grab smr_delete_lock + // and before we scan hazard ptrs because we're doing + // double-check locking in release_stable_list(). + Threads::set_smr_delete_notify(); + + if (!is_a_protected_JavaThread(thread)) { + // This is the common case. + Threads::clear_smr_delete_notify(); + Threads::smr_delete_lock()->unlock(); + break; + } + if (!has_logged_once) { + has_logged_once = true; + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is not deleted.", os::current_thread_id(), p2i(thread)); + if (log_is_enabled(Debug, os, thread)) { + ScanHazardPtrPrintMatchingThreadsClosure scan_cl(thread); + Threads::threads_do(&scan_cl); + } + } + } // We have to drop the Threads_lock to wait or delete the thread + + if (EnableThreadSMRStatistics) { + _smr_delete_lock_wait_cnt++; + if (_smr_delete_lock_wait_cnt > _smr_delete_lock_wait_max) { + _smr_delete_lock_wait_max = _smr_delete_lock_wait_cnt; + } + } + // Wait for a release_stable_list() call before we check again. No + // safepoint check, no timeout, and not as suspend equivalent flag + // because this JavaThread is not on the Threads list. + Threads::smr_delete_lock()->wait(Mutex::_no_safepoint_check_flag, 0, + !Mutex::_as_suspend_equivalent_flag); + if (EnableThreadSMRStatistics) { + _smr_delete_lock_wait_cnt--; + } + + Threads::clear_smr_delete_notify(); + Threads::smr_delete_lock()->unlock(); + // Retry the whole scenario. + } + + if (ThreadLocalHandshakes) { + // The thread is about to be deleted so cancel any handshake. + thread->cancel_handshake(); + } + + delete thread; + if (EnableThreadSMRStatistics) { + timer.stop(); + uint millis = (uint)timer.milliseconds(); + Threads::inc_smr_deleted_thread_cnt(); + Threads::add_smr_deleted_thread_times(millis); + Threads::update_smr_deleted_thread_time_max(millis); + } + + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is deleted.", os::current_thread_id(), p2i(thread)); +} + +bool Threads::smr_delete_notify() { + // Use load_acquire() in order to see any updates to _smr_delete_notify + // earlier than when smr_delete_lock is grabbed. + return (OrderAccess::load_acquire(&_smr_delete_notify) != 0); +} + +// set_smr_delete_notify() and clear_smr_delete_notify() are called +// under the protection of the smr_delete_lock, but we also use an +// Atomic operation to ensure the memory update is seen earlier than +// when the smr_delete_lock is dropped. +// +void Threads::set_smr_delete_notify() { + Atomic::inc(&_smr_delete_notify); +} + +void Threads::clear_smr_delete_notify() { + Atomic::dec(&_smr_delete_notify); +} + +// Closure to gather hazard ptrs (ThreadsList references) into a hash table. +// +class ScanHazardPtrGatherThreadsListClosure : public ThreadClosure { + private: + ThreadScanHashtable *_table; + public: + ScanHazardPtrGatherThreadsListClosure(ThreadScanHashtable *table) : _table(table) {} + + virtual void do_thread(Thread* thread) { + assert_locked_or_safepoint(Threads_lock); + + if (thread == NULL) return; + ThreadsList *threads = thread->get_threads_hazard_ptr(); + if (threads == NULL) { + assert(thread->get_nested_threads_hazard_ptr() == NULL, + "cannot have a nested hazard ptr with a NULL regular hazard ptr"); + return; + } + // In this closure we always ignore the tag that might mark this + // hazard ptr as not yet verified. If we happen to catch an + // unverified hazard ptr that is subsequently discarded (not + // published), then the only side effect is that we might keep a + // to-be-deleted ThreadsList alive a little longer. + threads = Thread::untag_hazard_ptr(threads); + if (!_table->has_entry((void*)threads)) { + _table->add_entry((void*)threads); + } + + // Any NestedThreadsLists are also protecting JavaThreads so + // gather those also; the ThreadsLists may be different. + for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr(); + node != NULL; node = node->next()) { + threads = node->t_list(); + if (!_table->has_entry((void*)threads)) { + _table->add_entry((void*)threads); + } + } + } +}; + +// Safely free a ThreadsList after a Threads::add() or Threads::remove(). +// The specified ThreadsList may not get deleted during this call if it +// is still in-use (referenced by a hazard ptr). Other ThreadsLists +// in the chain may get deleted by this call if they are no longer in-use. +void Threads::smr_free_list(ThreadsList* threads) { + assert_locked_or_safepoint(Threads_lock); + + threads->set_next_list(_smr_to_delete_list); + _smr_to_delete_list = threads; + if (EnableThreadSMRStatistics) { + _smr_to_delete_list_cnt++; + if (_smr_to_delete_list_cnt > _smr_to_delete_list_max) { + _smr_to_delete_list_max = _smr_to_delete_list_cnt; + } + } + + // Hash table size should be first power of two higher than twice the length of the ThreadsList + int hash_table_size = MIN2(_number_of_threads, 32) << 1; + hash_table_size--; + hash_table_size |= hash_table_size >> 1; + hash_table_size |= hash_table_size >> 2; + hash_table_size |= hash_table_size >> 4; + hash_table_size |= hash_table_size >> 8; + hash_table_size |= hash_table_size >> 16; + hash_table_size++; + + // Gather a hash table of the current hazard ptrs: + ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size); + ScanHazardPtrGatherThreadsListClosure scan_cl(scan_table); + Threads::threads_do(&scan_cl); + + // Walk through the linked list of pending freeable ThreadsLists + // and free the ones that are not referenced from hazard ptrs. + ThreadsList* current = _smr_to_delete_list; + ThreadsList* prev = NULL; + ThreadsList* next = NULL; + bool threads_is_freed = false; + while (current != NULL) { + next = current->next_list(); + if (!scan_table->has_entry((void*)current)) { + // This ThreadsList is not referenced by a hazard ptr. + if (prev != NULL) { + prev->set_next_list(next); + } + if (_smr_to_delete_list == current) { + _smr_to_delete_list = next; + } + + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is freed.", os::current_thread_id(), p2i(current)); + if (current == threads) threads_is_freed = true; + delete current; + if (EnableThreadSMRStatistics) { + _smr_java_thread_list_free_cnt++; + _smr_to_delete_list_cnt--; + } + } else { + prev = current; + } + current = next; + } + + if (!threads_is_freed) { + // Only report "is not freed" on the original call to + // smr_free_list() for this ThreadsList. + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is not freed.", os::current_thread_id(), p2i(threads)); + } + + delete scan_table; +} + +// Remove a JavaThread from a ThreadsList. The returned ThreadsList is a +// new copy of the specified ThreadsList with the specified JavaThread +// removed. +ThreadsList *ThreadsList::remove_thread(ThreadsList* list, JavaThread* java_thread) { + assert(list->_length > 0, "sanity"); + + uint i = 0; + DO_JAVA_THREADS(list, current) { + if (current == java_thread) { + break; + } + i++; + } + assert(i < list->_length, "did not find JavaThread on the list"); + const uint index = i; + const uint new_length = list->_length - 1; + const uint head_length = index; + const uint tail_length = (new_length >= index) ? (new_length - index) : 0; + ThreadsList *const new_list = new ThreadsList(new_length); + + if (head_length > 0) { + Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length); + } + if (tail_length > 0) { + Copy::disjoint_words((HeapWord*)list->_threads + index + 1, (HeapWord*)new_list->_threads + index, tail_length); + } + + return new_list; +} + +// Add a JavaThread to a ThreadsList. The returned ThreadsList is a +// new copy of the specified ThreadsList with the specified JavaThread +// appended to the end. +ThreadsList *ThreadsList::add_thread(ThreadsList *list, JavaThread *java_thread) { + const uint index = list->_length; + const uint new_length = index + 1; + const uint head_length = index; + ThreadsList *const new_list = new ThreadsList(new_length); + + if (head_length > 0) { + Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length); + } + *(JavaThread**)(new_list->_threads + index) = java_thread; + + return new_list; +} + +int ThreadsList::find_index_of_JavaThread(JavaThread *target) { + if (target == NULL) { + return -1; + } + for (uint i = 0; i < length(); i++) { + if (target == thread_at(i)) { + return (int)i; + } + } + return -1; +} + +JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const { + DO_JAVA_THREADS(this, thread) { + oop tobj = thread->threadObj(); + // Ignore the thread if it hasn't run yet, has exited + // or is starting to exit. + if (tobj != NULL && !thread->is_exiting() && + java_tid == java_lang_Thread::thread_id(tobj)) { + // found a match + return thread; + } + } + return NULL; +} + +bool ThreadsList::includes(const JavaThread * const p) const { + if (p == NULL) { + return false; + } + DO_JAVA_THREADS(this, q) { + if (q == p) { + return true; + } + } + return false; +} void Threads::add(JavaThread* p, bool force_daemon) { // The threads lock must be owned at this point @@ -4222,6 +5141,11 @@ void Threads::add(JavaThread* p, bool force_daemon) { p->initialize_queues(); p->set_next(_thread_list); _thread_list = p; + + // Once a JavaThread is added to the Threads list, smr_delete() has + // to be used to delete it. Otherwise we can just delete it directly. + p->set_on_thread_list(); + _number_of_threads++; oop threadObj = p->threadObj(); bool daemon = true; @@ -4234,6 +5158,20 @@ void Threads::add(JavaThread* p, bool force_daemon) { ThreadService::add_thread(p, daemon); + // Maintain fast thread list + ThreadsList *new_list = ThreadsList::add_thread(get_smr_java_thread_list(), p); + if (EnableThreadSMRStatistics) { + _smr_java_thread_list_alloc_cnt++; + if (new_list->length() > _smr_java_thread_list_max) { + _smr_java_thread_list_max = new_list->length(); + } + } + // Initial _smr_java_thread_list will not generate a "Threads::add" mesg. + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::add: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list)); + + ThreadsList *old_list = xchg_smr_java_thread_list(new_list); + smr_free_list(old_list); + // Possible GC point. Events::log(p, "Thread added: " INTPTR_FORMAT, p2i(p)); } @@ -4247,7 +5185,20 @@ void Threads::remove(JavaThread* p) { // that we do not remove thread without safepoint code notice { MutexLocker ml(Threads_lock); - assert(includes(p), "p must be present"); + assert(get_smr_java_thread_list()->includes(p), "p must be present"); + + // Maintain fast thread list + ThreadsList *new_list = ThreadsList::remove_thread(get_smr_java_thread_list(), p); + if (EnableThreadSMRStatistics) { + _smr_java_thread_list_alloc_cnt++; + // This list is smaller so no need to check for a "longest" update. + } + + // Final _smr_java_thread_list will not generate a "Threads::remove" mesg. + log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::remove: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list)); + + ThreadsList *old_list = xchg_smr_java_thread_list(new_list); + smr_free_list(old_list); JavaThread* current = _thread_list; JavaThread* prev = NULL; @@ -4262,6 +5213,7 @@ void Threads::remove(JavaThread* p) { } else { _thread_list = p->next(); } + _number_of_threads--; oop threadObj = p->threadObj(); bool daemon = true; @@ -4288,17 +5240,6 @@ void Threads::remove(JavaThread* p) { Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p)); } -// Threads_lock must be held when this is called (or must be called during a safepoint) -bool Threads::includes(JavaThread* p) { - assert(Threads_lock->is_locked(), "sanity check"); - ALL_JAVA_THREADS(q) { - if (q == p) { - return true; - } - } - return false; -} - // Operations on the Threads list for GC. These are not explicitly locked, // but the garbage collector must provide a safe context for them to run. // In particular, these things should never be called when the Threads_lock @@ -4411,47 +5352,36 @@ void Threads::deoptimized_wrt_marked_nmethods() { // Get count Java threads that are waiting to enter the specified monitor. -GrowableArray<JavaThread*>* Threads::get_pending_threads(int count, - address monitor, - bool doLock) { - assert(doLock || SafepointSynchronize::is_at_safepoint(), - "must grab Threads_lock or be at safepoint"); +GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list, + int count, + address monitor) { GrowableArray<JavaThread*>* result = new GrowableArray<JavaThread*>(count); int i = 0; - { - MutexLockerEx ml(doLock ? Threads_lock : NULL); - ALL_JAVA_THREADS(p) { - if (!p->can_call_java()) continue; + DO_JAVA_THREADS(t_list, p) { + if (!p->can_call_java()) continue; - address pending = (address)p->current_pending_monitor(); - if (pending == monitor) { // found a match - if (i < count) result->append(p); // save the first count matches - i++; - } + address pending = (address)p->current_pending_monitor(); + if (pending == monitor) { // found a match + if (i < count) result->append(p); // save the first count matches + i++; } } + return result; } -JavaThread *Threads::owning_thread_from_monitor_owner(address owner, - bool doLock) { - assert(doLock || - Threads_lock->owned_by_self() || - SafepointSynchronize::is_at_safepoint(), - "must grab Threads_lock or be at safepoint"); - +JavaThread *Threads::owning_thread_from_monitor_owner(ThreadsList * t_list, + address owner) { // NULL owner means not locked so we can skip the search if (owner == NULL) return NULL; - { - MutexLockerEx ml(doLock ? Threads_lock : NULL); - ALL_JAVA_THREADS(p) { - // first, see if owner is the address of a Java thread - if (owner == (address)p) return p; - } + DO_JAVA_THREADS(t_list, p) { + // first, see if owner is the address of a Java thread + if (owner == (address)p) return p; } + // Cannot assert on lack of success here since this function may be // used by code that is trying to report useful problem information // like deadlock detection. @@ -4462,15 +5392,13 @@ JavaThread *Threads::owning_thread_from_monitor_owner(address owner, // Lock Word in the owning Java thread's stack. // JavaThread* the_owner = NULL; - { - MutexLockerEx ml(doLock ? Threads_lock : NULL); - ALL_JAVA_THREADS(q) { - if (q->is_lock_owned(owner)) { - the_owner = q; - break; - } + DO_JAVA_THREADS(t_list, q) { + if (q->is_lock_owned(owner)) { + the_owner = q; + break; } } + // cannot assert on lack of success here; see above comment return the_owner; } @@ -4495,6 +5423,9 @@ void Threads::print_on(outputStream* st, bool print_stacks, } #endif // INCLUDE_SERVICES + print_smr_info_on(st); + st->cr(); + ALL_JAVA_THREADS(p) { ResourceMark rm; p->print_on(st); @@ -4521,9 +5452,105 @@ void Threads::print_on(outputStream* st, bool print_stacks, wt->print_on(st); st->cr(); } + st->flush(); } +// Log Threads class SMR info. +void Threads::log_smr_statistics() { + LogTarget(Info, thread, smr) log; + if (log.is_enabled()) { + LogStream out(log); + print_smr_info_on(&out); + } +} + +// Print Threads class SMR info. +void Threads::print_smr_info_on(outputStream* st) { + // Only grab the Threads_lock if we don't already own it + // and if we are not reporting an error. + MutexLockerEx ml((Threads_lock->owned_by_self() || VMError::is_error_reported()) ? NULL : Threads_lock); + + st->print_cr("Threads class SMR info:"); + st->print_cr("_smr_java_thread_list=" INTPTR_FORMAT ", length=%u, " + "elements={", p2i(_smr_java_thread_list), + _smr_java_thread_list->length()); + print_smr_info_elements_on(st, _smr_java_thread_list); + st->print_cr("}"); + if (_smr_to_delete_list != NULL) { + st->print_cr("_smr_to_delete_list=" INTPTR_FORMAT ", length=%u, " + "elements={", p2i(_smr_to_delete_list), + _smr_to_delete_list->length()); + print_smr_info_elements_on(st, _smr_to_delete_list); + st->print_cr("}"); + for (ThreadsList *t_list = _smr_to_delete_list->next_list(); + t_list != NULL; t_list = t_list->next_list()) { + st->print("next-> " INTPTR_FORMAT ", length=%u, " + "elements={", p2i(t_list), t_list->length()); + print_smr_info_elements_on(st, t_list); + st->print_cr("}"); + } + } + if (!EnableThreadSMRStatistics) { + return; + } + st->print_cr("_smr_java_thread_list_alloc_cnt=" UINT64_FORMAT "," + "_smr_java_thread_list_free_cnt=" UINT64_FORMAT "," + "_smr_java_thread_list_max=%u, " + "_smr_nested_thread_list_max=%u", + _smr_java_thread_list_alloc_cnt, + _smr_java_thread_list_free_cnt, + _smr_java_thread_list_max, + _smr_nested_thread_list_max); + if (_smr_tlh_cnt > 0) { + st->print_cr("_smr_tlh_cnt=%u" + ", _smr_tlh_times=%u" + ", avg_smr_tlh_time=%0.2f" + ", _smr_tlh_time_max=%u", + _smr_tlh_cnt, _smr_tlh_times, + ((double) _smr_tlh_times / _smr_tlh_cnt), + _smr_tlh_time_max); + } + if (_smr_deleted_thread_cnt > 0) { + st->print_cr("_smr_deleted_thread_cnt=%u" + ", _smr_deleted_thread_times=%u" + ", avg_smr_deleted_thread_time=%0.2f" + ", _smr_deleted_thread_time_max=%u", + _smr_deleted_thread_cnt, _smr_deleted_thread_times, + ((double) _smr_deleted_thread_times / _smr_deleted_thread_cnt), + _smr_deleted_thread_time_max); + } + st->print_cr("_smr_delete_lock_wait_cnt=%u, _smr_delete_lock_wait_max=%u", + _smr_delete_lock_wait_cnt, _smr_delete_lock_wait_max); + st->print_cr("_smr_to_delete_list_cnt=%u, _smr_to_delete_list_max=%u", + _smr_to_delete_list_cnt, _smr_to_delete_list_max); +} + +// Print ThreadsList elements (4 per line). +void Threads::print_smr_info_elements_on(outputStream* st, + ThreadsList* t_list) { + uint cnt = 0; + JavaThreadIterator jti(t_list); + for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) { + st->print(INTPTR_FORMAT, p2i(jt)); + if (cnt < t_list->length() - 1) { + // Separate with comma or comma-space except for the last one. + if (((cnt + 1) % 4) == 0) { + // Four INTPTR_FORMAT fit on an 80 column line so end the + // current line with just a comma. + st->print_cr(","); + } else { + // Not the last one on the current line so use comma-space: + st->print(", "); + } + } else { + // Last one so just end the current line. + st->cr(); + } + cnt++; + } +} + void Threads::print_on_error(Thread* this_thread, outputStream* st, Thread* current, char* buf, int buflen, bool* found_current) { if (this_thread != NULL) { @@ -4560,6 +5587,9 @@ class PrintOnErrorClosure : public ThreadClosure { // memory (even in resource area), it might deadlock the error handler. void Threads::print_on_error(outputStream* st, Thread* current, char* buf, int buflen) { + print_smr_info_on(st); + st->cr(); + bool found_current = false; st->print_cr("Java Threads: ( => current thread )"); ALL_JAVA_THREADS(thread) { @@ -4581,6 +5611,7 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf, st->cr(); } st->cr(); + st->print_cr("Threads with active compile tasks:"); print_threads_compiling(st, buf, buflen); } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 65fa5aae1c5..df8e81d3776 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -57,6 +57,8 @@ #endif class ThreadSafepointState; +class ThreadsList; +class NestedThreadsList; class JvmtiThreadState; class JvmtiGetLoadedClassesClosure; @@ -101,6 +103,7 @@ class WorkerThread; // - WatcherThread class Thread: public ThreadShadow { + friend class Threads; friend class VMStructs; friend class JVMCIVMStructs; private: @@ -118,6 +121,47 @@ class Thread: public ThreadShadow { protected: // Support for forcing alignment of thread objects for biased locking void* _real_malloc_address; + // JavaThread lifecycle support: + friend class ScanHazardPtrGatherProtectedThreadsClosure; + friend class ScanHazardPtrGatherThreadsListClosure; + friend class ScanHazardPtrPrintMatchingThreadsClosure; + friend class ThreadsListHandle; + friend class ThreadsListSetter; + ThreadsList* volatile _threads_hazard_ptr; + ThreadsList* cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value); + ThreadsList* get_threads_hazard_ptr(); + void set_threads_hazard_ptr(ThreadsList* new_list); + static bool is_hazard_ptr_tagged(ThreadsList* list) { + return (intptr_t(list) & intptr_t(1)) == intptr_t(1); + } + static ThreadsList* tag_hazard_ptr(ThreadsList* list) { + return (ThreadsList*)(intptr_t(list) | intptr_t(1)); + } + static ThreadsList* untag_hazard_ptr(ThreadsList* list) { + return (ThreadsList*)(intptr_t(list) & ~intptr_t(1)); + } + NestedThreadsList* _nested_threads_hazard_ptr; + NestedThreadsList* get_nested_threads_hazard_ptr() { + return _nested_threads_hazard_ptr; + } + void set_nested_threads_hazard_ptr(NestedThreadsList* value) { + assert(Threads_lock->owned_by_self(), + "must own Threads_lock for _nested_threads_hazard_ptr to be valid."); + _nested_threads_hazard_ptr = value; + } + // This field is enabled via -XX:+EnableThreadSMRStatistics: + uint _nested_threads_hazard_ptr_cnt; + void dec_nested_threads_hazard_ptr_cnt() { + assert(_nested_threads_hazard_ptr_cnt != 0, "mismatched {dec,inc}_nested_threads_hazard_ptr_cnt()"); + _nested_threads_hazard_ptr_cnt--; + } + void inc_nested_threads_hazard_ptr_cnt() { + _nested_threads_hazard_ptr_cnt++; + } + uint nested_threads_hazard_ptr_cnt() { + return _nested_threads_hazard_ptr_cnt; + } + public: void* operator new(size_t size) throw() { return allocate(size, true); } void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() { @@ -359,6 +403,9 @@ class Thread: public ThreadShadow { static inline Thread* current_or_null_safe(); // Common thread operations +#ifdef ASSERT + static void check_for_dangling_thread_pointer(Thread *thread); +#endif static void set_priority(Thread* thread, ThreadPriority priority); static ThreadPriority get_priority(const Thread* const thread); static void start(Thread* thread); @@ -576,6 +623,7 @@ protected: // Printing virtual void print_on(outputStream* st) const; + virtual void print_nested_threads_hazard_ptrs_on(outputStream* st) const; void print() const { print_on(tty); } virtual void print_on_error(outputStream* st, char* buf, int buflen) const; void print_value_on(outputStream* st) const; @@ -798,6 +846,7 @@ class JavaThread: public Thread { friend class WhiteBox; private: JavaThread* _next; // The next thread in the Threads list + bool _on_thread_list; // Is set when this JavaThread is added to the Threads list oop _threadObj; // The Java level thread object #ifdef ASSERT @@ -1125,15 +1174,23 @@ class JavaThread: public Thread { void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; } bool is_at_poll_safepoint() { return _safepoint_state->is_at_poll_safepoint(); } + // JavaThread termination and lifecycle support: + void smr_delete(); + bool on_thread_list() const { return _on_thread_list; } + void set_on_thread_list() { _on_thread_list = true; } + // thread has called JavaThread::exit() or is terminated - bool is_exiting() { return _terminated == _thread_exiting || is_terminated(); } + bool is_exiting() const; // thread is terminated (no longer on the threads list); we compare // against the two non-terminated values so that a freed JavaThread // will also be considered terminated. - bool is_terminated() { return _terminated != _not_terminated && _terminated != _thread_exiting; } - void set_terminated(TerminatedTypes t) { _terminated = t; } + bool check_is_terminated(TerminatedTypes l_terminated) const { + return l_terminated != _not_terminated && l_terminated != _thread_exiting; + } + bool is_terminated() const; + void set_terminated(TerminatedTypes t); // special for Threads::remove() which is static: - void set_terminated_value() { _terminated = _thread_terminated; } + void set_terminated_value(); void block_if_vm_exited(); bool doing_unsafe_access() { return _doing_unsafe_access; } @@ -1220,6 +1277,9 @@ class JavaThread: public Thread { // via the appropriate -XX options. bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits); + // test for suspend - most (all?) of these should go away + bool is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits); + inline void set_external_suspend(); inline void clear_external_suspend(); @@ -2066,28 +2126,84 @@ inline CompilerThread* CompilerThread::current() { class Threads: AllStatic { friend class VMStructs; private: - static JavaThread* _thread_list; - static int _number_of_threads; - static int _number_of_non_daemon_threads; - static int _return_code; - static int _thread_claim_parity; + // Safe Memory Reclamation (SMR) support: + static Monitor* _smr_delete_lock; + // The '_cnt', '_max' and '_times" fields are enabled via + // -XX:+EnableThreadSMRStatistics (see thread.cpp for a + // description about each field): + static uint _smr_delete_lock_wait_cnt; + static uint _smr_delete_lock_wait_max; + static volatile uint _smr_delete_notify; + static volatile uint _smr_deleted_thread_cnt; + static volatile uint _smr_deleted_thread_time_max; + static volatile uint _smr_deleted_thread_times; + static ThreadsList* volatile _smr_java_thread_list; + static ThreadsList* get_smr_java_thread_list(); + static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list); + static uint64_t _smr_java_thread_list_alloc_cnt; + static uint64_t _smr_java_thread_list_free_cnt; + static uint _smr_java_thread_list_max; + static uint _smr_nested_thread_list_max; + static volatile uint _smr_tlh_cnt; + static volatile uint _smr_tlh_time_max; + static volatile uint _smr_tlh_times; + static ThreadsList* _smr_to_delete_list; + static uint _smr_to_delete_list_cnt; + static uint _smr_to_delete_list_max; + + static JavaThread* _thread_list; + static int _number_of_threads; + static int _number_of_non_daemon_threads; + static int _return_code; + static int _thread_claim_parity; #ifdef ASSERT - static bool _vm_complete; + static bool _vm_complete; #endif static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS); static void initialize_jsr292_core_classes(TRAPS); + + static void smr_free_list(ThreadsList* threads); + public: // Thread management // force_daemon is a concession to JNI, where we may need to add a // thread to the thread list before allocating its thread object static void add(JavaThread* p, bool force_daemon = false); static void remove(JavaThread* p); - static bool includes(JavaThread* p); - static JavaThread* first() { return _thread_list; } static void threads_do(ThreadClosure* tc); static void possibly_parallel_threads_do(bool is_par, ThreadClosure* tc); + // SMR support: + static ThreadsList *acquire_stable_list(Thread *self, bool is_ThreadsListSetter); + static ThreadsList *acquire_stable_list_fast_path(Thread *self); + static ThreadsList *acquire_stable_list_nested_path(Thread *self); + static void release_stable_list(Thread *self); + static void release_stable_list_fast_path(Thread *self); + static void release_stable_list_nested_path(Thread *self); + static void release_stable_list_wake_up(char *log_str); + static bool is_a_protected_JavaThread(JavaThread *thread); + static bool is_a_protected_JavaThread_with_lock(JavaThread *thread) { + MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock); + return is_a_protected_JavaThread(thread); + } + static void smr_delete(JavaThread *thread); + // The coordination between Threads::release_stable_list() and + // Threads::smr_delete() uses the smr_delete_lock in order to + // reduce the traffic on the Threads_lock. + static Monitor* smr_delete_lock() { return _smr_delete_lock; } + // The smr_delete_notify flag is used for proper double-check + // locking in order to reduce the traffic on the smr_delete_lock. + static bool smr_delete_notify(); + static void set_smr_delete_notify(); + static void clear_smr_delete_notify(); + static void inc_smr_deleted_thread_cnt(); + static void update_smr_deleted_thread_time_max(uint new_value); + static void add_smr_deleted_thread_times(uint add_value); + static void inc_smr_tlh_cnt(); + static void update_smr_tlh_time_max(uint new_value); + static void add_smr_tlh_times(uint add_value); + // Initializes the vm and creates the vm thread static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain); static void convert_vm_init_libraries_to_agents(); @@ -2148,7 +2264,10 @@ class Threads: AllStatic { // Verification static void verify(); + static void log_smr_statistics(); static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks); + static void print_smr_info_on(outputStream* st); + static void print_smr_info_elements_on(outputStream* st, ThreadsList* t_list); static void print(bool print_stacks, bool internal_format) { // this function is only used by debug.cpp print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */); @@ -2158,17 +2277,13 @@ class Threads: AllStatic { int buflen, bool* found_current); static void print_threads_compiling(outputStream* st, char* buf, int buflen); - // Get Java threads that are waiting to enter a monitor. If doLock - // is true, then Threads_lock is grabbed as needed. Otherwise, the - // VM needs to be at a safepoint. - static GrowableArray<JavaThread*>* get_pending_threads(int count, - address monitor, bool doLock); + // Get Java threads that are waiting to enter a monitor. + static GrowableArray<JavaThread*>* get_pending_threads(ThreadsList * t_list, + int count, address monitor); - // Get owning Java thread from the monitor's owner field. If doLock - // is true, then Threads_lock is grabbed as needed. Otherwise, the - // VM needs to be at a safepoint. - static JavaThread *owning_thread_from_monitor_owner(address owner, - bool doLock); + // Get owning Java thread from the monitor's owner field. + static JavaThread *owning_thread_from_monitor_owner(ThreadsList * t_list, + address owner); // Number of threads on the active threads list static int number_of_threads() { return _number_of_threads; } @@ -2177,9 +2292,6 @@ class Threads: AllStatic { // Deoptimizes all frames tied to marked nmethods static void deoptimized_wrt_marked_nmethods(); - - static JavaThread* find_java_thread_from_java_tid(jlong java_tid); - }; diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index 5a664953b9a..50d327bcb66 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -25,13 +25,10 @@ #ifndef SHARE_VM_RUNTIME_THREAD_INLINE_HPP #define SHARE_VM_RUNTIME_THREAD_INLINE_HPP -#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE - #include "runtime/atomic.hpp" #include "runtime/os.inline.hpp" #include "runtime/thread.hpp" - -#undef SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE +#include "runtime/threadSMR.hpp" inline void Thread::set_suspend_flag(SuspendFlags f) { assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch"); @@ -89,6 +86,18 @@ inline jlong Thread::cooked_allocated_bytes() { return allocated_bytes; } +inline ThreadsList* Thread::cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value) { + return (ThreadsList*)Atomic::cmpxchg(exchange_value, &_threads_hazard_ptr, compare_value); +} + +inline ThreadsList* Thread::get_threads_hazard_ptr() { + return (ThreadsList*)OrderAccess::load_acquire(&_threads_hazard_ptr); +} + +inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) { + OrderAccess::release_store_fence(&_threads_hazard_ptr, new_list); +} + inline void JavaThread::set_ext_suspended() { set_suspend_flag (_ext_suspended); } @@ -176,4 +185,83 @@ inline volatile void* JavaThread::get_polling_page() { return OrderAccess::load_acquire(polling_page_addr()); } +inline bool JavaThread::is_exiting() const { + // Use load-acquire so that setting of _terminated by + // JavaThread::exit() is seen more quickly. + TerminatedTypes l_terminated = (TerminatedTypes) + OrderAccess::load_acquire((volatile jint *) &_terminated); + return l_terminated == _thread_exiting || check_is_terminated(l_terminated); +} + +inline bool JavaThread::is_terminated() const { + // Use load-acquire so that setting of _terminated by + // JavaThread::exit() is seen more quickly. + TerminatedTypes l_terminated = (TerminatedTypes) + OrderAccess::load_acquire((volatile jint *) &_terminated); + return check_is_terminated(l_terminated); +} + +inline void JavaThread::set_terminated(TerminatedTypes t) { + // use release-store so the setting of _terminated is seen more quickly + OrderAccess::release_store((volatile jint *) &_terminated, (jint) t); +} + +// special for Threads::remove() which is static: +inline void JavaThread::set_terminated_value() { + // use release-store so the setting of _terminated is seen more quickly + OrderAccess::release_store((volatile jint *) &_terminated, (jint) _thread_terminated); +} + +inline ThreadsList* Threads::get_smr_java_thread_list() { + return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list); +} + +inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) { + return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list); +} + +inline void Threads::inc_smr_deleted_thread_cnt() { + Atomic::inc(&_smr_deleted_thread_cnt); +} + +inline void Threads::update_smr_deleted_thread_time_max(uint new_value) { + while (true) { + uint cur_value = _smr_deleted_thread_time_max; + if (new_value <= cur_value) { + // No need to update max value so we're done. + break; + } + if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) { + // Updated max value so we're done. Otherwise try it all again. + break; + } + } +} + +inline void Threads::add_smr_deleted_thread_times(uint add_value) { + Atomic::add(add_value, &_smr_deleted_thread_times); +} + +inline void Threads::inc_smr_tlh_cnt() { + Atomic::inc(&_smr_tlh_cnt); +} + +inline void Threads::update_smr_tlh_time_max(uint new_value) { + while (true) { + uint cur_value = _smr_tlh_time_max; + if (new_value <= cur_value) { + // No need to update max value so we're done. + break; + } + if (Atomic::cmpxchg(new_value, &_smr_tlh_time_max, cur_value) == cur_value) { + // Updated max value so we're done. Otherwise try it all again. + break; + } + } +} + +inline void Threads::add_smr_tlh_times(uint add_value) { + Atomic::add(add_value, &_smr_tlh_times); +} + #endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp new file mode 100644 index 00000000000..82ecc2590eb --- /dev/null +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" +#include "services/threadService.hpp" + +// 'entries + 1' so we always have at least one entry. +ThreadsList::ThreadsList(int entries) : _length(entries), _threads(NEW_C_HEAP_ARRAY(JavaThread*, entries + 1, mtThread)), _next_list(NULL) { + *(JavaThread**)(_threads + entries) = NULL; // Make sure the extra entry is NULL. +} + +ThreadsList::~ThreadsList() { + FREE_C_HEAP_ARRAY(JavaThread*, _threads); +} + +ThreadsListSetter::~ThreadsListSetter() { + if (_target_needs_release) { + // The hazard ptr in the target needs to be released. + Threads::release_stable_list(_target); + } +} + +void ThreadsListSetter::set() { + assert(_target->get_threads_hazard_ptr() == NULL, "hazard ptr should not already be set"); + (void) Threads::acquire_stable_list(_target, /* is_ThreadsListSetter */ true); + _target_needs_release = true; +} + +ThreadsListHandle::ThreadsListHandle(Thread *self) : _list(Threads::acquire_stable_list(self, /* is_ThreadsListSetter */ false)), _self(self) { + assert(self == Thread::current(), "sanity check"); + if (EnableThreadSMRStatistics) { + _timer.start(); + } +} + +ThreadsListHandle::~ThreadsListHandle() { + Threads::release_stable_list(_self); + if (EnableThreadSMRStatistics) { + _timer.stop(); + uint millis = (uint)_timer.milliseconds(); + Threads::inc_smr_tlh_cnt(); + Threads::add_smr_tlh_times(millis); + Threads::update_smr_tlh_time_max(millis); + } +} + +// Convert an internal thread reference to a JavaThread found on the +// associated ThreadsList. This ThreadsListHandle "protects" the +// returned JavaThread *. +// +// If thread_oop_p is not NULL, then the caller wants to use the oop +// after this call so the oop is returned. On success, *jt_pp is set +// to the converted JavaThread * and true is returned. On error, +// returns false. +// +bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread, + JavaThread ** jt_pp, + oop * thread_oop_p) { + assert(this->list() != NULL, "must have a ThreadsList"); + assert(jt_pp != NULL, "must have a return JavaThread pointer"); + // thread_oop_p is optional so no assert() + + // The JVM_* interfaces don't allow a NULL thread parameter; JVM/TI + // allows a NULL thread parameter to signify "current thread" which + // allows us to avoid calling cv_external_thread_to_JavaThread(). + // The JVM_* interfaces have no such leeway. + + oop thread_oop = JNIHandles::resolve_non_null(jthread); + // Looks like an oop at this point. + if (thread_oop_p != NULL) { + // Return the oop to the caller; the caller may still want + // the oop even if this function returns false. + *thread_oop_p = thread_oop; + } + + JavaThread *java_thread = java_lang_Thread::thread(thread_oop); + if (java_thread == NULL) { + // The java.lang.Thread does not contain a JavaThread * so it has + // not yet run or it has died. + return false; + } + // Looks like a live JavaThread at this point. + + if (java_thread != JavaThread::current()) { + // jthread is not for the current JavaThread so have to verify + // the JavaThread * against the ThreadsList. + if (EnableThreadSMRExtraValidityChecks && !includes(java_thread)) { + // Not on the JavaThreads list so it is not alive. + return false; + } + } + + // Return a live JavaThread that is "protected" by the + // ThreadsListHandle in the caller. + *jt_pp = java_thread; + return true; +} diff --git a/src/hotspot/share/runtime/threadSMR.hpp b/src/hotspot/share/runtime/threadSMR.hpp new file mode 100644 index 00000000000..1e177b7f435 --- /dev/null +++ b/src/hotspot/share/runtime/threadSMR.hpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#ifndef SHARE_VM_RUNTIME_THREADSMR_HPP +#define SHARE_VM_RUNTIME_THREADSMR_HPP + +#include "memory/allocation.hpp" +#include "runtime/timer.hpp" + +// Thread Safe Memory Reclamation (Thread-SMR) support. +// +// ThreadsListHandles are used to safely perform operations on one or more +// threads without the risk of the thread or threads exiting during the +// operation. It is no longer necessary to hold the Threads_lock to safely +// perform an operation on a target thread. +// +// There are several different ways to refer to java.lang.Thread objects +// so we have a few ways to get a protected JavaThread *: +// +// JNI jobject example: +// jobject jthread = ...; +// : +// ThreadsListHandle tlh; +// JavaThread* jt = NULL; +// bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &jt, NULL); +// if (is_alive) { +// : // do stuff with 'jt'... +// } +// +// JVM/TI jthread example: +// jthread thread = ...; +// : +// JavaThread* jt = NULL; +// ThreadsListHandle tlh; +// jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &jt, NULL); +// if (err != JVMTI_ERROR_NONE) { +// return err; +// } +// : // do stuff with 'jt'... +// +// JVM/TI oop example (this one should be very rare): +// oop thread_obj = ...; +// : +// JavaThread *jt = NULL; +// ThreadsListHandle tlh; +// jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &jt); +// if (err != JVMTI_ERROR_NONE) { +// return err; +// } +// : // do stuff with 'jt'... +// +// A JavaThread * that is included in the ThreadsList that is held by +// a ThreadsListHandle is protected as long as the ThreadsListHandle +// remains in scope. The target JavaThread * may have logically exited, +// but that target JavaThread * will not be deleted until it is no +// longer protected by a ThreadsListHandle. + + +// A fast list of JavaThreads. +// +class ThreadsList : public CHeapObj<mtThread> { + friend class ScanHazardPtrGatherProtectedThreadsClosure; + friend class Threads; + + const uint _length; + ThreadsList* _next_list; + JavaThread *const *const _threads; + + template <class T> + void threads_do_dispatch(T *cl, JavaThread *const thread) const; + + ThreadsList *next_list() const { return _next_list; } + void set_next_list(ThreadsList *list) { _next_list = list; } + +public: + ThreadsList(int entries); + ~ThreadsList(); + + template <class T> + void threads_do(T *cl) const; + + uint length() const { return _length; } + + JavaThread *const thread_at(uint i) const { return _threads[i]; } + + JavaThread *const *threads() const { return _threads; } + + // Returns -1 if target is not found. + int find_index_of_JavaThread(JavaThread* target); + JavaThread* find_JavaThread_from_java_tid(jlong java_tid) const; + bool includes(const JavaThread * const p) const; + + static ThreadsList* add_thread(ThreadsList* list, JavaThread* java_thread); + static ThreadsList* remove_thread(ThreadsList* list, JavaThread* java_thread); +}; + +// Linked list of ThreadsLists to support nested ThreadsListHandles. +class NestedThreadsList : public CHeapObj<mtThread> { + ThreadsList*const _t_list; + NestedThreadsList* _next; + +public: + NestedThreadsList(ThreadsList* t_list) : _t_list(t_list) { + assert(Threads_lock->owned_by_self(), + "must own Threads_lock for saved t_list to be valid."); + } + + ThreadsList* t_list() { return _t_list; } + NestedThreadsList* next() { return _next; } + void set_next(NestedThreadsList* value) { _next = value; } +}; + +// A helper to optionally set the hazard ptr in ourself. This helper can +// be used by ourself or by another thread. If the hazard ptr is set(), +// then the destructor will release it. +// +class ThreadsListSetter : public StackObj { +private: + bool _target_needs_release; // needs release only when set() + Thread * _target; + +public: + ThreadsListSetter() : _target_needs_release(false), _target(Thread::current()) { + } + ~ThreadsListSetter(); + ThreadsList* list(); + void set(); + bool target_needs_release() { return _target_needs_release; } +}; + +// This stack allocated ThreadsListHandle keeps all JavaThreads in the +// ThreadsList from being deleted until it is safe. +// +class ThreadsListHandle : public StackObj { + ThreadsList * _list; + Thread *const _self; + elapsedTimer _timer; // Enabled via -XX:+EnableThreadSMRStatistics. + +public: + ThreadsListHandle(Thread *self = Thread::current()); + ~ThreadsListHandle(); + + ThreadsList *list() const { + return _list; + } + + template <class T> + void threads_do(T *cl) const { + return _list->threads_do(cl); + } + + bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, oop * thread_oop_p); + + bool includes(JavaThread* p) { + return _list->includes(p); + } + + uint length() const { + return _list->length(); + } +}; + +// This stack allocated JavaThreadIterator is used to walk the +// specified ThreadsList using the following style: +// +// JavaThreadIterator jti(t_list); +// for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) { +// ... +// } +// +class JavaThreadIterator : public StackObj { + ThreadsList * _list; + uint _index; + +public: + JavaThreadIterator(ThreadsList *list) : _list(list), _index(0) { + assert(list != NULL, "ThreadsList must not be NULL."); + } + + JavaThread *first() { + _index = 0; + return _list->thread_at(_index); + } + + uint length() const { + return _list->length(); + } + + ThreadsList *list() const { + return _list; + } + + JavaThread *next() { + if (++_index >= length()) { + return NULL; + } + return _list->thread_at(_index); + } +}; + +// This stack allocated ThreadsListHandle and JavaThreadIterator combo +// is used to walk the ThreadsList in the included ThreadsListHandle +// using the following style: +// +// for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { +// ... +// } +// +class JavaThreadIteratorWithHandle : public StackObj { + ThreadsListHandle _tlh; + uint _index; + +public: + JavaThreadIteratorWithHandle() : _index(0) {} + + uint length() const { + return _tlh.length(); + } + + ThreadsList *list() const { + return _tlh.list(); + } + + JavaThread *next() { + if (_index >= length()) { + return NULL; + } + return _tlh.list()->thread_at(_index++); + } + + void rewind() { + _index = 0; + } +}; + +#endif // SHARE_VM_RUNTIME_THREADSMR_HPP diff --git a/src/hotspot/share/runtime/threadSMR.inline.hpp b/src/hotspot/share/runtime/threadSMR.inline.hpp new file mode 100644 index 00000000000..0203fc6e55f --- /dev/null +++ b/src/hotspot/share/runtime/threadSMR.inline.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#ifndef SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP +#define SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP + +#include "runtime/atomic.hpp" +#include "runtime/prefetch.inline.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" + +// Devirtualize known thread closure types. +template <class T> +inline void ThreadsList::threads_do_dispatch(T *cl, JavaThread *const thread) const { + cl->T::do_thread(thread); +} + +template <> +inline void ThreadsList::threads_do_dispatch<ThreadClosure>(ThreadClosure *cl, JavaThread *const thread) const { + cl->do_thread(thread); +} + +template <class T> +inline void ThreadsList::threads_do(T *cl) const { + const intx scan_interval = PrefetchScanIntervalInBytes; + JavaThread *const *const end = _threads + _length; + for (JavaThread *const *current_p = _threads; current_p != end; current_p++) { + Prefetch::read((void*)current_p, scan_interval); + JavaThread *const current = *current_p; + threads_do_dispatch(cl, current); + } +} + +inline ThreadsList* ThreadsListSetter::list() { + ThreadsList *ret = _target->get_threads_hazard_ptr(); + assert(ret != NULL, "hazard ptr should be set"); + assert(!Thread::is_hazard_ptr_tagged(ret), "hazard ptr should be validated"); + return ret; +} + +#endif // SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP diff --git a/src/hotspot/share/runtime/vm_operations.cpp b/src/hotspot/share/runtime/vm_operations.cpp index e9ad8af4314..9e5608f6f06 100644 --- a/src/hotspot/share/runtime/vm_operations.cpp +++ b/src/hotspot/share/runtime/vm_operations.cpp @@ -38,6 +38,7 @@ #include "runtime/interfaceSupport.hpp" #include "runtime/sweeper.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.inline.hpp" #include "runtime/vm_operations.hpp" #include "services/threadService.hpp" #include "trace/tracing.hpp" @@ -96,11 +97,12 @@ void VM_Operation::print_on_error(outputStream* st) const { void VM_ThreadStop::doit() { assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); + ThreadsListHandle tlh; JavaThread* target = java_lang_Thread::thread(target_thread()); // Note that this now allows multiple ThreadDeath exceptions to be // thrown at a thread. - if (target != NULL) { - // the thread has run and is not already in the process of exiting + if (target != NULL && (!EnableThreadSMRExtraValidityChecks || tlh.includes(target))) { + // The target thread has run and has not exited yet. target->send_thread_stop(throwable()); } } @@ -146,9 +148,10 @@ void VM_DeoptimizeFrame::doit() { void VM_DeoptimizeAll::doit() { DeoptimizationMarker dm; + JavaThreadIteratorWithHandle jtiwh; // deoptimize all java threads in the system if (DeoptimizeALot) { - for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (; JavaThread *thread = jtiwh.next(); ) { if (thread->has_last_Java_frame()) { thread->deoptimize(); } @@ -159,7 +162,7 @@ void VM_DeoptimizeAll::doit() { int tnum = os::random() & 0x3; int fnum = os::random() & 0x3; int tcount = 0; - for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) { + for (; JavaThread *thread = jtiwh.next(); ) { if (thread->has_last_Java_frame()) { if (tcount++ == tnum) { tcount = 0; @@ -259,12 +262,19 @@ bool VM_FindDeadlocks::doit_prologue() { } void VM_FindDeadlocks::doit() { - _deadlocks = ThreadService::find_deadlocks_at_safepoint(_concurrent_locks); + // Update the hazard ptr in the originating thread to the current + // list of threads. This VM operation needs the current list of + // threads for proper deadlock detection and those are the + // JavaThreads we need to be protected when we return info to the + // originating thread. + _setter.set(); + + _deadlocks = ThreadService::find_deadlocks_at_safepoint(_setter.list(), _concurrent_locks); if (_out != NULL) { int num_deadlocks = 0; for (DeadlockCycle* cycle = _deadlocks; cycle != NULL; cycle = cycle->next()) { num_deadlocks++; - cycle->print_on(_out); + cycle->print_on_with(_setter.list(), _out); } if (num_deadlocks == 1) { @@ -331,6 +341,12 @@ void VM_ThreadDump::doit_epilogue() { void VM_ThreadDump::doit() { ResourceMark rm; + // Set the hazard ptr in the originating thread to protect the + // current list of threads. This VM operation needs the current list + // of threads for a proper dump and those are the JavaThreads we need + // to be protected when we return info to the originating thread. + _result->set_t_list(); + ConcurrentLocksDump concurrent_locks(true); if (_with_locked_synchronizers) { concurrent_locks.dump_at_safepoint(); @@ -338,7 +354,9 @@ void VM_ThreadDump::doit() { if (_num_threads == 0) { // Snapshot all live threads - for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + + for (uint i = 0; i < _result->t_list()->length(); i++) { + JavaThread* jt = _result->t_list()->thread_at(i); if (jt->is_exiting() || jt->is_hidden_from_external_view()) { // skip terminating threads and hidden threads @@ -354,6 +372,7 @@ void VM_ThreadDump::doit() { } else { // Snapshot threads in the given _threads array // A dummy snapshot is created if a thread doesn't exist + for (int i = 0; i < _num_threads; i++) { instanceHandle th = _threads->at(i); if (th() == NULL) { @@ -366,6 +385,12 @@ void VM_ThreadDump::doit() { // Dump thread stack only if the thread is alive and not exiting // and not VM internal thread. JavaThread* jt = java_lang_Thread::thread(th()); + if (jt != NULL && !_result->t_list()->includes(jt)) { + // _threads[i] doesn't refer to a valid JavaThread; this check + // is primarily for JVM_DumpThreads() which doesn't have a good + // way to validate the _threads array. + jt = NULL; + } if (jt == NULL || /* thread not alive */ jt->is_exiting() || jt->is_hidden_from_external_view()) { @@ -384,7 +409,7 @@ void VM_ThreadDump::doit() { } ThreadSnapshot* VM_ThreadDump::snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl) { - ThreadSnapshot* snapshot = new ThreadSnapshot(java_thread); + ThreadSnapshot* snapshot = new ThreadSnapshot(_result->t_list(), java_thread); snapshot->dump_stack_at_safepoint(_max_depth, _with_locked_monitors); snapshot->set_concurrent_locks(tcl); return snapshot; @@ -403,11 +428,12 @@ int VM_Exit::set_vm_exited() { _shutdown_thread = thr_cur; _vm_exited = true; // global flag - for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) { if (thr!=thr_cur && thr->thread_state() == _thread_in_native) { ++num_active; thr->set_terminated(JavaThread::_vm_exited); // per-thread flag } + } return num_active; } @@ -435,11 +461,13 @@ int VM_Exit::wait_for_threads_in_native_to_block() { int max_wait = max_wait_compiler_thread; int attempts = 0; + JavaThreadIteratorWithHandle jtiwh; while (true) { int num_active = 0; int num_active_compiler_thread = 0; - for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) { + jtiwh.rewind(); + for (; JavaThread *thr = jtiwh.next(); ) { if (thr!=thr_cur && thr->thread_state() == _thread_in_native) { num_active++; if (thr->is_Compiler_thread()) { diff --git a/src/hotspot/share/runtime/vm_operations.hpp b/src/hotspot/share/runtime/vm_operations.hpp index 4bc2fdd7794..3311ee8be25 100644 --- a/src/hotspot/share/runtime/vm_operations.hpp +++ b/src/hotspot/share/runtime/vm_operations.hpp @@ -392,12 +392,14 @@ class VM_PrintMetadata : public VM_Operation { class DeadlockCycle; class VM_FindDeadlocks: public VM_Operation { private: - bool _concurrent_locks; - DeadlockCycle* _deadlocks; - outputStream* _out; + bool _concurrent_locks; + DeadlockCycle* _deadlocks; + outputStream* _out; + ThreadsListSetter _setter; // Helper to set hazard ptr in the originating thread + // which protects the JavaThreads in _deadlocks. public: - VM_FindDeadlocks(bool concurrent_locks) : _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL) {}; + VM_FindDeadlocks(bool concurrent_locks) : _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL), _setter() {}; VM_FindDeadlocks(outputStream* st) : _concurrent_locks(true), _out(st), _deadlocks(NULL) {}; ~VM_FindDeadlocks(); diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index e931b2ec334..71dbe24f0b5 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -39,6 +39,8 @@ #include "runtime/jniHandles.hpp" #include "runtime/os.hpp" #include "runtime/reflectionUtils.hpp" +#include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" @@ -1895,7 +1897,7 @@ void VM_HeapDumper::dump_stack_traces() { _stack_traces = NEW_C_HEAP_ARRAY(ThreadStackTrace*, Threads::number_of_threads(), mtInternal); int frame_serial_num = 0; - for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { oop threadObj = thread->threadObj(); if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) { // dump thread stack trace diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 2581c3da9ab..8273fec1b2e 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -42,6 +42,7 @@ #include "runtime/os.hpp" #include "runtime/serviceThread.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "services/classLoadingService.hpp" #include "services/diagnosticCommand.hpp" #include "services/diagnosticFramework.hpp" @@ -1025,11 +1026,15 @@ static void do_thread_dump(ThreadDumpResult* dump_result, // First get an array of threadObj handles. // A JavaThread may terminate before we get the stack trace. GrowableArray<instanceHandle>* thread_handle_array = new GrowableArray<instanceHandle>(num_threads); + { - MutexLockerEx ml(Threads_lock); + // Need this ThreadsListHandle for converting Java thread IDs into + // threadObj handles; dump_result->set_t_list() is called in the + // VM op below so we can't use it yet. + ThreadsListHandle tlh; for (int i = 0; i < num_threads; i++) { jlong tid = ids_ah->long_at(i); - JavaThread* jt = Threads::find_java_thread_from_java_tid(tid); + JavaThread* jt = tlh.list()->find_JavaThread_from_java_tid(tid); oop thread_obj = (jt != NULL ? jt->threadObj() : (oop)NULL); instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj); thread_handle_array->append(threadObj_h); @@ -1101,22 +1106,21 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo ThreadDumpResult dump_result(num_threads); if (maxDepth == 0) { - // no stack trace dumped - do not need to stop the world - { - MutexLockerEx ml(Threads_lock); - for (int i = 0; i < num_threads; i++) { - jlong tid = ids_ah->long_at(i); - JavaThread* jt = Threads::find_java_thread_from_java_tid(tid); - ThreadSnapshot* ts; - if (jt == NULL) { - // if the thread does not exist or now it is terminated, - // create dummy snapshot - ts = new ThreadSnapshot(); - } else { - ts = new ThreadSnapshot(jt); - } - dump_result.add_thread_snapshot(ts); + // No stack trace to dump so we do not need to stop the world. + // Since we never do the VM op here we must set the threads list. + dump_result.set_t_list(); + for (int i = 0; i < num_threads; i++) { + jlong tid = ids_ah->long_at(i); + JavaThread* jt = dump_result.t_list()->find_JavaThread_from_java_tid(tid); + ThreadSnapshot* ts; + if (jt == NULL) { + // if the thread does not exist or now it is terminated, + // create dummy snapshot + ts = new ThreadSnapshot(); + } else { + ts = new ThreadSnapshot(dump_result.t_list(), jt); } + dump_result.add_thread_snapshot(ts); } } else { // obtain thread dump with the specific list of threads with stack trace @@ -1131,6 +1135,7 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo int num_snapshots = dump_result.num_snapshots(); assert(num_snapshots == num_threads, "Must match the number of thread snapshots"); + assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot"); int index = 0; for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; index++, ts = ts->next()) { // For each thread, create an java/lang/management/ThreadInfo object @@ -1196,6 +1201,7 @@ JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboo } int num_snapshots = dump_result.num_snapshots(); + assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot"); // create the result ThreadInfo[] object InstanceKlass* ik = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL); @@ -1319,10 +1325,10 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType } // Look for the JavaThread of this given tid - MutexLockerEx ml(Threads_lock); + JavaThreadIteratorWithHandle jtiwh; if (tid == 0) { // reset contention statistics for all threads if tid == 0 - for (JavaThread* java_thread = Threads::first(); java_thread != NULL; java_thread = java_thread->next()) { + for (; JavaThread *java_thread = jtiwh.next(); ) { if (type == JMM_STAT_THREAD_CONTENTION_COUNT) { ThreadService::reset_contention_count_stat(java_thread); } else { @@ -1331,7 +1337,7 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType } } else { // reset contention statistics for a given thread - JavaThread* java_thread = Threads::find_java_thread_from_java_tid(tid); + JavaThread* java_thread = jtiwh.list()->find_JavaThread_from_java_tid(tid); if (java_thread == NULL) { return false; } @@ -1399,8 +1405,8 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTime(JNIEnv *env, jlong thread_id)) // current thread return os::current_thread_cpu_time(); } else { - MutexLockerEx ml(Threads_lock); - java_thread = Threads::find_java_thread_from_java_tid(thread_id); + ThreadsListHandle tlh; + java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id); if (java_thread != NULL) { return os::thread_cpu_time((Thread*) java_thread); } @@ -1649,6 +1655,7 @@ ThreadTimesClosure::ThreadTimesClosure(objArrayHandle names, // Called with Threads_lock held // void ThreadTimesClosure::do_thread(Thread* thread) { + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); assert(thread != NULL, "thread was NULL"); // exclude externally visible JavaThreads @@ -2109,9 +2116,9 @@ JVM_ENTRY(void, jmm_GetThreadAllocatedMemory(JNIEnv *env, jlongArray ids, "the given array of thread IDs"); } - MutexLockerEx ml(Threads_lock); + ThreadsListHandle tlh; for (int i = 0; i < num_threads; i++) { - JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i)); + JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i)); if (java_thread != NULL) { sizeArray_h->long_at_put(i, java_thread->cooked_allocated_bytes()); } @@ -2138,8 +2145,8 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTimeWithKind(JNIEnv *env, jlong thread_id, jboo // current thread return os::current_thread_cpu_time(user_sys_cpu_time != 0); } else { - MutexLockerEx ml(Threads_lock); - java_thread = Threads::find_java_thread_from_java_tid(thread_id); + ThreadsListHandle tlh; + java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id); if (java_thread != NULL) { return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0); } @@ -2180,9 +2187,9 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids, "the given array of thread IDs"); } - MutexLockerEx ml(Threads_lock); + ThreadsListHandle tlh; for (int i = 0; i < num_threads; i++) { - JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i)); + JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i)); if (java_thread != NULL) { timeArray_h->long_at_put(i, os::thread_cpu_time((Thread*)java_thread, user_sys_cpu_time != 0)); diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index da25120bb37..9a1a4ae393d 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -34,9 +34,9 @@ #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" -#include "runtime/thread.hpp" -#include "runtime/vframe.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.inline.hpp" +#include "runtime/vframe.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" #include "services/threadService.hpp" @@ -148,7 +148,7 @@ void ThreadService::current_thread_exiting(JavaThread* jt) { // FIXME: JVMTI should call this function Handle ThreadService::get_current_contended_monitor(JavaThread* thread) { assert(thread != NULL, "should be non-NULL"); - assert(Threads_lock->owned_by_self(), "must grab Threads_lock or be at safepoint"); + debug_only(Thread::check_for_dangling_thread_pointer(thread);) ObjectMonitor *wait_obj = thread->current_waiting_monitor(); @@ -266,6 +266,7 @@ Handle ThreadService::dump_stack_traces(GrowableArray<instanceHandle>* threads, int num_snapshots = dump_result.num_snapshots(); assert(num_snapshots == num_threads, "Must have num_threads thread snapshots"); + assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot"); int i = 0; for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; i++, ts = ts->next()) { ThreadStackTrace* stacktrace = ts->get_stack_trace(); @@ -297,7 +298,9 @@ void ThreadService::reset_contention_time_stat(JavaThread* thread) { } // Find deadlocks involving object monitors and concurrent locks if concurrent_locks is true -DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) { +DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list, bool concurrent_locks) { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + // This code was modified from the original Threads::find_deadlocks code. int globalDfn = 0, thisDfn; ObjectMonitor* waitingToLockMonitor = NULL; @@ -306,15 +309,16 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) JavaThread *currentThread, *previousThread; int num_deadlocks = 0; - for (JavaThread* p = Threads::first(); p != NULL; p = p->next()) { - // Initialize the depth-first-number - p->set_depth_first_number(-1); + // Initialize the depth-first-number for each JavaThread. + JavaThreadIterator jti(t_list); + for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) { + jt->set_depth_first_number(-1); } DeadlockCycle* deadlocks = NULL; DeadlockCycle* last = NULL; DeadlockCycle* cycle = new DeadlockCycle(); - for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) { if (jt->depth_first_number() >= 0) { // this thread was already visited continue; @@ -339,9 +343,8 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) if (waitingToLockMonitor != NULL) { address currentOwner = (address)waitingToLockMonitor->owner(); if (currentOwner != NULL) { - currentThread = Threads::owning_thread_from_monitor_owner( - currentOwner, - false /* no locking needed */); + currentThread = Threads::owning_thread_from_monitor_owner(t_list, + currentOwner); if (currentThread == NULL) { // This function is called at a safepoint so the JavaThread // that owns waitingToLockMonitor should be findable, but @@ -366,6 +369,8 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) if (concurrent_locks) { if (waitingToLockBlocker->is_a(SystemDictionary::abstract_ownable_synchronizer_klass())) { oop threadObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker); + // This JavaThread (if there is one) is protected by the + // ThreadsListSetter in VM_FindDeadlocks::doit(). currentThread = threadObj != NULL ? java_lang_Thread::thread(threadObj) : NULL; } else { currentThread = NULL; @@ -414,7 +419,7 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) return deadlocks; } -ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) { +ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() { // Create a new ThreadDumpResult object and append to the list. // If GC happens before this function returns, Method* @@ -422,7 +427,7 @@ ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snap ThreadService::add_thread_dump(this); } -ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) { +ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() { // Create a new ThreadDumpResult object and append to the list. // If GC happens before this function returns, oops // will be visited. @@ -467,6 +472,10 @@ void ThreadDumpResult::metadata_do(void f(Metadata*)) { } } +ThreadsList* ThreadDumpResult::t_list() { + return _setter.list(); +} + StackFrameInfo::StackFrameInfo(javaVFrame* jvf, bool with_lock_info) { _method = jvf->method(); _bci = jvf->bci(); @@ -683,6 +692,8 @@ void ConcurrentLocksDump::build_map(GrowableArray<oop>* aos_objects) { oop o = aos_objects->at(i); oop owner_thread_obj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(o); if (owner_thread_obj != NULL) { + // See comments in ThreadConcurrentLocks to see how this + // JavaThread* is protected. JavaThread* thread = java_lang_Thread::thread(owner_thread_obj); assert(o->is_instance(), "Must be an instanceOop"); add_lock(thread, (instanceOop) o); @@ -764,7 +775,7 @@ ThreadStatistics::ThreadStatistics() { memset((void*) _perf_recursion_counts, 0, sizeof(_perf_recursion_counts)); } -ThreadSnapshot::ThreadSnapshot(JavaThread* thread) { +ThreadSnapshot::ThreadSnapshot(ThreadsList * t_list, JavaThread* thread) { _thread = thread; _threadObj = thread->threadObj(); _stack_trace = NULL; @@ -796,7 +807,7 @@ ThreadSnapshot::ThreadSnapshot(JavaThread* thread) { _thread_status = java_lang_Thread::RUNNABLE; } else { _blocker_object = obj(); - JavaThread* owner = ObjectSynchronizer::get_lock_owner(obj, false); + JavaThread* owner = ObjectSynchronizer::get_lock_owner(t_list, obj); if ((owner == NULL && _thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) || (owner != NULL && owner->is_attaching_via_jni())) { // ownership information of the monitor is not available @@ -865,7 +876,7 @@ DeadlockCycle::~DeadlockCycle() { delete _threads; } -void DeadlockCycle::print_on(outputStream* st) const { +void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const { st->cr(); st->print_cr("Found one Java-level deadlock:"); st->print("============================="); @@ -895,9 +906,8 @@ void DeadlockCycle::print_on(outputStream* st) const { // No Java object associated - a JVMTI raw monitor owner_desc = " (JVMTI raw monitor),\n which is held by"; } - currentThread = Threads::owning_thread_from_monitor_owner( - (address)waitingToLockMonitor->owner(), - false /* no locking needed */); + currentThread = Threads::owning_thread_from_monitor_owner(t_list, + (address)waitingToLockMonitor->owner()); if (currentThread == NULL) { // The deadlock was detected at a safepoint so the JavaThread // that owns waitingToLockMonitor should be findable, but @@ -915,6 +925,7 @@ void DeadlockCycle::print_on(outputStream* st) const { "Must be an AbstractOwnableSynchronizer"); oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker); currentThread = java_lang_Thread::thread(ownerObj); + assert(currentThread != NULL, "AbstractOwnableSynchronizer owning thread is unexpectedly NULL"); } st->print("%s \"%s\"", owner_desc, currentThread->get_thread_name()); } @@ -943,9 +954,7 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread, int init_size = ThreadService::get_live_thread_count(); _threads_array = new GrowableArray<instanceHandle>(init_size); - MutexLockerEx ml(Threads_lock); - - for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) { + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { // skips JavaThreads in the process of exiting // and also skips VM internal JavaThreads // Threads in _thread_new or _thread_new_trans state are included. diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp index 46bc012f73e..950482e462d 100644 --- a/src/hotspot/share/services/threadService.hpp +++ b/src/hotspot/share/services/threadService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -32,6 +32,7 @@ #include "runtime/objectMonitor.hpp" #include "runtime/objectMonitor.inline.hpp" #include "runtime/perfData.hpp" +#include "runtime/thread.hpp" #include "services/management.hpp" #include "services/serviceUtil.hpp" @@ -109,7 +110,7 @@ public: static void reset_contention_count_stat(JavaThread* thread); static void reset_contention_time_stat(JavaThread* thread); - static DeadlockCycle* find_deadlocks_at_safepoint(bool object_monitors_only); + static DeadlockCycle* find_deadlocks_at_safepoint(ThreadsList * t_list, bool object_monitors_only); // GC support static void oops_do(OopClosure* f); @@ -189,6 +190,8 @@ public: // Thread snapshot to represent the thread state and statistics class ThreadSnapshot : public CHeapObj<mtInternal> { private: + // This JavaThread* is protected by being stored in objects that are + // protected by a ThreadsListSetter (ThreadDumpResult). JavaThread* _thread; oop _threadObj; java_lang_Thread::ThreadStatus _thread_status; @@ -213,7 +216,7 @@ public: // Dummy snapshot ThreadSnapshot() : _thread(NULL), _threadObj(NULL), _stack_trace(NULL), _concurrent_locks(NULL), _next(NULL), _blocker_object(NULL), _blocker_object_owner(NULL) {}; - ThreadSnapshot(JavaThread* thread); + ThreadSnapshot(ThreadsList * t_list, JavaThread* thread); ~ThreadSnapshot(); java_lang_Thread::ThreadStatus thread_status() { return _thread_status; } @@ -310,6 +313,12 @@ class ThreadConcurrentLocks : public CHeapObj<mtInternal> { private: GrowableArray<instanceOop>* _owned_locks; ThreadConcurrentLocks* _next; + // This JavaThread* is protected in one of two different ways + // depending on the usage of the ThreadConcurrentLocks object: + // 1) by being stored in objects that are only allocated and used at a + // safepoint (ConcurrentLocksDump), or 2) by being stored in objects + // that are protected by a ThreadsListSetter (ThreadSnapshot inside + // ThreadDumpResult). JavaThread* _thread; public: ThreadConcurrentLocks(JavaThread* thread); @@ -333,8 +342,12 @@ class ConcurrentLocksDump : public StackObj { void add_lock(JavaThread* thread, instanceOop o); public: - ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {}; - ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {}; + ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint."); + }; + ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint."); + }; ~ConcurrentLocksDump(); void dump_at_safepoint(); @@ -349,6 +362,9 @@ class ThreadDumpResult : public StackObj { ThreadSnapshot* _snapshots; ThreadSnapshot* _last; ThreadDumpResult* _next; + ThreadsListSetter _setter; // Helper to set hazard ptr in the originating thread + // which protects the JavaThreads in _snapshots. + public: ThreadDumpResult(); ThreadDumpResult(int num_threads); @@ -360,6 +376,9 @@ class ThreadDumpResult : public StackObj { int num_threads() { return _num_threads; } int num_snapshots() { return _num_snapshots; } ThreadSnapshot* snapshots() { return _snapshots; } + void set_t_list() { _setter.set(); } + ThreadsList* t_list(); + bool t_list_has_been_set() { return _setter.target_needs_release(); } void oops_do(OopClosure* f); void metadata_do(void f(Metadata*)); }; @@ -381,7 +400,7 @@ class DeadlockCycle : public CHeapObj<mtInternal> { bool is_deadlock() { return _is_deadlock; } int num_threads() { return _threads->length(); } GrowableArray<JavaThread*>* threads() { return _threads; } - void print_on(outputStream* st) const; + void print_on_with(ThreadsList * t_list, outputStream* st) const; }; // Utility class to get list of java threads. diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 580fc86e89e..8d127605e13 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -36,6 +36,7 @@ #include "runtime/init.hpp" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" #include "runtime/vm_version.hpp" @@ -1655,7 +1656,12 @@ void VMError::controlled_crash(int how) { char * const dataPtr = NULL; // bad data pointer const void (*funcPtr)(void) = (const void(*)()) 0xF; // bad function pointer - // Keep this in sync with test/runtime/ErrorHandling/ErrorHandler.java + // Keep this in sync with test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java + // which tests cases 1 thru 13. + // Case 14 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SafeFetchInErrorHandlingTest.java. + // Case 15 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java. + // Case 16 is tested by test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java. + // Case 17 is tested by test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java. switch (how) { case 1: vmassert(str == NULL, "expected null"); case 2: vmassert(num == 1023 && *str == 'X', @@ -1683,6 +1689,17 @@ void VMError::controlled_crash(int how) { case 13: (*funcPtr)(); break; case 14: crash_with_segfault(); break; case 15: crash_with_sigfpe(); break; + case 16: { + ThreadsListHandle tlh; + fatal("Force crash with an active ThreadsListHandle."); + } + case 17: { + ThreadsListHandle tlh; + { + ThreadsListHandle tlh2; + fatal("Force crash with a nested ThreadsListHandle."); + } + } default: tty->print_cr("ERROR: %d: unexpected test_num value.", how); } diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java index 26882f59d61..d820809a791 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -23,6 +23,7 @@ /* * @test + * @requires (vm.debug == true) * @bug 6888954 * @bug 8015884 * @summary Exercise HotSpot error handling code by invoking java with @@ -39,6 +40,7 @@ import jdk.test.lib.process.OutputAnalyzer; public class ErrorHandler { public static OutputAnalyzer runTest(int testcase) throws Exception { + // The -XX:ErrorHandlerTest=N option requires debug bits. return new OutputAnalyzer( ProcessTools.createJavaProcessBuilder( "-XX:-TransmitErrorReport", "-XX:-CreateCoredumpOnCrash", "-XX:ErrorHandlerTest=" + testcase) @@ -46,10 +48,6 @@ public class ErrorHandler { } public static void main(String[] args) throws Exception { - // Test is only applicable for debug builds - if (!Platform.isDebugBuild()) { - return; - } // Keep this in sync with hotspot/src/share/vm/utilities/debug.cpp int i = 1; String[] strings = { @@ -69,6 +67,10 @@ public class ErrorHandler { String[] patterns = { "(SIGILL|SIGSEGV|EXCEPTION_ACCESS_VIOLATION).* at pc=", "(SIGBUS|SIGSEGV|SIGILL|EXCEPTION_ACCESS_VIOLATION).* at pc=" + // -XX:ErrorHandlerTest=14 is tested by SafeFetchInErrorHandlingTest.java + // -XX:ErrorHandlerTest=15 is tested by SecondaryErrorTest.java + // -XX:ErrorHandlerTest=16 is tested by ThreadsListHandleInErrorHandlingTest.java + // -XX:ErrorHandlerTest=17 is tested by NestedThreadsListHandleInErrorHandlingTest.java }; for (String s : strings) { diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java new file mode 100644 index 00000000000..c0f2e0b7b2b --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @requires (vm.debug == true) + * @bug 8167108 + * @summary Nested ThreadsListHandle info should be in error handling output. + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics NestedThreadsListHandleInErrorHandlingTest + */ + +/* + * This test was created using SafeFetchInErrorHandlingTest.java + * as a guide. + */ +public class NestedThreadsListHandleInErrorHandlingTest { + public static void main(String[] args) throws Exception { + + // The -XX:ErrorHandlerTest=N option requires debug bits. + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-Xmx100M", + "-XX:ErrorHandlerTest=17", + "-XX:-CreateCoredumpOnCrash", + "-version"); + + OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); + + // We should have crashed with a specific fatal error: + output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + System.out.println("Found fatal error header."); + output_detail.shouldMatch("# +fatal error: Force crash with a nested ThreadsListHandle."); + System.out.println("Found specific fatal error."); + + // Extract hs_err_pid file. + String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1); + if (hs_err_file == null) { + throw new RuntimeException("Did not find hs_err_pid file in output.\n"); + } + + File f = new File(hs_err_file); + if (!f.exists()) { + throw new RuntimeException("hs_err_pid file missing at " + + f.getAbsolutePath() + ".\n"); + } + + System.out.println("Found hs_err_pid file. Scanning..."); + + FileInputStream fis = new FileInputStream(f); + BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + String line = null; + + Pattern [] pattern = new Pattern[] { + // The "Current thread" line should show a hazard ptr and + // a nested hazard ptr: + Pattern.compile("Current thread .* _threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"), + // We should have a section of Threads class SMR info: + Pattern.compile("Threads class SMR info:"), + // We should have one nested ThreadsListHandle: + Pattern.compile(".*, _smr_nested_thread_list_max=1"), + // The current thread (marked with '=>') in the threads list + // should show a hazard ptr: + Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"), + }; + int currentPattern = 0; + + String lastLine = null; + while ((line = br.readLine()) != null) { + if (currentPattern < pattern.length) { + if (pattern[currentPattern].matcher(line).matches()) { + System.out.println("Found: " + line + "."); + currentPattern++; + } + } + lastLine = line; + } + br.close(); + + if (currentPattern < pattern.length) { + throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")"); + } + + if (!lastLine.equals("END.")) { + throw new RuntimeException("hs-err file incomplete (missing END marker.)"); + } else { + System.out.println("End marker found."); + } + + System.out.println("Done scanning hs_err_pid_file."); + System.out.println("PASSED."); + } +} diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java new file mode 100644 index 00000000000..52d9851a5bb --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @requires (vm.debug == true) + * @bug 8167108 + * @summary ThreadsListHandle info should be in error handling output. + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics ThreadsListHandleInErrorHandlingTest + */ + +/* + * This test was created using SafeFetchInErrorHandlingTest.java + * as a guide. + */ +public class ThreadsListHandleInErrorHandlingTest { + public static void main(String[] args) throws Exception { + + // The -XX:ErrorHandlerTest=N option requires debug bits. + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-Xmx100M", + "-XX:ErrorHandlerTest=16", + "-XX:-CreateCoredumpOnCrash", + "-version"); + + OutputAnalyzer output_detail = new OutputAnalyzer(pb.start()); + + // We should have crashed with a specific fatal error: + output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + System.out.println("Found fatal error header."); + output_detail.shouldMatch("# +fatal error: Force crash with an active ThreadsListHandle."); + System.out.println("Found specific fatal error."); + + // Extract hs_err_pid file. + String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1); + if (hs_err_file == null) { + throw new RuntimeException("Did not find hs_err_pid file in output.\n"); + } + + File f = new File(hs_err_file); + if (!f.exists()) { + throw new RuntimeException("hs_err_pid file missing at " + + f.getAbsolutePath() + ".\n"); + } + + System.out.println("Found hs_err_pid file. Scanning..."); + + FileInputStream fis = new FileInputStream(f); + BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + String line = null; + + Pattern [] pattern = new Pattern[] { + // The "Current thread" line should show a hazard ptr: + Pattern.compile("Current thread .* _threads_hazard_ptr=0x.*"), + // We should have a section of Threads class SMR info: + Pattern.compile("Threads class SMR info:"), + // The current thread (marked with '=>') in the threads list + // should show a hazard ptr: + Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x.*"), + }; + int currentPattern = 0; + + String lastLine = null; + while ((line = br.readLine()) != null) { + if (currentPattern < pattern.length) { + if (pattern[currentPattern].matcher(line).matches()) { + System.out.println("Found: " + line + "."); + currentPattern++; + } + } + lastLine = line; + } + br.close(); + + if (currentPattern < pattern.length) { + throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " + currentPattern + ")"); + } + + if (!lastLine.equals("END.")) { + throw new RuntimeException("hs-err file incomplete (missing END marker.)"); + } else { + System.out.println("End marker found."); + } + + System.out.println("Done scanning hs_err_pid_file."); + System.out.println("PASSED."); + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java b/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java new file mode 100644 index 00000000000..af9f6453836 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.countStackFrames() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug CountStackFramesAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class CountStackFramesAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 1000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + CountStackFramesAtExit threads[] = new CountStackFramesAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new CountStackFramesAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out of + // the exitSyncObj.await() call and the countStackFrames() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + try { + threads[i].countStackFrames(); + } catch (IllegalThreadStateException itse) { + // ignore because we expect it + } + + if (!threads[i].isAlive()) { + // Done with Thread.countStackFrames() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.countStackFrames()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.countStackFrames() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].countStackFrames(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java b/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java new file mode 100644 index 00000000000..e230bf4ef61 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.interrupt() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug InterruptAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class InterruptAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 1000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + InterruptAtExit threads[] = new InterruptAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new InterruptAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // The first interrupt() call will break the + // worker out of the exitSyncObj.await() call + // and the rest will come in during thread exit. + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].interrupt(); + + if (!threads[i].isAlive()) { + // Done with Thread.interrupt() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.interrupt()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.interrupt() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].interrupt(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java b/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java new file mode 100644 index 00000000000..b775636e0a9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.isInterrupted() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug IsInterruptedAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class IsInterruptedAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 2000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + IsInterruptedAtExit threads[] = new IsInterruptedAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new IsInterruptedAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out of + // the exitSyncObj.await() call and the isInterrupted() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].isInterrupted(); + + if (!threads[i].isAlive()) { + // Done with Thread.isInterrupted() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.isInterrupted()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.isInterrupted() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].isInterrupted(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java b/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java new file mode 100644 index 00000000000..d8a0dbc9f9b --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.resume() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug ResumeAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class ResumeAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 2000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + ResumeAtExit threads[] = new ResumeAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new ResumeAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out + // of the exitSyncObj.await() call and the resume() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].resume(); + + if (!threads[i].isAlive()) { + // Done with Thread.resume() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.resume()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.resume() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].resume(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java b/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java new file mode 100644 index 00000000000..f6dd8c72cc9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.setName() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug SetNameAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class SetNameAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 1000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + SetNameAtExit threads[] = new SetNameAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new SetNameAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out + // of the exitSyncObj.await() call and the setName() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].setName("T" + i + "-" + late_count); + + if (!threads[i].isAlive()) { + // Done with Thread.setName() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.setName()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.setName() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].setName("T" + i + "-done"); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java b/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java new file mode 100644 index 00000000000..68589056417 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.setPriority() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug SetPriorityAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class SetPriorityAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 2000; + + final static int MIN = java.lang.Thread.MIN_PRIORITY; + final static int NORM = java.lang.Thread.NORM_PRIORITY; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + SetPriorityAtExit threads[] = new SetPriorityAtExit[N_THREADS]; + + int prio = MIN; + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new SetPriorityAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out of + // the exitSyncObj.await() call and the setPriority() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].setPriority(prio); + if (prio == MIN) { + prio = NORM; + } else { + prio = MIN; + } + + if (!threads[i].isAlive()) { + // Done with Thread.setPriority() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.setPriority()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.setPriority() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].setPriority(prio); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/StopAtExit.java b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java new file mode 100644 index 00000000000..3d4306a2da3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.stop() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug StopAtExit + */ + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class StopAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 1000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + try { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } catch (ThreadDeath td) { + // ignore because we're testing Thread.stop() which throws it + } catch (NoClassDefFoundError ncdfe) { + // ignore because we're testing Thread.stop() which can cause it + } + } + + public static void main(String[] args) { + StopAtExit threads[] = new StopAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new StopAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out + // of the exitSyncObj.await() call and the stop() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].stop(); + + if (!threads[i].isAlive()) { + // Done with Thread.stop() calls since + // thread is not alive. + break; + } + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } catch (NoClassDefFoundError ncdfe) { + // Ignore because we're testing Thread.stop() which can + // cause it. Yes, a NoClassDefFoundError that happens + // in a worker thread can subsequently be seen in the + // main thread. + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.stop()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.stop() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].stop(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java b/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java new file mode 100644 index 00000000000..05c1fb4dd2a --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Stress test java.lang.Thread.suspend() at thread exit. + * @run main/othervm -Xlog:thread+smr=debug SuspendAtExit + */ + +import java.util.concurrent.CountDownLatch; + +public class SuspendAtExit extends Thread { + final static int N_THREADS = 32; + final static int N_LATE_CALLS = 10000; + + public CountDownLatch exitSyncObj = new CountDownLatch(1); + public CountDownLatch startSyncObj = new CountDownLatch(1); + + @Override + public void run() { + // Tell main thread we have started. + startSyncObj.countDown(); + try { + // Wait for main thread to interrupt us so we + // can race to exit. + exitSyncObj.await(); + } catch (InterruptedException e) { + // ignore because we expect one + } + } + + public static void main(String[] args) { + SuspendAtExit threads[] = new SuspendAtExit[N_THREADS]; + + for (int i = 0; i < N_THREADS; i++ ) { + threads[i] = new SuspendAtExit(); + int late_count = 1; + threads[i].start(); + try { + // Wait for the worker thread to get going. + threads[i].startSyncObj.await(); + + // This interrupt() call will break the worker out + // of the exitSyncObj.await() call and the suspend() + // calls will come in during thread exit. + threads[i].interrupt(); + for (; late_count <= N_LATE_CALLS; late_count++) { + threads[i].suspend(); + + if (!threads[i].isAlive()) { + // Done with Thread.suspend() calls since + // thread is not alive. + break; + } + threads[i].resume(); + } + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + + System.out.println("INFO: thread #" + i + ": made " + late_count + + " late calls to java.lang.Thread.suspend()"); + System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" + + N_LATE_CALLS + " value is " + + ((late_count >= N_LATE_CALLS) ? "NOT " : "") + + "large enough to cause a Thread.suspend() " + + "call after thread exit."); + + try { + threads[i].join(); + } catch (InterruptedException e) { + throw new Error("Unexpected: " + e); + } + threads[i].suspend(); + threads[i].resume(); + if (threads[i].isAlive()) { + throw new Error("Expected !Thread.isAlive() after thread #" + + i + " has been join()'ed"); + } + } + + String cmd = System.getProperty("sun.java.command"); + if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) { + // Exit with success in a non-JavaTest environment: + System.exit(0); + } + } +} diff --git a/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java b/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java new file mode 100644 index 00000000000..02b673bc299 --- /dev/null +++ b/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, 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 8167108 + * @summary Checks whether jstack reports a "Threads class SMR info" section. + * + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics TestThreadDumpSMRInfo + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.JDKToolFinder; + +public class TestThreadDumpSMRInfo { + // jstack tends to be closely bound to the VM that we are running + // so use getTestJDKTool() instead of getCompileJDKTool() or even + // getJDKTool() which can fall back to "compile.jdk". + final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack"); + final static String PID = "" + ProcessHandle.current().pid(); + + // Here's a sample "Threads class SMR info" section: + // + // Threads class SMR info: + // _smr_java_thread_list=0x0000000000ce8da0, length=23, elements={ + // 0x000000000043a800, 0x0000000000aee800, 0x0000000000b06800, 0x0000000000b26000, + // 0x0000000000b28800, 0x0000000000b2b000, 0x0000000000b2e000, 0x0000000000b30000, + // 0x0000000000b32800, 0x0000000000b35000, 0x0000000000b3f000, 0x0000000000b41800, + // 0x0000000000b44000, 0x0000000000b46800, 0x0000000000b48800, 0x0000000000b53000, + // 0x0000000000b55800, 0x0000000000b57800, 0x0000000000b5a000, 0x0000000000b5c800, + // 0x0000000000cc8800, 0x0000000000fd9800, 0x0000000000ef4800 + // } + // _smr_java_thread_list_alloc_cnt=24, _smr_java_thread_list_free_cnt=23, _smr_java_thread_list_max=23, _smr_nested_thread_list_max=0 + // _smr_delete_lock_wait_cnt=0, _smr_delete_lock_wait_max=0 + // _smr_to_delete_list_cnt=0, _smr_to_delete_list_max=1 + + final static String HEADER_STR = "Threads class SMR info:"; + + static boolean verbose = false; + + public static void main(String[] args) throws Exception { + if (args.length != 0) { + int arg_i = 0; + if (args[arg_i].equals("-v")) { + verbose = true; + arg_i++; + } + } + + ProcessBuilder pb = new ProcessBuilder(JSTACK, PID); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (verbose) { + System.out.println("stdout: " + output.getStdout()); + } + + output.shouldHaveExitValue(0); + System.out.println("INFO: jstack ran successfully."); + + output.shouldContain(HEADER_STR); + System.out.println("INFO: Found: '" + HEADER_STR + "' in jstack output."); + + System.out.println("Test PASSED."); + } + + static void usage() { + System.err.println("Usage: java TestThreadDumpSMRInfo [-v]"); + System.exit(1); + } +} diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java index 1da94483cf8..6a6b16b9e91 100644 --- a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java @@ -42,22 +42,18 @@ public class HandshakeWalkExitTest implements Runnable { } static volatile boolean exit_now = false; - static Thread[] threads; public static void main(String... args) throws Exception { - int testRuns = 100; - int testThreads = 500; + int testRuns = 20; + int testThreads = 128; HandshakeWalkExitTest test = new HandshakeWalkExitTest(); - threads = new Thread[64]; - Runnable hser = new Runnable(){ public void run(){ WhiteBox wb = WhiteBox.getWhiteBox(); while(!exit_now) { wb.handshakeWalkStack(null, true); - try { Thread.sleep(1); } catch(Exception e) {} } } }; diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java new file mode 100644 index 00000000000..45af7e0b95f --- /dev/null +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 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 HandshakeWalkOneExitTest + * @summary This test tries to stress the handshakes with new and exiting threads + * @library /testlibrary /test/lib + * @build HandshakeWalkOneExitTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkOneExitTest + */ + +import jdk.test.lib.Asserts; +import sun.hotspot.WhiteBox; + +public class HandshakeWalkOneExitTest implements Runnable { + + @Override + public void run() { + } + + static volatile boolean exit_now = false; + static Thread[] threads; + + public static void main(String... args) throws Exception { + int testRuns = 20; + int testThreads = 128; + + HandshakeWalkOneExitTest test = new HandshakeWalkOneExitTest(); + + Runnable hser = new Runnable(){ + public void run(){ + WhiteBox wb = WhiteBox.getWhiteBox(); + while(!exit_now) { + Thread[] t = threads; + for (int i = 0; i<t.length ; i++) { + wb.handshakeWalkStack(t[i], false); + } + } + } + }; + Thread hst = new Thread(hser); + for (int k = 0; k<testRuns ; k++) { + threads = new Thread[testThreads]; + for (int i = 0; i<threads.length ; i++) { + threads[i] = new Thread(test); + threads[i].start(); + } + if (k == 0) { + hst.start(); + } + } + exit_now = true; + hst.join(); + } +} From d21c547b0e1df480a6d940d9029c12d66cb7a989 Mon Sep 17 00:00:00 2001 From: Stefan Johansson <sjohanss@openjdk.org> Date: Thu, 23 Nov 2017 09:53:53 +0100 Subject: [PATCH 013/165] 8189733: Cleanup Full GC setup and tear down Reviewed-by: tschatzl, ehelin --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 37 ++----------- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- src/hotspot/share/gc/g1/g1FullCollector.cpp | 61 +++++++++++++++------ src/hotspot/share/gc/g1/g1FullCollector.hpp | 18 +++--- 4 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index a62a48eeac5..467489b863b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -39,7 +39,6 @@ #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1FullCollector.hpp" -#include "gc/g1/g1FullGCScope.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapSizingPolicy.hpp" #include "gc/g1/g1HeapTransition.hpp" @@ -1217,34 +1216,6 @@ void G1CollectedHeap::print_heap_after_full_collection(G1HeapTransition* heap_tr #endif } -void G1CollectedHeap::do_full_collection_inner(G1FullGCScope* scope) { - GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true); - g1_policy()->record_full_collection_start(); - - print_heap_before_gc(); - print_heap_regions(); - - abort_concurrent_cycle(); - verify_before_full_collection(scope->is_explicit_gc()); - - gc_prologue(true); - prepare_heap_for_full_collection(); - - G1FullCollector collector(scope, ref_processor_stw(), concurrent_mark()->next_mark_bitmap(), workers()->active_workers()); - collector.prepare_collection(); - collector.collect(); - collector.complete_collection(); - - prepare_heap_for_mutators(); - - g1_policy()->record_full_collection_end(); - gc_epilogue(true); - - verify_after_full_collection(); - - print_heap_after_full_collection(scope->heap_transition()); -} - bool G1CollectedHeap::do_full_collection(bool explicit_gc, bool clear_all_soft_refs) { assert_at_safepoint(true /* should_be_vm_thread */); @@ -1257,8 +1228,12 @@ bool G1CollectedHeap::do_full_collection(bool explicit_gc, const bool do_clear_all_soft_refs = clear_all_soft_refs || collector_policy()->should_clear_all_soft_refs(); - G1FullGCScope scope(explicit_gc, do_clear_all_soft_refs); - do_full_collection_inner(&scope); + G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs); + GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true); + + collector.prepare_collection(); + collector.collect(); + collector.complete_collection(); // Full collection was successfully completed. return true; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7467a5493f9..5c2109ede03 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -126,6 +126,7 @@ class G1CollectedHeap : public CollectedHeap { friend class VM_G1IncCollectionPause; friend class VMStructs; friend class MutatorAllocRegion; + friend class G1FullCollector; friend class G1GCAllocRegion; friend class G1HeapVerifier; @@ -517,7 +518,6 @@ protected: private: // Internal helpers used during full GC to split it up to // increase readability. - void do_full_collection_inner(G1FullGCScope* scope); void abort_concurrent_cycle(); void verify_before_full_collection(bool explicit_gc); void prepare_heap_for_full_collection(); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 81ec9a05e25..e47b02f2efb 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -35,6 +35,7 @@ #include "gc/g1/g1FullGCReferenceProcessorExecutor.hpp" #include "gc/g1/g1FullGCScope.hpp" #include "gc/g1/g1OopClosures.hpp" +#include "gc/g1/g1Policy.hpp" #include "gc/g1/g1StringDedup.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/preservedMarks.hpp" @@ -62,20 +63,24 @@ static void update_derived_pointers() { #endif } -G1FullCollector::G1FullCollector(G1FullGCScope* scope, - ReferenceProcessor* reference_processor, - G1CMBitMap* bitmap, - uint workers) : - _scope(scope), - _num_workers(workers), - _mark_bitmap(bitmap), +G1CMBitMap* G1FullCollector::mark_bitmap() { + return _heap->concurrent_mark()->next_mark_bitmap(); +} + +ReferenceProcessor* G1FullCollector::reference_processor() { + return _heap->ref_processor_stw(); +} + +G1FullCollector::G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs) : + _heap(heap), + _scope(explicit_gc, clear_soft_refs), + _num_workers(heap->workers()->active_workers()), _oop_queue_set(_num_workers), _array_queue_set(_num_workers), _preserved_marks_set(true), - _reference_processor(reference_processor), _serial_compaction_point(), - _is_alive(_mark_bitmap), - _is_alive_mutator(_reference_processor, &_is_alive) { + _is_alive(heap->concurrent_mark()->next_mark_bitmap()), + _is_alive_mutator(heap->ref_processor_stw(), &_is_alive) { assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); _preserved_marks_set.init(_num_workers); @@ -99,8 +104,19 @@ G1FullCollector::~G1FullCollector() { } void G1FullCollector::prepare_collection() { - _reference_processor->enable_discovery(); - _reference_processor->setup_policy(scope()->should_clear_soft_refs()); + _heap->g1_policy()->record_full_collection_start(); + + _heap->print_heap_before_gc(); + _heap->print_heap_regions(); + + _heap->abort_concurrent_cycle(); + _heap->verify_before_full_collection(scope()->is_explicit_gc()); + + _heap->gc_prologue(true); + _heap->prepare_heap_for_full_collection(); + + reference_processor()->enable_discovery(); + reference_processor()->setup_policy(scope()->should_clear_soft_refs()); // When collecting the permanent generation Method*s may be moving, // so we either have to flush all bcp data or convert it into bci. @@ -139,6 +155,15 @@ void G1FullCollector::complete_collection() { BiasedLocking::restore_marks(); CodeCache::gc_epilogue(); JvmtiExport::gc_epilogue(); + + _heap->prepare_heap_for_mutators(); + + _heap->g1_policy()->record_full_collection_end(); + _heap->gc_epilogue(true); + + _heap->verify_after_full_collection(); + + _heap->print_heap_after_full_collection(scope()->heap_transition()); } void G1FullCollector::phase1_mark_live_objects() { @@ -164,11 +189,11 @@ void G1FullCollector::phase1_mark_live_objects() { GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer()); // Unload classes and purge the SystemDictionary. bool purged_class = SystemDictionary::do_unloading(&_is_alive, scope()->timer()); - G1CollectedHeap::heap()->complete_cleaning(&_is_alive, purged_class); + _heap->complete_cleaning(&_is_alive, purged_class); } else { GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer()); // If no class unloading just clean out strings and symbols. - G1CollectedHeap::heap()->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled()); + _heap->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled()); } scope()->tracer()->report_object_count_after_gc(&_is_alive); @@ -210,13 +235,13 @@ void G1FullCollector::phase4_do_compaction() { } void G1FullCollector::restore_marks() { - SharedRestorePreservedMarksTaskExecutor task_executor(G1CollectedHeap::heap()->workers()); + SharedRestorePreservedMarksTaskExecutor task_executor(_heap->workers()); _preserved_marks_set.restore(&task_executor); _preserved_marks_set.reclaim(); } void G1FullCollector::run_task(AbstractGangTask* task) { - G1CollectedHeap::heap()->workers()->run_task(task, _num_workers); + _heap->workers()->run_task(task, _num_workers); } void G1FullCollector::verify_after_marking() { @@ -229,7 +254,7 @@ void G1FullCollector::verify_after_marking() { #if COMPILER2_OR_JVMCI DerivedPointerTableDeactivate dpt_deact; #endif - G1CollectedHeap::heap()->prepare_for_verify(); + _heap->prepare_for_verify(); // Note: we can verify only the heap here. When an object is // marked, the previous value of the mark word (including // identity hash values, ages, etc) is preserved, and the mark @@ -241,5 +266,5 @@ void G1FullCollector::verify_after_marking() { // (including hash values) are restored to the appropriate // objects. GCTraceTime(Info, gc, verify)("During GC (full)"); - G1CollectedHeap::heap()->verify(VerifyOption_G1UseFullMarking); + _heap->verify(VerifyOption_G1UseFullMarking); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 947304596ee..42caa812ec6 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1FullGCCompactionPoint.hpp" #include "gc/g1/g1FullGCMarker.hpp" #include "gc/g1/g1FullGCOopClosures.hpp" +#include "gc/g1/g1FullGCScope.hpp" #include "gc/shared/preservedMarks.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/taskqueue.hpp" @@ -42,41 +43,36 @@ class ReferenceProcessor; // The G1FullCollector holds data associated with the current Full GC. class G1FullCollector : StackObj { - G1FullGCScope* _scope; + G1CollectedHeap* _heap; + G1FullGCScope _scope; uint _num_workers; G1FullGCMarker** _markers; G1FullGCCompactionPoint** _compaction_points; - G1CMBitMap* _mark_bitmap; OopQueueSet _oop_queue_set; ObjArrayTaskQueueSet _array_queue_set; PreservedMarksSet _preserved_marks_set; - ReferenceProcessor* _reference_processor; G1FullGCCompactionPoint _serial_compaction_point; - G1IsAliveClosure _is_alive; ReferenceProcessorIsAliveMutator _is_alive_mutator; public: - G1FullCollector(G1FullGCScope* scope, - ReferenceProcessor* reference_processor, - G1CMBitMap* mark_bitmap, - uint workers); + G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs); ~G1FullCollector(); void prepare_collection(); void collect(); void complete_collection(); - G1FullGCScope* scope() { return _scope; } + G1FullGCScope* scope() { return &_scope; } uint workers() { return _num_workers; } G1FullGCMarker* marker(uint id) { return _markers[id]; } G1FullGCCompactionPoint* compaction_point(uint id) { return _compaction_points[id]; } - G1CMBitMap* mark_bitmap() { return _mark_bitmap; } OopQueueSet* oop_queue_set() { return &_oop_queue_set; } ObjArrayTaskQueueSet* array_queue_set() { return &_array_queue_set; } PreservedMarksSet* preserved_mark_set() { return &_preserved_marks_set; } - ReferenceProcessor* reference_processor() { return _reference_processor; } G1FullGCCompactionPoint* serial_compaction_point() { return &_serial_compaction_point; } + G1CMBitMap* mark_bitmap(); + ReferenceProcessor* reference_processor(); private: void phase1_mark_live_objects(); From e0ae6483a2a072f96697214c41df447c3dfd2c34 Mon Sep 17 00:00:00 2001 From: Robbin Ehn <rehn@openjdk.org> Date: Fri, 24 Nov 2017 13:58:52 +0100 Subject: [PATCH 014/165] 8191782: Missing deprecated options in VMDeprecatedOptions.java Reviewed-by: dcubed, mlarsson --- .../jtreg/runtime/CommandLine/VMDeprecatedOptions.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java index 78820042fea..2d372ebe2e1 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java @@ -46,6 +46,11 @@ public class VMDeprecatedOptions { {"MinRAMFraction", "2"}, {"InitialRAMFraction", "64"}, {"AssumeMP", "false"}, + {"UseMembar", "true"}, + {"FastTLABRefill", "false"}, + {"DeferPollingPageLoopCount", "-1"}, + {"SafepointSpinBeforeYield", "2000"}, + {"DeferPollingPageLoopCount", "4000"}, // deprecated alias flags (see also aliased_jvm_flags): {"DefaultMaxRAMFraction", "4"}, From 50f5bcbc03e8a59c252693d6f37c3d81960b23ec Mon Sep 17 00:00:00 2001 From: David Holmes <dholmes@openjdk.org> Date: Fri, 24 Nov 2017 14:07:59 +0100 Subject: [PATCH 015/165] 8191707: Options with invalid values are incorrectly treated as obsolete and ignored Reviewed-by: rehn, dcubed --- src/hotspot/share/runtime/arguments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index eb23fd76c06..de4adedf8f2 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -497,7 +497,7 @@ bool Arguments::is_obsolete_flag(const char *flag_name, JDK_Version* version) { SpecialFlag flag; if (lookup_special_flag(flag_name, flag)) { if (!flag.obsolete_in.is_undefined()) { - if (version_less_than(JDK_Version::current(), flag.expired_in)) { + if (!version_less_than(JDK_Version::current(), flag.obsolete_in)) { *version = flag.obsolete_in; return true; } From fa19052aa4f79ff475bd847d8fcb4a149d594bff Mon Sep 17 00:00:00 2001 From: Rahul Raghavan <rraghavan@openjdk.org> Date: Sun, 26 Nov 2017 09:05:13 -0800 Subject: [PATCH 016/165] 8191313: compiler/runtime/SpreadNullArg.java fails in tier1 Corrected SpreadNullArg.java test to expect NullPointerException after 8157246 fix Reviewed-by: dcubed, mchung --- test/hotspot/jtreg/compiler/runtime/SpreadNullArg.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/runtime/SpreadNullArg.java b/test/hotspot/jtreg/compiler/runtime/SpreadNullArg.java index 826058ed378..800890506d2 100644 --- a/test/hotspot/jtreg/compiler/runtime/SpreadNullArg.java +++ b/test/hotspot/jtreg/compiler/runtime/SpreadNullArg.java @@ -49,8 +49,8 @@ public class SpreadNullArg { mh_spread_target = MethodHandles.lookup().findStatic(SpreadNullArg.class, "target_spread_arg", mt_ref_arg); result = (int) mh_spreadInvoker.invokeExact(mh_spread_target, (Object[]) null); - throw new Error("Expected IllegalArgumentException was not thrown"); - } catch (IllegalArgumentException e) { + throw new Error("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { System.out.println("Expected exception : " + e); } catch (Throwable e) { throw new Error(e); @@ -58,7 +58,7 @@ public class SpreadNullArg { if (result != 42) { throw new Error("result [" + result - + "] != 42 : Expected IllegalArgumentException was not thrown?"); + + "] != 42 : Expected NullPointerException was not thrown?"); } } From 627a32d6722c92f814c1ddd1c2fdf9a3b28cd655 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga <ysuenaga@openjdk.org> Date: Mon, 27 Nov 2017 11:20:38 +0530 Subject: [PATCH 017/165] 8185796: jstack and clhsdb jstack should show lock objects Modifications to display monitor details with SA jstack Reviewed-by: sspitsyn, jgeorge --- .../classes/sun/jvm/hotspot/oops/Oop.java | 5 +- .../sun/jvm/hotspot/oops/java_lang_Class.java | 23 ++- .../sun/jvm/hotspot/runtime/BasicType.java | 43 +++++ .../jvm/hotspot/runtime/CompiledVFrame.java | 8 +- .../hotspot/runtime/InterpretedVFrame.java | 6 +- .../sun/jvm/hotspot/runtime/JavaVFrame.java | 132 ++++++++++++-- .../sun/jvm/hotspot/tools/StackTrace.java | 3 + .../ui/classbrowser/HTMLGenerator.java | 14 ++ .../sa/LingeredAppWithLock.java | 52 ++++++ .../sa/TestClhsdbJstackLock.java | 171 ++++++++++++++++++ .../sa/TestJhsdbJstackLock.java | 86 +++++++++ 11 files changed, 512 insertions(+), 31 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java create mode 100644 test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java create mode 100644 test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java index 241e5be3756..c0776dbad2b 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -202,8 +202,7 @@ public class Oop { public boolean verify() { return true;} - // Package-private routine to speed up ObjectHeap.newOop - static Klass getKlassForOopHandle(OopHandle handle) { + public static Klass getKlassForOopHandle(OopHandle handle) { if (handle == null) { return null; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java index 4eab2fe6c91..1e3014655d0 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -41,6 +41,7 @@ public class java_lang_Class { // java.lang.Class fields static int klassOffset; + static int arrayKlassOffset; static IntField oopSizeField; static { @@ -56,6 +57,7 @@ public class java_lang_Class { // find them from InstanceKlass for java.lang.Class. Type jlc = db.lookupType("java_lang_Class"); klassOffset = (int) jlc.getCIntegerField("_klass_offset").getValue(); + arrayKlassOffset = (int) jlc.getCIntegerField("_array_klass_offset").getValue(); int oopSizeOffset = (int) jlc.getCIntegerField("_oop_size_offset").getValue(); oopSizeField = new IntField(new NamedFieldIdentifier("oop_size"), oopSizeOffset, true); } @@ -69,4 +71,23 @@ public class java_lang_Class { public static long getOopSize(Oop aClass) { return java_lang_Class.oopSizeField.getValue(aClass); } + + /** + * Returns the Java name for this Java mirror + */ + public static String asExternalName(Oop aClass) { + Klass k = java_lang_Class.asKlass(aClass); + if (k == null) { // primitive array + BasicType type = BasicType.T_VOID; + ArrayKlass ak = (ArrayKlass)Metadata.instantiateWrapperFor( + aClass.getHandle().getAddressAt(arrayKlassOffset)); + if (ak != null) { + type = BasicType.intToBasicType(ak.getElementType()); + } + return type.getName(); + } else { + return k.getName().asString(); + } + } + } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java index 8331b5d34ca..93de9426d04 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java @@ -133,6 +133,27 @@ public class BasicType { return tIllegal; } + public static BasicType intToBasicType(int i) { + switch(i) { + case tBoolean: return T_BOOLEAN; + case tChar: return T_CHAR; + case tFloat: return T_FLOAT; + case tDouble: return T_DOUBLE; + case tByte: return T_BYTE; + case tShort: return T_SHORT; + case tInt: return T_INT; + case tLong: return T_LONG; + case tObject: return T_OBJECT; + case tArray: return T_ARRAY; + case tVoid: return T_VOID; + case tAddress: return T_ADDRESS; + case tNarrowOop: return T_NARROWOOP; + case tMetadata: return T_METADATA; + case tNarrowKlass: return T_NARROWKLASS; + default: return T_ILLEGAL; + } + } + public static BasicType charToBasicType(char c) { switch( c ) { case 'B': return T_BYTE; @@ -158,6 +179,28 @@ public class BasicType { return type; } + public String getName() { + switch (type) { + case tBoolean: return "boolean"; + case tChar: return "char"; + case tFloat: return "float"; + case tDouble: return "double"; + case tByte: return "byte"; + case tShort: return "short"; + case tInt: return "int"; + case tLong: return "long"; + case tObject: return "object"; + case tArray: return "array"; + case tVoid: return "void"; + case tAddress: return "address"; + case tNarrowOop: return "narrow oop"; + case tMetadata: return "metadata"; + case tNarrowKlass: return "narrow klass"; + case tConflict: return "conflict"; + default: return "ILLEGAL TYPE"; + } + } + //-- Internals only below this point private BasicType(int type) { this.type = type; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java index ba42e8da19f..a2d67b3fe3e 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -127,12 +127,12 @@ public class CompiledVFrame extends JavaVFrame { } /** Returns List<MonitorInfo> */ - public List getMonitors() { + public List<MonitorInfo> getMonitors() { List monitors = getScope().getMonitors(); if (monitors == null) { - return new ArrayList(); + return new ArrayList<>(); } - List result = new ArrayList(monitors.size()); + List<MonitorInfo> result = new ArrayList<>(monitors.size()); for (int i = 0; i < monitors.size(); i++) { MonitorValue mv = (MonitorValue) monitors.get(i); ScopeValue ov = mv.owner(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java index 180de93a366..75750548ef5 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -108,8 +108,8 @@ public class InterpretedVFrame extends JavaVFrame { } /** Returns List<MonitorInfo> */ - public List getMonitors() { - List result = new ArrayList(5); + public List<MonitorInfo> getMonitors() { + List<MonitorInfo> result = new ArrayList<>(5); for (BasicObjectLock current = getFrame().interpreterFrameMonitorEnd(); current.address().lessThan(getFrame().interpreterFrameMonitorBegin().address()); current = getFrame().nextMonitorInInterpreterFrame(current)) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java index a45cbc3640c..5486d6c2bc3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -28,14 +28,19 @@ import java.io.*; import java.util.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.debugger.*; public abstract class JavaVFrame extends VFrame { + + private static final String ADDRESS_FORMAT = VM.getVM().isLP64() ? "0x%016x" + : "0x%08x"; + /** JVM state */ public abstract Method getMethod(); public abstract int getBCI(); public abstract StackValueCollection getLocals(); public abstract StackValueCollection getExpressions(); - public abstract List getMonitors(); // List<MonitorInfo> + public abstract List<MonitorInfo> getMonitors(); /** Test operation */ public boolean isJavaFrame() { return true; } @@ -49,9 +54,112 @@ public abstract class JavaVFrame extends VFrame { // FIXME: not yet implemented // public Address getPendingMonitor(int frameCount); + public void printLockedObjectClassName(PrintStream tty, + OopHandle hobj, String lockState) { + if (hobj.asLongValue() != 0L) { + tty.format("\t- %s <" + ADDRESS_FORMAT + "> ", + lockState, hobj.asLongValue()); + + Klass klass = Oop.getKlassForOopHandle(hobj); + String klassName = klass.getName().asString(); + tty.print("(a "); + if (klassName.equals("java/lang/Class")) { + Oop obj = VM.getVM().getObjectHeap().newOop(hobj); + klassName = java_lang_Class.asExternalName(obj); + tty.print("java.lang.Class for "); + } + tty.println(klassName.replace('/', '.') + ")"); + } + } + + private String identifyLockState(MonitorInfo monitor, String waitingState) { + Mark mark = new Mark(monitor.owner()); + if (mark.hasMonitor() && + ( // we have marked ourself as pending on this monitor + mark.monitor().equals(thread.getCurrentPendingMonitor()) || + // we are not the owner of this monitor + !mark.monitor().isEntered(thread) + )) { + return waitingState; + } + return "locked"; + } + /** Printing used during stack dumps */ - // FIXME: not yet implemented - // void print_lock_info(int frame_count); + public void printLockInfo(PrintStream tty, int frameCount) { + // If this is the first frame and it is java.lang.Object.wait(...) + // then print out the receiver. Locals are not always available, + // e.g., compiled native frames have no scope so there are no locals. + if (frameCount == 0) { + if (getMethod().getName().asString().equals("wait") && + getMethod().getMethodHolder().getName().asString().equals("java/lang/Object")) { + String waitState = "waiting on"; // assume we are waiting + // If earlier in the output we reported java.lang.Thread.State == + // "WAITING (on object monitor)" and now we report "waiting on", then + // we are still waiting for notification or timeout. Otherwise if + // we earlier reported java.lang.Thread.State == "BLOCKED (on object + // monitor)", then we are actually waiting to re-lock the monitor. + // At this level we can't distinguish the two cases to report + // "waited on" rather than "waiting on" for the second case. + StackValueCollection locs = getLocals(); + if (!locs.isEmpty()) { + StackValue sv = locs.get(0); + if (sv.getType() == BasicType.getTObject()) { + OopHandle o = sv.getObject(); + printLockedObjectClassName(tty, o, waitState); + } + } else { + tty.println("\t- " + waitState + " <no object reference available>"); + } + } else if (thread.getCurrentParkBlocker() != null) { + Oop obj = thread.getCurrentParkBlocker(); + Klass k = obj.getKlass(); + tty.format("\t- parking to wait for <" + ADDRESS_FORMAT + "> (a %s)", + obj.getHandle().asLongValue(), k.getName().asString()); + tty.println(); + } + } + + // Print out all monitors that we have locked, or are trying to lock, + // including re-locking after being notified or timing out in a wait(). + List<MonitorInfo> mons = getMonitors(); + if (!mons.isEmpty()) { + boolean foundFirstMonitor = false; + for (int index = mons.size() - 1; index >= 0; index--) { + MonitorInfo monitor = mons.get(index); + if (monitor.eliminated() && isCompiledFrame()) { // Eliminated in compiled code + if (monitor.ownerIsScalarReplaced()) { + Klass k = Oop.getKlassForOopHandle(monitor.ownerKlass()); + tty.println("\t- eliminated <owner is scalar replaced> (a " + k.getName().asString() + ")"); + } else if (monitor.owner() != null) { + printLockedObjectClassName(tty, monitor.owner(), "eliminated"); + } + continue; + } + if (monitor.owner() != null) { + // the monitor is associated with an object, i.e., it is locked + String lockState = "locked"; + if (!foundFirstMonitor && frameCount == 0) { + // If this is the first frame and we haven't found an owned + // monitor before, then we need to see if we have completed + // the lock or if we are blocked trying to acquire it. Only + // an inflated monitor that is first on the monitor list in + // the first frame can block us on a monitor enter. + lockState = identifyLockState(monitor, "waiting to lock"); + } else if (frameCount != 0) { + // This is not the first frame so we either own this monitor + // or we owned the monitor before and called wait(). Because + // wait() could have been called on any monitor in a lower + // numbered frame on the stack, we have to check all the + // monitors on the list for this frame. + lockState = identifyLockState(monitor, "waiting to re-lock in wait()"); + } + printLockedObjectClassName(tty, monitor.owner(), lockState); + foundFirstMonitor = true; + } + } + } + } /** Printing operations */ @@ -73,22 +181,6 @@ public abstract class JavaVFrame extends VFrame { printStackValuesOn(tty, "locals", getLocals()); printStackValuesOn(tty, "expressions", getExpressions()); - - // List<MonitorInfo> - // FIXME: not yet implemented - // List list = getMonitors(); - // if (list.isEmpty()) { - // return; - // } - // for (int index = 0; index < list.size(); index++) { - // MonitorInfo monitor = (MonitorInfo) list.get(index); - // tty.print("\t obj\t"); - // monitor.getOwner().printValueOn(tty); - // tty.println(); - // tty.print("\t "); - // monitor.lock().printOn(tty); - // tty.println(); - // } } public void printActivation(int index) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java index 7774fb68df0..0072a5bf64b 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java @@ -76,6 +76,8 @@ public class StackTrace extends Tool { if (cur.isJavaThread()) { cur.printThreadInfoOn(tty); try { + int count = 0; + for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { Method method = vf.getMethod(); tty.print(" - " + method.externalNameAndSignature() + @@ -109,6 +111,7 @@ public class StackTrace extends Tool { } tty.println(")"); + vf.printLockInfo(tty, count++); } } catch (Exception e) { tty.println("Error occurred during stack walking:"); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java index d2d639214af..f37d15e696f 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java @@ -1910,6 +1910,7 @@ public class HTMLGenerator implements /* imports */ ClassConstants { buf.append(thread.getThreadState().toString()); buf.br(); buf.beginTag("pre"); + int count = 0; for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { Method method = vf.getMethod(); buf.append(" - "); @@ -1954,6 +1955,19 @@ public class HTMLGenerator implements /* imports */ ClassConstants { } buf.append(")"); buf.br(); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(bytes); + try (printStream) { + vf.printLockInfo(printStream, count++); + for (String line : bytes.toString().split("\n")) { + if (genHTML) { + line = line.replace("<", "<").replace(">", ">"); + } + buf.append(line); + buf.br(); + } + } } buf.endTag("pre"); diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java new file mode 100644 index 00000000000..9acf9d3bbb9 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 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. + */ + +import jdk.test.lib.apps.LingeredApp; + + +public class LingeredAppWithLock extends LingeredApp { + + public static void lockMethod(Object lock) { + synchronized (lock) { + try { + Thread.sleep(300000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public static void main(String args[]) { + Thread classLock1 = new Thread(() -> lockMethod(LingeredAppWithLock.class)); + Thread classLock2 = new Thread(() -> lockMethod(LingeredAppWithLock.class)); + Thread objectLock = new Thread(() -> lockMethod(classLock1)); + Thread primitiveLock = new Thread(() -> lockMethod(int.class)); + + classLock1.start(); + classLock2.start(); + objectLock.start(); + primitiveLock.start(); + + LingeredApp.main(args); + } + } diff --git a/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java new file mode 100644 index 00000000000..2132a4e36fe --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.ArrayList; +import java.util.Scanner; +import java.util.List; +import java.io.File; +import java.io.IOException; +import java.util.stream.Collectors; +import java.io.OutputStream; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Utils; +import jdk.test.lib.Asserts; + +/* + * @test + * @library /test/lib + * @run main/othervm TestClhsdbJstackLock + */ + +public class TestClhsdbJstackLock { + + private static final String JSTACK_OUT_FILE = "jstack_out.txt"; + + private static void verifyJStackOutput() throws Exception { + + Exception unexpected = null; + File jstackFile = new File(JSTACK_OUT_FILE); + Asserts.assertTrue(jstackFile.exists() && jstackFile.isFile(), + "File with jstack output not created: " + + jstackFile.getAbsolutePath()); + try { + Scanner scanner = new Scanner(jstackFile); + + boolean classLockOwnerFound = false; + boolean classLockWaiterFound = false; + boolean objectLockOwnerFound = false; + boolean primitiveLockOwnerFound = false; + + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + System.out.println(line); + + if (line.contains("missing reason for ")) { + unexpected = new RuntimeException("Unexpected msg: missing reason for "); + break; + } + if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) { + classLockOwnerFound = true; + } + if (line.matches("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) { + classLockWaiterFound = true; + } + if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$")) { + objectLockOwnerFound = true; + } + if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$")) { + primitiveLockOwnerFound = true; + } + } + + if (!classLockOwnerFound || !classLockWaiterFound || + !objectLockOwnerFound || !primitiveLockOwnerFound) { + unexpected = new RuntimeException( + "classLockOwnerFound = " + classLockOwnerFound + + ", classLockWaiterFound = " + classLockWaiterFound + + ", objectLockOwnerFound = " + objectLockOwnerFound + + ", primitiveLockOwnerFound = " + primitiveLockOwnerFound); + } + if (unexpected != null) { + throw unexpected; + } + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + jstackFile.delete(); + } + } + + private static void startClhsdbForLock(long lingeredAppPid) throws Exception { + + Process p; + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addToolArg("clhsdb"); + launcher.addToolArg("--pid"); + launcher.addToolArg(Long.toString(lingeredAppPid)); + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(launcher.getCommand()); + System.out.println(pb.command().stream().collect(Collectors.joining(" "))); + + try { + p = pb.start(); + } catch (Exception attachE) { + throw new Error("Couldn't start jhsdb or attach to LingeredApp : " + attachE); + } + + // Issue the 'jstack' input at the clhsdb prompt. + OutputStream input = p.getOutputStream(); + String str = "jstack > " + JSTACK_OUT_FILE + "\nquit\n"; + try { + input.write(str.getBytes()); + input.flush(); + } catch (IOException ioe) { + throw new Error("Problem issuing the jstack command: " + str, ioe); + } + + try { + p.waitFor(); + } catch (InterruptedException ie) { + throw new Error("Problem awaiting the child process: " + ie, ie); + } + + int exitValue = p.exitValue(); + if (exitValue != 0) { + String output; + try { + output = new OutputAnalyzer(p).getOutput(); + } catch (IOException ioe) { + throw new Error("Can't get failed clhsdb process output: " + ioe, ioe); + } + throw new AssertionError("clhsdb wasn't run successfully: " + output); + } + } + + public static void main (String... args) throws Exception { + + LingeredApp app = null; + + if (!Platform.shouldSAAttach()) { + System.out.println("SA attach not expected to work - test skipped."); + return; + } + + try { + List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions()); + + app = new LingeredAppWithLock(); + LingeredApp.startApp(vmArgs, app); + System.out.println ("Started LingeredApp with pid " + app.getPid()); + startClhsdbForLock(app.getPid()); + verifyJStackOutput(); + } finally { + LingeredApp.stopApp(app); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java new file mode 100644 index 00000000000..423d56bef35 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.Asserts; +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Utils; + +/* + * @test + * @library /test/lib + * @run main/othervm TestJhsdbJstackLock + */ + +public class TestJhsdbJstackLock { + + public static void main (String... args) throws Exception { + + LingeredApp app = null; + + if (!Platform.shouldSAAttach()) { + System.out.println("SA attach not expected to work - test skipped."); + return; + } + + try { + List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions()); + + app = new LingeredAppWithLock(); + LingeredApp.startApp(vmArgs, app); + System.out.println ("Started LingeredApp with pid " + app.getPid()); + + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addToolArg("jstack"); + launcher.addToolArg("--pid"); + launcher.addToolArg(Long.toString(app.getPid())); + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(launcher.getCommand()); + Process jhsdb = pb.start(); + + jhsdb.waitFor(); + + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$"); + out.shouldMatch("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$"); + out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$"); + out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$"); + out.stderrShouldBeEmpty(); + + System.out.println("Test Completed"); + } finally { + LingeredApp.stopApp(app); + } + } +} From 5fd6905c94358e2667ce4194d51ae24f4f726ca4 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson <stefank@openjdk.org> Date: Fri, 24 Nov 2017 15:21:28 +0100 Subject: [PATCH 018/165] 8191858: Add missing includes in memoryManager.hpp Reviewed-by: ehelin, tschatzl, pliden --- src/hotspot/share/services/memoryManager.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/services/memoryManager.hpp b/src/hotspot/share/services/memoryManager.hpp index f2a7d4c3420..3b853d08579 100644 --- a/src/hotspot/share/services/memoryManager.hpp +++ b/src/hotspot/share/services/memoryManager.hpp @@ -25,7 +25,10 @@ #ifndef SHARE_VM_SERVICES_MEMORYMANAGER_HPP #define SHARE_VM_SERVICES_MEMORYMANAGER_HPP +#include "gc/shared/gcCause.hpp" #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" +#include "runtime/handles.hpp" #include "runtime/timer.hpp" #include "services/memoryUsage.hpp" From dbc915dcb90480fc835e3ebee30c59773d9d1963 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson <stefank@openjdk.org> Date: Fri, 24 Nov 2017 15:21:30 +0100 Subject: [PATCH 019/165] 8191860: Add perfData.inline.hpp Reviewed-by: ehelin, pliden --- src/hotspot/share/classfile/classLoader.hpp | 2 + .../share/gc/shared/collectorCounters.cpp | 22 +++++++ .../share/gc/shared/collectorCounters.hpp | 17 +---- .../share/gc/shared/generationCounters.cpp | 6 ++ .../share/gc/shared/generationCounters.hpp | 4 +- src/hotspot/share/prims/perf.cpp | 2 +- src/hotspot/share/runtime/arguments.hpp | 1 + src/hotspot/share/runtime/objectMonitor.hpp | 1 + src/hotspot/share/runtime/perfData.cpp | 9 ++- src/hotspot/share/runtime/perfData.hpp | 28 ++++---- src/hotspot/share/runtime/perfData.inline.hpp | 64 +++++++++++++++++++ src/hotspot/share/runtime/statSampler.cpp | 1 + 12 files changed, 122 insertions(+), 35 deletions(-) create mode 100644 src/hotspot/share/runtime/perfData.inline.hpp diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index 3ba612ec747..202a170979f 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_CLASSFILE_CLASSLOADER_HPP #include "jimage.hpp" +#include "runtime/handles.hpp" #include "runtime/orderAccess.hpp" #include "runtime/perfData.hpp" #include "utilities/exceptions.hpp" @@ -42,6 +43,7 @@ class JImageFile; class ClassFileStream; class PackageEntry; +template <typename T> class GrowableArray; class ClassPathEntry : public CHeapObj<mtClass> { private: diff --git a/src/hotspot/share/gc/shared/collectorCounters.cpp b/src/hotspot/share/gc/shared/collectorCounters.cpp index b204a02e272..ff1edac2e4b 100644 --- a/src/hotspot/share/gc/shared/collectorCounters.cpp +++ b/src/hotspot/share/gc/shared/collectorCounters.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/shared/collectorCounters.hpp" #include "memory/resourceArea.hpp" +#include "runtime/os.hpp" CollectorCounters::CollectorCounters(const char* name, int ordinal) { @@ -59,3 +60,24 @@ CollectorCounters::CollectorCounters(const char* name, int ordinal) { CHECK); } } + +CollectorCounters::~CollectorCounters() { + if (_name_space != NULL) { + FREE_C_HEAP_ARRAY(char, _name_space); + } +} + +TraceCollectorStats::TraceCollectorStats(CollectorCounters* c) : + PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()), + _c(c) { + + if (UsePerfData) { + _c->last_entry_counter()->set_value(os::elapsed_counter()); + } +} + +TraceCollectorStats::~TraceCollectorStats() { + if (UsePerfData) { + _c->last_exit_counter()->set_value(os::elapsed_counter()); + } +} diff --git a/src/hotspot/share/gc/shared/collectorCounters.hpp b/src/hotspot/share/gc/shared/collectorCounters.hpp index 554d749e0cc..e9b272f03a9 100644 --- a/src/hotspot/share/gc/shared/collectorCounters.hpp +++ b/src/hotspot/share/gc/shared/collectorCounters.hpp @@ -49,9 +49,7 @@ class CollectorCounters: public CHeapObj<mtGC> { CollectorCounters(const char* name, int ordinal); - ~CollectorCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~CollectorCounters(); inline PerfCounter* invocation_counter() const { return _invocations; } @@ -70,18 +68,9 @@ class TraceCollectorStats: public PerfTraceTimedEvent { CollectorCounters* _c; public: - inline TraceCollectorStats(CollectorCounters* c) : - PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()), - _c(c) { + TraceCollectorStats(CollectorCounters* c); - if (UsePerfData) { - _c->last_entry_counter()->set_value(os::elapsed_counter()); - } - } - - inline ~TraceCollectorStats() { - if (UsePerfData) _c->last_exit_counter()->set_value(os::elapsed_counter()); - } + ~TraceCollectorStats(); }; #endif // SHARE_VM_GC_SHARED_COLLECTORCOUNTERS_HPP diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp index 134d28765e0..d1b8f3e64c4 100644 --- a/src/hotspot/share/gc/shared/generationCounters.cpp +++ b/src/hotspot/share/gc/shared/generationCounters.cpp @@ -78,6 +78,12 @@ GenerationCounters::GenerationCounters(const char* name, initialize(name, ordinal, spaces, min_capacity, max_capacity, curr_capacity); } +GenerationCounters::~GenerationCounters() { + if (_name_space != NULL) { + FREE_C_HEAP_ARRAY(char, _name_space); + } +} + void GenerationCounters::update_all() { assert(_virtual_space != NULL, "otherwise, override this method"); _current_size->set_value(_virtual_space->committed_size()); diff --git a/src/hotspot/share/gc/shared/generationCounters.hpp b/src/hotspot/share/gc/shared/generationCounters.hpp index 2b53549d575..71c42bb318c 100644 --- a/src/hotspot/share/gc/shared/generationCounters.hpp +++ b/src/hotspot/share/gc/shared/generationCounters.hpp @@ -68,9 +68,7 @@ private: GenerationCounters(const char* name, int ordinal, int spaces, size_t min_capacity, size_t max_capacity, VirtualSpace* v); - ~GenerationCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~GenerationCounters(); virtual void update_all(); diff --git a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp index ba019e04cb2..cf93aaeb99b 100644 --- a/src/hotspot/share/prims/perf.cpp +++ b/src/hotspot/share/prims/perf.cpp @@ -30,7 +30,7 @@ #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "runtime/interfaceSupport.hpp" -#include "runtime/perfData.hpp" +#include "runtime/perfData.inline.hpp" #include "runtime/perfMemory.hpp" /* diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 43c0ff37d33..c3105c39263 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -27,6 +27,7 @@ #include "logging/logLevel.hpp" #include "logging/logTag.hpp" +#include "memory/allocation.inline.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" #include "runtime/perfData.hpp" diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 484b99aa2da..e7744cc5962 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_RUNTIME_OBJECTMONITOR_HPP #define SHARE_VM_RUNTIME_OBJECTMONITOR_HPP +#include "memory/allocation.inline.hpp" #include "memory/padded.hpp" #include "runtime/os.hpp" #include "runtime/park.hpp" diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp index d79bbc66b48..e6a9045825b 100644 --- a/src/hotspot/share/runtime/perfData.cpp +++ b/src/hotspot/share/runtime/perfData.cpp @@ -32,7 +32,7 @@ #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" -#include "runtime/perfData.hpp" +#include "runtime/perfData.inline.hpp" #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" @@ -611,3 +611,10 @@ PerfDataList* PerfDataList::clone() { return copy; } + +PerfTraceTime::~PerfTraceTime() { + if (!UsePerfData || (_recursion_counter != NULL && + --(*_recursion_counter) > 0)) return; + _t.stop(); + _timerp->inc(_t.ticks()); +} diff --git a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp index 8fa9f14d598..25167bf3930 100644 --- a/src/hotspot/share/runtime/perfData.hpp +++ b/src/hotspot/share/runtime/perfData.hpp @@ -25,10 +25,11 @@ #ifndef SHARE_VM_RUNTIME_PERFDATA_HPP #define SHARE_VM_RUNTIME_PERFDATA_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "runtime/perfMemory.hpp" #include "runtime/timer.hpp" -#include "utilities/growableArray.hpp" + +template <typename T> class GrowableArray; /* jvmstat global and subsystem counter name space - enumeration value * serve as an index into the PerfDataManager::_name_space[] array @@ -629,10 +630,10 @@ class PerfDataList : public CHeapObj<mtInternal> { bool contains(const char* name) { return find_by_name(name) != NULL; } // return the number of PerfData items in this list - int length() { return _set->length(); } + inline int length(); // add a PerfData item to this list - void append(PerfData *p) { _set->append(p); } + inline void append(PerfData *p); // remove the given PerfData item from this list. When called // while iterating over the list, this method will result in a @@ -640,7 +641,7 @@ class PerfDataList : public CHeapObj<mtInternal> { // method is also impacted by this method as elements with an // index greater than the index of the element removed by this // method will be shifted down by one. - void remove(PerfData *p) { _set->remove(p); } + inline void remove(PerfData *p); // create a new PerfDataList from this list. The new list is // a shallow copy of the original list and care should be taken @@ -651,7 +652,7 @@ class PerfDataList : public CHeapObj<mtInternal> { // for backward compatibility with GrowableArray - need to implement // some form of iterator to provide a cleaner abstraction for // iteration over the container. - PerfData* at(int index) { return _set->at(index); } + inline PerfData* at(int index); }; @@ -677,23 +678,23 @@ class PerfDataManager : AllStatic { protected: // return the list of all known PerfData items static PerfDataList* all(); - static int count() { return _all->length(); } + static inline int count(); // return the list of all known PerfData items that are to be // sampled by the StatSampler. static PerfDataList* sampled(); - static int sampled_count() { return _sampled->length(); } + static inline int sampled_count(); // return the list of all known PerfData items that have a // variability classification of type Constant static PerfDataList* constants(); - static int constants_count() { return _constants->length(); } + static inline int constants_count(); public: // method to check for the existence of a PerfData item with // the given name. - static bool exists(const char* name) { return _all->contains(name); } + static inline bool exists(const char* name); // method to search for a instrumentation object by name static PerfData* find_by_name(const char* name); @@ -929,12 +930,7 @@ class PerfTraceTime : public StackObj { inline void suspend() { if (!UsePerfData) return; _t.stop(); } inline void resume() { if (!UsePerfData) return; _t.start(); } - inline ~PerfTraceTime() { - if (!UsePerfData || (_recursion_counter != NULL && - --(*_recursion_counter) > 0)) return; - _t.stop(); - _timerp->inc(_t.ticks()); - } + ~PerfTraceTime(); }; /* The PerfTraceTimedEvent class is responsible for counting the diff --git a/src/hotspot/share/runtime/perfData.inline.hpp b/src/hotspot/share/runtime/perfData.inline.hpp new file mode 100644 index 00000000000..b867bbdbb4e --- /dev/null +++ b/src/hotspot/share/runtime/perfData.inline.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002, 2017, 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. + * + */ + +#ifndef SHARE_VM_RUNTIME_PERFDATA_INLINE_HPP +#define SHARE_VM_RUNTIME_PERFDATA_INLINE_HPP + +#include "runtime/perfData.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" + +inline int PerfDataList::length() { + return _set->length(); +} + +inline void PerfDataList::append(PerfData *p) { + _set->append(p); +} + +inline void PerfDataList::remove(PerfData *p) { + _set->remove(p); +} + +inline PerfData* PerfDataList::at(int index) { + return _set->at(index); +} + +inline int PerfDataManager::count() { + return _all->length(); +} + +inline int PerfDataManager::sampled_count() { + return _sampled->length(); +} + +inline int PerfDataManager::constants_count() { + return _constants->length(); +} + +inline bool PerfDataManager::exists(const char* name) { + return _all->contains(name); +} + +#endif // SHARE_VM_RUNTIME_PERFDATA_INLINE_HPP diff --git a/src/hotspot/share/runtime/statSampler.cpp b/src/hotspot/share/runtime/statSampler.cpp index 6cdd0743ad1..3f995380468 100644 --- a/src/hotspot/share/runtime/statSampler.cpp +++ b/src/hotspot/share/runtime/statSampler.cpp @@ -32,6 +32,7 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/os.hpp" +#include "runtime/perfData.inline.hpp" #include "runtime/statSampler.hpp" #include "runtime/vm_version.hpp" From 677e5bdaedbc0245e7115c8ccf15af9d68dd3555 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson <stefank@openjdk.org> Date: Fri, 24 Nov 2017 15:48:01 +0100 Subject: [PATCH 020/165] 8191861: Move and refactor hSpaceCounters Reviewed-by: pliden, ehelin, rkennke --- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 2 +- .../share/gc/g1/g1MonitoringSupport.cpp | 27 +++++----- .../share/gc/g1/g1MonitoringSupport.hpp | 4 +- .../gc/{g1 => shared}/hSpaceCounters.cpp | 45 ++++++++++++++--- .../gc/{g1 => shared}/hSpaceCounters.hpp | 50 ++++++------------- 5 files changed, 73 insertions(+), 55 deletions(-) rename src/hotspot/share/gc/{g1 => shared}/hSpaceCounters.cpp (69%) rename src/hotspot/share/gc/{g1 => shared}/hSpaceCounters.hpp (60%) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 5c2109ede03..ff00bc114ab 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -42,11 +42,11 @@ #include "gc/g1/g1SATBCardTableModRefBS.hpp" #include "gc/g1/g1SurvivorRegions.hpp" #include "gc/g1/g1YCTypes.hpp" -#include "gc/g1/hSpaceCounters.hpp" #include "gc/g1/heapRegionManager.hpp" #include "gc/g1/heapRegionSet.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/gcHeapSummary.hpp" #include "gc/shared/plab.hpp" #include "gc/shared/preservedMarks.hpp" #include "memory/memRegion.hpp" diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index a0cf7c2c3cf..0a566b3afc0 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -26,6 +26,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1Policy.hpp" +#include "gc/shared/hSpaceCounters.hpp" G1GenerationCounters::G1GenerationCounters(G1MonitoringSupport* g1mm, const char* name, @@ -128,10 +129,10 @@ G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) : // name "generation.1.space.0" // Counters are created from maxCapacity, capacity, initCapacity, // and used. - _old_space_counters = new HSpaceCounters("space", 0 /* ordinal */, + _old_space_counters = new HSpaceCounters(_old_collection_counters->name_space(), + "space", 0 /* ordinal */, pad_capacity(overall_reserved()) /* max_capacity */, - pad_capacity(old_space_committed()) /* init_capacity */, - _old_collection_counters); + pad_capacity(old_space_committed()) /* init_capacity */); // Young collection set // name "generation.0". This is logically the young generation. @@ -139,27 +140,29 @@ G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) : // See _old_collection_counters for additional counters _young_collection_counters = new G1YoungGenerationCounters(this, "young"); + const char* young_collection_name_space = _young_collection_counters->name_space(); + // name "generation.0.space.0" // See _old_space_counters for additional counters - _eden_counters = new HSpaceCounters("eden", 0 /* ordinal */, + _eden_counters = new HSpaceCounters(young_collection_name_space, + "eden", 0 /* ordinal */, pad_capacity(overall_reserved()) /* max_capacity */, - pad_capacity(eden_space_committed()) /* init_capacity */, - _young_collection_counters); + pad_capacity(eden_space_committed()) /* init_capacity */); // name "generation.0.space.1" // See _old_space_counters for additional counters // Set the arguments to indicate that this survivor space is not used. - _from_counters = new HSpaceCounters("s0", 1 /* ordinal */, + _from_counters = new HSpaceCounters(young_collection_name_space, + "s0", 1 /* ordinal */, pad_capacity(0) /* max_capacity */, - pad_capacity(0) /* init_capacity */, - _young_collection_counters); + pad_capacity(0) /* init_capacity */); // name "generation.0.space.2" // See _old_space_counters for additional counters - _to_counters = new HSpaceCounters("s1", 2 /* ordinal */, + _to_counters = new HSpaceCounters(young_collection_name_space, + "s1", 2 /* ordinal */, pad_capacity(overall_reserved()) /* max_capacity */, - pad_capacity(survivor_space_committed()) /* init_capacity */, - _young_collection_counters); + pad_capacity(survivor_space_committed()) /* init_capacity */); if (UsePerfData) { // Given that this survivor space is not used, we update it here diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp index 3c1d7444108..ee103237009 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp @@ -25,9 +25,11 @@ #ifndef SHARE_VM_GC_G1_G1MONITORINGSUPPORT_HPP #define SHARE_VM_GC_G1_G1MONITORINGSUPPORT_HPP -#include "gc/g1/hSpaceCounters.hpp" +#include "gc/shared/generationCounters.hpp" +class CollectorCounters; class G1CollectedHeap; +class HSpaceCounters; // Class for monitoring logical spaces in G1. It provides data for // both G1's jstat counters as well as G1's memory pools. diff --git a/src/hotspot/share/gc/g1/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp similarity index 69% rename from src/hotspot/share/gc/g1/hSpaceCounters.cpp rename to src/hotspot/share/gc/shared/hSpaceCounters.cpp index 7a5afddfedf..8b5e92fa7f4 100644 --- a/src/hotspot/share/gc/g1/hSpaceCounters.cpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -23,22 +23,23 @@ */ #include "precompiled.hpp" -#include "gc/g1/hSpaceCounters.hpp" -#include "gc/shared/generation.hpp" +#include "gc/shared/hSpaceCounters.hpp" +#include "memory/allocation.hpp" #include "memory/resourceArea.hpp" +#include "runtime/perfData.hpp" -HSpaceCounters::HSpaceCounters(const char* name, +HSpaceCounters::HSpaceCounters(const char* name_space, + const char* name, int ordinal, size_t max_size, - size_t initial_capacity, - GenerationCounters* gc) { + size_t initial_capacity) { if (UsePerfData) { EXCEPTION_MARK; ResourceMark rm; const char* cns = - PerfDataManager::name_space(gc->name_space(), "space", ordinal); + PerfDataManager::name_space(name_space, "space", ordinal); _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1, mtGC); strcpy(_name_space, cns); @@ -64,3 +65,33 @@ HSpaceCounters::HSpaceCounters(const char* name, initial_capacity, CHECK); } } + +HSpaceCounters::~HSpaceCounters() { + if (_name_space != NULL) { + FREE_C_HEAP_ARRAY(char, _name_space); + } +} + +void HSpaceCounters::update_capacity(size_t v) { + _capacity->set_value(v); +} + +void HSpaceCounters::update_used(size_t v) { + _used->set_value(v); +} + +void HSpaceCounters::update_all(size_t capacity, size_t used) { + update_capacity(capacity); + update_used(used); +} + +debug_only( + // for security reasons, we do not allow arbitrary reads from + // the counters as they may live in shared memory. + jlong HSpaceCounters::used() { + return _used->get_value(); + } + jlong HSpaceCounters::capacity() { + return _used->get_value(); + } +) diff --git a/src/hotspot/share/gc/g1/hSpaceCounters.hpp b/src/hotspot/share/gc/shared/hSpaceCounters.hpp similarity index 60% rename from src/hotspot/share/gc/g1/hSpaceCounters.hpp rename to src/hotspot/share/gc/shared/hSpaceCounters.hpp index fd7ed263415..36873fd8f70 100644 --- a/src/hotspot/share/gc/g1/hSpaceCounters.hpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -22,65 +22,47 @@ * */ -#ifndef SHARE_VM_GC_G1_HSPACECOUNTERS_HPP -#define SHARE_VM_GC_G1_HSPACECOUNTERS_HPP +#ifndef SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP +#define SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP -#include "gc/shared/generation.hpp" -#include "gc/shared/generationCounters.hpp" +#include "memory/allocation.hpp" #include "runtime/perfData.hpp" #include "utilities/macros.hpp" // A HSpaceCounter is a holder class for performance counters // that track a collections (logical spaces) in a heap; -class HeapSpaceUsedHelper; -class G1SpaceMonitoringSupport; - class HSpaceCounters: public CHeapObj<mtGC> { friend class VMStructs; private: - PerfVariable* _capacity; - PerfVariable* _used; + PerfVariable* _capacity; + PerfVariable* _used; // Constant PerfData types don't need to retain a reference. // However, it's a good idea to document them here. - char* _name_space; + char* _name_space; public: - HSpaceCounters(const char* name, int ordinal, size_t max_size, - size_t initial_capacity, GenerationCounters* gc); + HSpaceCounters(const char* name_space, const char* name, int ordinal, + size_t max_size, size_t initial_capacity); - ~HSpaceCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~HSpaceCounters(); - inline void update_capacity(size_t v) { - _capacity->set_value(v); - } + void update_capacity(size_t v); + void update_used(size_t v); - inline void update_used(size_t v) { - _used->set_value(v); - } + void update_all(size_t capacity, size_t used); debug_only( // for security reasons, we do not allow arbitrary reads from // the counters as they may live in shared memory. - jlong used() { - return _used->get_value(); - } - jlong capacity() { - return _used->get_value(); - } + jlong used(); + jlong capacity(); ) - inline void update_all(size_t capacity, size_t used) { - update_capacity(capacity); - update_used(used); - } - const char* name_space() const { return _name_space; } }; -#endif // SHARE_VM_GC_G1_HSPACECOUNTERS_HPP +#endif // SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP From 01e9cb7e23eb3ffd7bcf109926a10068e0a566aa Mon Sep 17 00:00:00 2001 From: Erik Helin <ehelin@openjdk.org> Date: Mon, 27 Nov 2017 10:51:31 +0100 Subject: [PATCH 021/165] 8080345: With perm gen gone, perfdata counter sun.gc.policy.generations should be 2, not 3 Co-authored-by: Y. Srinivas Ramakrishna <ysr1729@gmail.com> Reviewed-by: sjohanss, tschatzl --- .../share/gc/cms/cmsCollectorPolicy.cpp | 4 +- src/hotspot/share/gc/g1/g1DefaultPolicy.cpp | 2 +- .../gc/parallel/parallelScavengeHeap.cpp | 4 +- .../share/gc/shared/collectorPolicy.cpp | 4 +- .../jtreg/gc/TestGenerationPerfCounter.java | 49 +++++++++++++++++++ .../metaspace/TestMetaspacePerfCounters.java | 4 +- .../TestPerfCountersAndMemoryPools.java | 4 +- .../PerfCounter.java | 2 + .../PerfCounters.java | 2 + 9 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 test/hotspot/jtreg/gc/TestGenerationPerfCounter.java rename test/hotspot/jtreg/gc/{metaspace => testlibrary}/PerfCounter.java (98%) rename test/hotspot/jtreg/gc/{metaspace => testlibrary}/PerfCounters.java (99%) diff --git a/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp b/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp index 0333b4f433e..e7160fb622a 100644 --- a/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp +++ b/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp @@ -71,6 +71,6 @@ void ConcurrentMarkSweepPolicy::initialize_size_policy(size_t init_eden_size, } void ConcurrentMarkSweepPolicy::initialize_gc_policy_counters() { - // initialize the policy counters - 2 collectors, 3 generations - _gc_policy_counters = new GCPolicyCounters("ParNew:CMS", 2, 3); + // initialize the policy counters - 2 collectors, 2 generations + _gc_policy_counters = new GCPolicyCounters("ParNew:CMS", 2, 2); } diff --git a/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp b/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp index 409c8ba99aa..4c480ca5827 100644 --- a/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp @@ -52,7 +52,7 @@ G1DefaultPolicy::G1DefaultPolicy(STWGCTimer* gc_timer) : _analytics(new G1Analytics(&_predictor)), _mmu_tracker(new G1MMUTrackerQueue(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)), _ihop_control(create_ihop_control(&_predictor)), - _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 3)), + _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)), _young_list_fixed_length(0), _short_lived_surv_rate_group(new SurvRateGroup()), _survivor_surv_rate_group(new SurvRateGroup()), diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 27fcf5c1adc..166a3d953b0 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -105,9 +105,9 @@ jint ParallelScavengeHeap::initialize() { (old_gen()->virtual_space()->high_boundary() == young_gen()->virtual_space()->low_boundary()), "Boundaries must meet"); - // initialize the policy counters - 2 collectors, 3 generations + // initialize the policy counters - 2 collectors, 2 generations _gc_policy_counters = - new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 3, _size_policy); + new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 2, _size_policy); // Set up the GCTaskManager _gc_task_manager = GCTaskManager::create(ParallelGCThreads); diff --git a/src/hotspot/share/gc/shared/collectorPolicy.cpp b/src/hotspot/share/gc/shared/collectorPolicy.cpp index 2d21024a3c7..7542662ee76 100644 --- a/src/hotspot/share/gc/shared/collectorPolicy.cpp +++ b/src/hotspot/share/gc/shared/collectorPolicy.cpp @@ -911,7 +911,7 @@ void MarkSweepPolicy::initialize_generations() { } void MarkSweepPolicy::initialize_gc_policy_counters() { - // Initialize the policy counters - 2 collectors, 3 generations. - _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 3); + // Initialize the policy counters - 2 collectors, 2 generations. + _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 2); } diff --git a/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java b/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java new file mode 100644 index 00000000000..d9b4c94c7fb --- /dev/null +++ b/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, 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. + */ + +import static jdk.test.lib.Asserts.*; +import gc.testlibrary.PerfCounter; +import gc.testlibrary.PerfCounters; + + +/* @test TestGenerationPerfCounter + * @bug 8080345 + * @requires vm.gc=="null" + * @library /test/lib / + * @summary Tests that the sun.gc.policy.generations returns 2 for all GCs. + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management/sun.management + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @run main/othervm -XX:+UsePerfData -XX:+UseSerialGC TestGenerationPerfCounter + * @run main/othervm -XX:+UsePerfData -XX:+UseParallelGC TestGenerationPerfCounter + * @run main/othervm -XX:+UsePerfData -XX:+UseG1GC TestGenerationPerfCounter + * @run main/othervm -XX:+UsePerfData -XX:+UseConcMarkSweepGC TestGenerationPerfCounter + */ +public class TestGenerationPerfCounter { + public static void main(String[] args) throws Exception { + long numGenerations = + PerfCounters.findByName("sun.gc.policy.generations").longValue(); + assertEQ(numGenerations, 2L); + } +} diff --git a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java index f648be9f484..24c3bb0e8c8 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java +++ b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java @@ -32,11 +32,13 @@ import jdk.test.lib.Platform; import sun.management.ManagementFactoryHelper; import static jdk.test.lib.Asserts.*; +import gc.testlibrary.PerfCounter; +import gc.testlibrary.PerfCounters; /* @test TestMetaspacePerfCounters * @bug 8014659 * @requires vm.gc=="null" - * @library /test/lib + * @library /test/lib / * @summary Tests that performance counters for metaspace and compressed class * space exists and works. * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java index a59e33f7f6a..56d1c002696 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java +++ b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java @@ -26,10 +26,12 @@ import java.lang.management.*; import jdk.test.lib.Platform; import static jdk.test.lib.Asserts.*; +import gc.testlibrary.PerfCounter; +import gc.testlibrary.PerfCounters; /* @test TestPerfCountersAndMemoryPools * @bug 8023476 - * @library /test/lib + * @library /test/lib / * @requires vm.gc.Serial * @summary Tests that a MemoryPoolMXBeans and PerfCounters for metaspace * report the same data. diff --git a/test/hotspot/jtreg/gc/metaspace/PerfCounter.java b/test/hotspot/jtreg/gc/testlibrary/PerfCounter.java similarity index 98% rename from test/hotspot/jtreg/gc/metaspace/PerfCounter.java rename to test/hotspot/jtreg/gc/testlibrary/PerfCounter.java index d39277c910e..2cafcdfb9fc 100644 --- a/test/hotspot/jtreg/gc/metaspace/PerfCounter.java +++ b/test/hotspot/jtreg/gc/testlibrary/PerfCounter.java @@ -21,6 +21,8 @@ * questions. */ +package gc.testlibrary; + import sun.jvmstat.monitor.Monitor; /** diff --git a/test/hotspot/jtreg/gc/metaspace/PerfCounters.java b/test/hotspot/jtreg/gc/testlibrary/PerfCounters.java similarity index 99% rename from test/hotspot/jtreg/gc/metaspace/PerfCounters.java rename to test/hotspot/jtreg/gc/testlibrary/PerfCounters.java index c1242b1bee5..b6e6a1f1da1 100644 --- a/test/hotspot/jtreg/gc/metaspace/PerfCounters.java +++ b/test/hotspot/jtreg/gc/testlibrary/PerfCounters.java @@ -21,6 +21,8 @@ * questions. */ +package gc.testlibrary; + import sun.jvmstat.monitor.Monitor; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; From d54de52f138f9ef9a3ff21cc38ee38057248b861 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann <thartmann@openjdk.org> Date: Mon, 27 Nov 2017 11:39:21 +0100 Subject: [PATCH 022/165] 8179026: Remove explicit code cache options processing Removed explicit processing of code cache related options because generic processing already handles these. Reviewed-by: kvn --- src/hotspot/share/code/codeCache.cpp | 10 +++-- src/hotspot/share/runtime/arguments.cpp | 58 +------------------------ src/hotspot/share/runtime/globals.hpp | 8 ++-- 3 files changed, 11 insertions(+), 65 deletions(-) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index b49892b1bf0..36d15a046d7 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -259,12 +259,12 @@ void CodeCache::initialize_heaps() { } // We do not need the profiled CodeHeap, use all space for the non-profiled CodeHeap - if(!heap_available(CodeBlobType::MethodProfiled)) { + if (!heap_available(CodeBlobType::MethodProfiled)) { non_profiled_size += profiled_size; profiled_size = 0; } // We do not need the non-profiled CodeHeap, use all space for the non-nmethod CodeHeap - if(!heap_available(CodeBlobType::MethodNonProfiled)) { + if (!heap_available(CodeBlobType::MethodNonProfiled)) { non_nmethod_size += non_profiled_size; non_profiled_size = 0; } @@ -332,7 +332,8 @@ ReservedCodeSpace CodeCache::reserve_heap_memory(size_t size) { ReservedCodeSpace rs(r_size, rs_align, rs_align > 0); if (!rs.is_reserved()) { - vm_exit_during_initialization("Could not reserve enough space for code cache"); + vm_exit_during_initialization(err_msg("Could not reserve enough space for code cache (" SIZE_FORMAT "K)", + r_size/K)); } // Initialize bounds @@ -415,7 +416,8 @@ void CodeCache::add_heap(ReservedSpace rs, const char* name, int code_blob_type) size_t size_initial = MIN2(InitialCodeCacheSize, rs.size()); size_initial = align_up(size_initial, os::vm_page_size()); if (!heap->reserve(rs, size_initial, CodeCacheSegmentSize)) { - vm_exit_during_initialization("Could not reserve enough space for code cache"); + vm_exit_during_initialization(err_msg("Could not reserve enough space in %s (" SIZE_FORMAT "K)", + heap->name(), size_initial/K)); } // Register the CodeHeap diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index de4adedf8f2..94f0a811112 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2152,12 +2152,7 @@ bool Arguments::check_vm_args_consistency() { // Check lower bounds of the code cache // Template Interpreter code is approximately 3X larger in debug builds. uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3); - if (InitialCodeCacheSize < (uintx)os::vm_page_size()) { - jio_fprintf(defaultStream::error_stream(), - "Invalid InitialCodeCacheSize=%dK. Must be at least %dK.\n", InitialCodeCacheSize/K, - os::vm_page_size()/K); - status = false; - } else if (ReservedCodeCacheSize < InitialCodeCacheSize) { + if (ReservedCodeCacheSize < InitialCodeCacheSize) { jio_fprintf(defaultStream::error_stream(), "Invalid ReservedCodeCacheSize: %dK. Must be at least InitialCodeCacheSize=%dK.\n", ReservedCodeCacheSize/K, InitialCodeCacheSize/K); @@ -2770,18 +2765,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m if (FLAG_SET_CMDLINE(intx, ThreadStackSize, value) != Flag::SUCCESS) { return JNI_EINVAL; } - } else if (match_option(option, "-XX:CodeCacheExpansionSize=", &tail)) { - julong long_CodeCacheExpansionSize = 0; - ArgsRange errcode = parse_memory_size(tail, &long_CodeCacheExpansionSize, os::vm_page_size()); - if (errcode != arg_in_range) { - jio_fprintf(defaultStream::error_stream(), - "Invalid argument: %s. Must be at least %luK.\n", option->optionString, - os::vm_page_size()/K); - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(uintx, CodeCacheExpansionSize, (uintx)long_CodeCacheExpansionSize) != Flag::SUCCESS) { - return JNI_EINVAL; - } } else if (match_option(option, "-Xmaxjitcodesize", &tail) || match_option(option, "-XX:ReservedCodeCacheSize=", &tail)) { julong long_ReservedCodeCacheSize = 0; @@ -2795,45 +2778,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m if (FLAG_SET_CMDLINE(uintx, ReservedCodeCacheSize, (uintx)long_ReservedCodeCacheSize) != Flag::SUCCESS) { return JNI_EINVAL; } - // -XX:NonNMethodCodeHeapSize= - } else if (match_option(option, "-XX:NonNMethodCodeHeapSize=", &tail)) { - julong long_NonNMethodCodeHeapSize = 0; - - ArgsRange errcode = parse_memory_size(tail, &long_NonNMethodCodeHeapSize, 1); - if (errcode != arg_in_range) { - jio_fprintf(defaultStream::error_stream(), - "Invalid maximum non-nmethod code heap size: %s.\n", option->optionString); - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(uintx, NonNMethodCodeHeapSize, (uintx)long_NonNMethodCodeHeapSize) != Flag::SUCCESS) { - return JNI_EINVAL; - } - // -XX:ProfiledCodeHeapSize= - } else if (match_option(option, "-XX:ProfiledCodeHeapSize=", &tail)) { - julong long_ProfiledCodeHeapSize = 0; - - ArgsRange errcode = parse_memory_size(tail, &long_ProfiledCodeHeapSize, 1); - if (errcode != arg_in_range) { - jio_fprintf(defaultStream::error_stream(), - "Invalid maximum profiled code heap size: %s.\n", option->optionString); - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(uintx, ProfiledCodeHeapSize, (uintx)long_ProfiledCodeHeapSize) != Flag::SUCCESS) { - return JNI_EINVAL; - } - // -XX:NonProfiledCodeHeapSizee= - } else if (match_option(option, "-XX:NonProfiledCodeHeapSize=", &tail)) { - julong long_NonProfiledCodeHeapSize = 0; - - ArgsRange errcode = parse_memory_size(tail, &long_NonProfiledCodeHeapSize, 1); - if (errcode != arg_in_range) { - jio_fprintf(defaultStream::error_stream(), - "Invalid maximum non-profiled code heap size: %s.\n", option->optionString); - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(uintx, NonProfiledCodeHeapSize, (uintx)long_NonProfiledCodeHeapSize) != Flag::SUCCESS) { - return JNI_EINVAL; - } // -green } else if (match_option(option, "-green")) { jio_fprintf(defaultStream::error_stream(), diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 90a8b0c78b6..68f95ad58a3 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -3365,7 +3365,7 @@ public: \ product_pd(uintx, InitialCodeCacheSize, \ "Initial code cache size (in bytes)") \ - range(0, max_uintx) \ + range(os::vm_page_size(), max_uintx) \ \ develop_pd(uintx, CodeCacheMinimumUseSpace, \ "Minimum code cache size (in bytes) required to start VM.") \ @@ -3376,7 +3376,7 @@ public: \ product_pd(uintx, ReservedCodeCacheSize, \ "Reserved code cache size (in bytes) - maximum code cache size") \ - range(0, max_uintx) \ + range(os::vm_page_size(), max_uintx) \ \ product_pd(uintx, NonProfiledCodeHeapSize, \ "Size of code heap with non-profiled methods (in bytes)") \ @@ -3388,11 +3388,11 @@ public: \ product_pd(uintx, NonNMethodCodeHeapSize, \ "Size of code heap with non-nmethods (in bytes)") \ - range(0, max_uintx) \ + range(os::vm_page_size(), max_uintx) \ \ product_pd(uintx, CodeCacheExpansionSize, \ "Code cache expansion size (in bytes)") \ - range(0, max_uintx) \ + range(os::vm_page_size(), max_uintx) \ \ diagnostic_pd(uintx, CodeCacheMinBlockLength, \ "Minimum number of segments in a code cache block") \ From 925a508b2bb1600909418405e9e5ea1a93a94580 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann <thartmann@openjdk.org> Date: Mon, 27 Nov 2017 11:41:00 +0100 Subject: [PATCH 023/165] 8087339: The code heap might use different alignment for committed size and reserved size InitialCodeCacheSize should not constrain code cache memory alignment. Reviewed-by: kvn --- src/hotspot/share/code/codeCache.cpp | 42 +++++++++++----------------- src/hotspot/share/code/codeCache.hpp | 2 +- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 36d15a046d7..cf37f8db29c 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -282,10 +282,11 @@ void CodeCache::initialize_heaps() { FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, profiled_size); FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, non_profiled_size); - // Align CodeHeaps - size_t alignment = heap_alignment(); + // If large page support is enabled, align code heaps according to large + // page size to make sure that code cache is covered by large pages. + const size_t alignment = MAX2(page_size(false), (size_t) os::vm_allocation_granularity()); non_nmethod_size = align_up(non_nmethod_size, alignment); - profiled_size = align_down(profiled_size, alignment); + profiled_size = align_down(profiled_size, alignment); // Reserve one continuous chunk of memory for CodeHeaps and split it into // parts for the individual heaps. The memory layout looks like this: @@ -308,38 +309,29 @@ void CodeCache::initialize_heaps() { add_heap(non_profiled_space, "CodeHeap 'non-profiled nmethods'", CodeBlobType::MethodNonProfiled); } -size_t CodeCache::heap_alignment() { - // If large page support is enabled, align code heaps according to large - // page size to make sure that code cache is covered by large pages. - const size_t page_size = os::can_execute_large_page_memory() ? - os::page_size_for_region_unaligned(ReservedCodeCacheSize, 8) : - os::vm_page_size(); - return MAX2(page_size, (size_t) os::vm_allocation_granularity()); +size_t CodeCache::page_size(bool aligned) { + if (os::can_execute_large_page_memory()) { + return aligned ? os::page_size_for_region_aligned(ReservedCodeCacheSize, 8) : + os::page_size_for_region_unaligned(ReservedCodeCacheSize, 8); + } else { + return os::vm_page_size(); + } } ReservedCodeSpace CodeCache::reserve_heap_memory(size_t size) { - // Determine alignment - const size_t page_size = os::can_execute_large_page_memory() ? - MIN2(os::page_size_for_region_aligned(InitialCodeCacheSize, 8), - os::page_size_for_region_aligned(size, 8)) : - os::vm_page_size(); - const size_t granularity = os::vm_allocation_granularity(); - const size_t r_align = MAX2(page_size, granularity); - const size_t r_size = align_up(size, r_align); - const size_t rs_align = page_size == (size_t) os::vm_page_size() ? 0 : - MAX2(page_size, granularity); - - ReservedCodeSpace rs(r_size, rs_align, rs_align > 0); - + // Align and reserve space for code cache + const size_t rs_ps = page_size(); + const size_t rs_align = MAX2(rs_ps, (size_t) os::vm_allocation_granularity()); + const size_t rs_size = align_up(size, rs_align); + ReservedCodeSpace rs(rs_size, rs_align, rs_ps > (size_t) os::vm_page_size()); if (!rs.is_reserved()) { vm_exit_during_initialization(err_msg("Could not reserve enough space for code cache (" SIZE_FORMAT "K)", - r_size/K)); + rs_size/K)); } // Initialize bounds _low_bound = (address)rs.base(); _high_bound = _low_bound + rs.size(); - return rs; } diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 2749acd05b7..e8a098d0279 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -107,7 +107,7 @@ class CodeCache : AllStatic { static CodeHeap* get_code_heap(int code_blob_type); // Returns the CodeHeap for the given CodeBlobType // Returns the name of the VM option to set the size of the corresponding CodeHeap static const char* get_code_heap_flag_name(int code_blob_type); - static size_t heap_alignment(); // Returns the alignment of the CodeHeaps in bytes + static size_t page_size(bool aligned = true); // Returns the page size used by the CodeCache static ReservedCodeSpace reserve_heap_memory(size_t size); // Reserves one continuous chunk of memory for the CodeHeaps // Iteration From 250c05ee4cce2cfc173870b6869f1dca18aae452 Mon Sep 17 00:00:00 2001 From: Rahul Raghavan <rraghavan@openjdk.org> Date: Mon, 27 Nov 2017 03:11:38 -0800 Subject: [PATCH 024/165] 8191227: issues with unsafe handle resolution Added ThreadInVMfromNative or ThreadInVMfromUnknown support Reviewed-by: thartmann, vlivanov --- .../cpu/sparc/c1_LIRAssembler_sparc.cpp | 7 ++++++- src/hotspot/share/code/debugInfo.cpp | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp index 1f3dc374cce..c9b250eb06f 100644 --- a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp +++ b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp @@ -398,8 +398,13 @@ void LIR_Assembler::jobject2reg(jobject o, Register reg) { if (o == NULL) { __ set(NULL_WORD, reg); } else { +#ifdef ASSERT + { + ThreadInVMfromNative tiv(JavaThread::current()); + assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop"); + } +#endif int oop_index = __ oop_recorder()->find_index(o); - assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop"); RelocationHolder rspec = oop_Relocation::spec(oop_index); __ set(NULL_WORD, reg, rspec); // Will be set when the nmethod is created } diff --git a/src/hotspot/share/code/debugInfo.cpp b/src/hotspot/share/code/debugInfo.cpp index 76a92387528..c81ef90e9d0 100644 --- a/src/hotspot/share/code/debugInfo.cpp +++ b/src/hotspot/share/code/debugInfo.cpp @@ -28,6 +28,8 @@ #include "code/nmethod.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/thread.hpp" // Constructors @@ -209,14 +211,24 @@ void ConstantDoubleValue::print_on(outputStream* st) const { // ConstantOopWriteValue void ConstantOopWriteValue::write_on(DebugInfoWriteStream* stream) { - assert(JNIHandles::resolve(value()) == NULL || - Universe::heap()->is_in_reserved(JNIHandles::resolve(value())), - "Should be in heap"); +#ifdef ASSERT + { + // cannot use ThreadInVMfromNative here since in case of JVMCI compiler, + // thread is already in VM state. + ThreadInVMfromUnknown tiv; + assert(JNIHandles::resolve(value()) == NULL || + Universe::heap()->is_in_reserved(JNIHandles::resolve(value())), + "Should be in heap"); + } +#endif stream->write_int(CONSTANT_OOP_CODE); stream->write_handle(value()); } void ConstantOopWriteValue::print_on(outputStream* st) const { + // using ThreadInVMfromUnknown here since in case of JVMCI compiler, + // thread is already in VM state. + ThreadInVMfromUnknown tiv; JNIHandles::resolve(value())->print_value_on(st); } From 9771e4daa710a948253756f781ff58fef5c2ec23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Lid=C3=A9n?= <pliden@openjdk.org> Date: Mon, 27 Nov 2017 12:38:47 +0100 Subject: [PATCH 025/165] 8191846: jstat prints debug message when debugging is disabled Reviewed-by: ehelin, rehn --- .../share/classes/sun/tools/jstat/ExpressionResolver.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java b/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java index 63505988b8e..58b02dc4a77 100644 --- a/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java +++ b/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java @@ -69,8 +69,10 @@ public class ExpressionResolver implements ExpressionEvaluator { // look it up Monitor m = vm.findByName(id.getName()); if (m == null) { - System.err.println("Warning: Unresolved Symbol: " - + id.getName() + " substituted NaN"); + if (debug) { + System.err.println("Warning: Unresolved Symbol: " + + id.getName() + " substituted NaN"); + } return new Literal(Double.valueOf(Double.NaN)); } if (m.getVariability() == Variability.CONSTANT) { From 1e7b0b326d516309d836d7fb7c7f8ff6237b2891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Lid=C3=A9n?= <pliden@openjdk.org> Date: Mon, 27 Nov 2017 13:34:00 +0100 Subject: [PATCH 026/165] 8191862: Warn about UseNUMA/UseLargePages only when using ParallelGC Reviewed-by: stefank, sjohanss --- src/hotspot/os/linux/os_linux.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 9b52aba4574..27fe2dab85a 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4948,25 +4948,20 @@ jint os::init_2(void) { UseNUMA = false; } } - // With SHM and HugeTLBFS large pages we cannot uncommit a page, so there's no way - // we can make the adaptive lgrp chunk resizing work. If the user specified - // both UseNUMA and UseLargePages (or UseSHM/UseHugeTLBFS) on the command line - warn and - // disable adaptive resizing. - if (UseNUMA && UseLargePages && !can_commit_large_page_memory()) { - if (FLAG_IS_DEFAULT(UseNUMA)) { - UseNUMA = false; - } else { - if (FLAG_IS_DEFAULT(UseLargePages) && - FLAG_IS_DEFAULT(UseSHM) && - FLAG_IS_DEFAULT(UseHugeTLBFS)) { - UseLargePages = false; - } else if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) { - warning("UseNUMA is not fully compatible with SHM/HugeTLBFS large pages, disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)"); - UseAdaptiveSizePolicy = false; - UseAdaptiveNUMAChunkSizing = false; - } + + if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) { + // With SHM and HugeTLBFS large pages we cannot uncommit a page, so there's no way + // we can make the adaptive lgrp chunk resizing work. If the user specified both + // UseNUMA and UseLargePages (or UseSHM/UseHugeTLBFS) on the command line - warn + // and disable adaptive resizing. + if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) { + warning("UseNUMA is not fully compatible with SHM/HugeTLBFS large pages, " + "disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)"); + UseAdaptiveSizePolicy = false; + UseAdaptiveNUMAChunkSizing = false; } } + if (!UseNUMA && ForceNUMA) { UseNUMA = true; } From bfc0314e87034024f3667023a3a1feb7d3085968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Lid=C3=A9n?= <pliden@openjdk.org> Date: Mon, 27 Nov 2017 13:50:03 +0100 Subject: [PATCH 027/165] 8191864: Provide a public destructor for WorkGang Reviewed-by: stefank, sjohanss --- src/hotspot/share/gc/shared/workgroup.cpp | 4 ++++ src/hotspot/share/gc/shared/workgroup.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shared/workgroup.cpp b/src/hotspot/share/gc/shared/workgroup.cpp index 73b3a2a55ab..2538e701fc9 100644 --- a/src/hotspot/share/gc/shared/workgroup.cpp +++ b/src/hotspot/share/gc/shared/workgroup.cpp @@ -261,6 +261,10 @@ WorkGang::WorkGang(const char* name, _dispatcher(create_dispatcher()) { } +WorkGang::~WorkGang() { + delete _dispatcher; +} + AbstractGangWorker* WorkGang::allocate_worker(uint worker_id) { return new GangWorker(this, worker_id); } diff --git a/src/hotspot/share/gc/shared/workgroup.hpp b/src/hotspot/share/gc/shared/workgroup.hpp index 320652a993c..c1b50f3885d 100644 --- a/src/hotspot/share/gc/shared/workgroup.hpp +++ b/src/hotspot/share/gc/shared/workgroup.hpp @@ -122,6 +122,8 @@ class AbstractWorkGang : public CHeapObj<mtInternal> { // Printing support. const char* _name; + ~AbstractWorkGang() {} + private: // Initialize only instance data. const bool _are_GC_task_threads; @@ -206,9 +208,6 @@ class WorkGang: public AbstractWorkGang { // To get access to the GangTaskDispatcher instance. friend class GangWorker; - // Never deleted. - ~WorkGang(); - GangTaskDispatcher* const _dispatcher; GangTaskDispatcher* dispatcher() const { return _dispatcher; @@ -220,6 +219,8 @@ public: bool are_GC_task_threads, bool are_ConcurrentGC_threads); + ~WorkGang(); + // Run a task using the current active number of workers, returns when the task is done. virtual void run_task(AbstractGangTask* task); // Run a task with the given number of workers, returns From f25e51e643bcd91cc2d5e7b2483bce0ab0e0d935 Mon Sep 17 00:00:00 2001 From: Gustavo Romero <gromero@openjdk.org> Date: Mon, 27 Nov 2017 14:59:36 +0100 Subject: [PATCH 028/165] 8191868: IdealGraphVisualizer: "ant build/run" fails due to outdated bootstrap.url Fixed bootstrap.url to point to correct server. Reviewed-by: thartmann --- src/utils/IdealGraphVisualizer/nbproject/platform.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/IdealGraphVisualizer/nbproject/platform.properties b/src/utils/IdealGraphVisualizer/nbproject/platform.properties index c86f8f4227d..3e4aeabf98e 100644 --- a/src/utils/IdealGraphVisualizer/nbproject/platform.properties +++ b/src/utils/IdealGraphVisualizer/nbproject/platform.properties @@ -6,7 +6,7 @@ disabled.modules= nbplatform.active.dir=${suite.dir}/nbplatform nbplatform.default.netbeans.dest.dir=${suite.dir}/nbplatform nbplatform.default.harness.dir=${nbplatform.default.netbeans.dest.dir}/harness -bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/netbeans/harness/tasks.jar +bootstrap.url=http://bits.netbeans.org/dev/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/netbeans/harness/tasks.jar autoupdate.catalog.url=http://updates.netbeans.org/netbeans/updates/7.4/uc/final/distribution/catalog.xml.gz suite.dir=${basedir} nbplatform.active=default From b76a492de170f579b0dc43a3595be32246b544de Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@openjdk.org> Date: Tue, 21 Nov 2017 13:48:28 +0100 Subject: [PATCH 029/165] 8187227: Zero should use compiler built-ins for atomics on linux-m68k Reviewed-by: aph, kbarrett --- .../os_cpu/linux_zero/atomic_linux_zero.hpp | 80 ------------------- 1 file changed, 80 deletions(-) diff --git a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp index 0713b6de460..af9eb3fb85c 100644 --- a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp +++ b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp @@ -30,74 +30,6 @@ // Implementation of class atomic -#ifdef M68K - -/* - * __m68k_cmpxchg - * - * Atomically store newval in *ptr if *ptr is equal to oldval for user space. - * Returns newval on success and oldval if no exchange happened. - * This implementation is processor specific and works on - * 68020 68030 68040 and 68060. - * - * It will not work on ColdFire, 68000 and 68010 since they lack the CAS - * instruction. - * Using a kernelhelper would be better for arch complete implementation. - * - */ - -static inline int __m68k_cmpxchg(int oldval, int newval, volatile int *ptr) { - int ret; - __asm __volatile ("cas%.l %0,%2,%1" - : "=d" (ret), "+m" (*(ptr)) - : "d" (newval), "0" (oldval)); - return ret; -} - -/* Perform an atomic compare and swap: if the current value of `*PTR' - is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of - `*PTR' before the operation.*/ -static inline int m68k_compare_and_swap(int newval, - volatile int *ptr, - int oldval) { - for (;;) { - int prev = *ptr; - if (prev != oldval) - return prev; - - if (__m68k_cmpxchg (prev, newval, ptr) == newval) - // Success. - return prev; - - // We failed even though prev == oldval. Try again. - } -} - -/* Atomically add an int to memory. */ -static inline int m68k_add_and_fetch(int add_value, volatile int *ptr) { - for (;;) { - // Loop until success. - - int prev = *ptr; - - if (__m68k_cmpxchg (prev, prev + add_value, ptr) == prev + add_value) - return prev + add_value; - } -} - -/* Atomically write VALUE into `*PTR' and returns the previous - contents of `*PTR'. */ -static inline int m68k_lock_test_and_set(int newval, volatile int *ptr) { - for (;;) { - // Loop until success. - int prev = *ptr; - - if (__m68k_cmpxchg (prev, newval, ptr) == prev) - return prev; - } -} -#endif // M68K - #ifdef ARM /* @@ -175,12 +107,8 @@ inline D Atomic::PlatformAdd<4>::add_and_fetch(I add_value, D volatile* dest) co #ifdef ARM return add_using_helper<int>(arm_add_and_fetch, add_value, dest); -#else -#ifdef M68K - return add_using_helper<int>(m68k_add_and_fetch, add_value, dest); #else return __sync_add_and_fetch(dest, add_value); -#endif // M68K #endif // ARM } @@ -200,9 +128,6 @@ inline T Atomic::PlatformXchg<4>::operator()(T exchange_value, STATIC_ASSERT(4 == sizeof(T)); #ifdef ARM return xchg_using_helper<int>(arm_lock_test_and_set, exchange_value, dest); -#else -#ifdef M68K - return xchg_using_helper<int>(m68k_lock_test_and_set, exchange_value, dest); #else // __sync_lock_test_and_set is a bizarrely named atomic exchange // operation. Note that some platforms only support this with the @@ -215,7 +140,6 @@ inline T Atomic::PlatformXchg<4>::operator()(T exchange_value, // barrier. __sync_synchronize(); return result; -#endif // M68K #endif // ARM } @@ -242,12 +166,8 @@ inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value, STATIC_ASSERT(4 == sizeof(T)); #ifdef ARM return cmpxchg_using_helper<int>(arm_compare_and_swap, exchange_value, dest, compare_value); -#else -#ifdef M68K - return cmpxchg_using_helper<int>(m68k_compare_and_swap, exchange_value, dest, compare_value); #else return __sync_val_compare_and_swap(dest, compare_value, exchange_value); -#endif // M68K #endif // ARM } From b6d123f660a2fcee8e037fa56591f01b053cfad8 Mon Sep 17 00:00:00 2001 From: Igor Veresov <iveresov@openjdk.org> Date: Wed, 22 Nov 2017 01:12:23 -0800 Subject: [PATCH 030/165] 8191683: Compile problem on ARM after JDK-8043070 Make nmethod::_state explicitly a signed char Reviewed-by: thartmann --- src/hotspot/share/code/nmethod.hpp | 2 +- src/hotspot/share/runtime/vmStructs.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 4ac6ab9226d..c2862c84a0b 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -124,7 +124,7 @@ class nmethod : public CompiledMethod { bool _unload_reported; // Protected by Patching_lock - volatile char _state; // {not_installed, in_use, not_entrant, zombie, unloaded} + volatile signed char _state; // {not_installed, in_use, not_entrant, zombie, unloaded} #ifdef ASSERT bool _oops_are_stale; // indicates that it's no longer safe to access oops section diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 0466f7ae7db..f1b0eac2407 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -830,7 +830,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; nonstatic_field(nmethod, _osr_link, nmethod*) \ nonstatic_field(nmethod, _scavenge_root_link, nmethod*) \ nonstatic_field(nmethod, _scavenge_root_state, jbyte) \ - nonstatic_field(nmethod, _state, volatile char) \ + nonstatic_field(nmethod, _state, volatile signed char) \ nonstatic_field(nmethod, _exception_offset, int) \ nonstatic_field(nmethod, _orig_pc_offset, int) \ nonstatic_field(nmethod, _stub_offset, int) \ @@ -1350,8 +1350,8 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_integer_type(int) \ declare_integer_type(long) \ declare_integer_type(char) \ + declare_integer_type(volatile signed char) \ declare_unsigned_integer_type(unsigned char) \ - declare_unsigned_integer_type(volatile char) \ declare_unsigned_integer_type(u_char) \ declare_unsigned_integer_type(unsigned int) \ declare_unsigned_integer_type(uint) \ From 9b6a4282122377dd044074b19b933ff784a5d584 Mon Sep 17 00:00:00 2001 From: Razvan Lupusoru <rlupusoru@openjdk.org> Date: Wed, 22 Nov 2017 14:43:37 +0300 Subject: [PATCH 031/165] 8190800: Support vectorization of Math.sqrt() on floats Reviewed-by: vlivanov, kvn --- src/hotspot/cpu/x86/assembler_x86.cpp | 18 +++++ src/hotspot/cpu/x86/assembler_x86.hpp | 4 +- src/hotspot/cpu/x86/x86.ad | 98 ++++++++++++++++++++++++- src/hotspot/share/adlc/formssel.cpp | 3 +- src/hotspot/share/opto/classes.hpp | 2 + src/hotspot/share/opto/convertnode.cpp | 15 ++++ src/hotspot/share/opto/convertnode.hpp | 1 + src/hotspot/share/opto/subnode.cpp | 9 +++ src/hotspot/share/opto/subnode.hpp | 14 ++++ src/hotspot/share/opto/superword.cpp | 2 +- src/hotspot/share/opto/vectornode.cpp | 5 +- src/hotspot/share/opto/vectornode.hpp | 8 ++ src/hotspot/share/runtime/vmStructs.cpp | 1 + 13 files changed, 172 insertions(+), 8 deletions(-) diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 3bdc0ed3c30..1156969b193 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -5203,6 +5203,24 @@ void Assembler::vsqrtpd(XMMRegister dst, Address src, int vector_len) { emit_operand(dst, src); } +void Assembler::vsqrtps(XMMRegister dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::vsqrtps(XMMRegister dst, Address src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_operand(dst, src); +} + void Assembler::andpd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index c4e6645e319..2739cf3b5eb 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1919,9 +1919,11 @@ private: void vdivpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vdivps(XMMRegister dst, XMMRegister nds, Address src, int vector_len); - // Sqrt Packed Floating-Point Values - Double precision only + // Sqrt Packed Floating-Point Values void vsqrtpd(XMMRegister dst, XMMRegister src, int vector_len); void vsqrtpd(XMMRegister dst, Address src, int vector_len); + void vsqrtps(XMMRegister dst, XMMRegister src, int vector_len); + void vsqrtps(XMMRegister dst, Address src, int vector_len); // Bitwise Logical AND of Packed Floating-Point Values void andpd(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index afaa2da23c7..124e95a4dcd 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1252,6 +1252,7 @@ const bool Matcher::match_rule_supported(int opcode) { ret_value = false; break; case Op_SqrtVD: + case Op_SqrtVF: if (UseAVX < 1) // enabled for AVX only ret_value = false; break; @@ -2580,7 +2581,7 @@ instruct negD_reg_reg(regD dst, regD src) %{ instruct sqrtF_reg(regF dst, regF src) %{ predicate(UseSSE>=1); - match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + match(Set dst (SqrtF src)); format %{ "sqrtss $dst, $src" %} ins_cost(150); @@ -2592,7 +2593,7 @@ instruct sqrtF_reg(regF dst, regF src) %{ instruct sqrtF_mem(regF dst, memory src) %{ predicate(UseSSE>=1); - match(Set dst (ConvD2F (SqrtD (ConvF2D (LoadF src))))); + match(Set dst (SqrtF (LoadF src))); format %{ "sqrtss $dst, $src" %} ins_cost(150); @@ -2604,7 +2605,8 @@ instruct sqrtF_mem(regF dst, memory src) %{ instruct sqrtF_imm(regF dst, immF con) %{ predicate(UseSSE>=1); - match(Set dst (ConvD2F (SqrtD (ConvF2D con)))); + match(Set dst (SqrtF con)); + format %{ "sqrtss $dst, [$constantaddress]\t# load from constant table: float=$con" %} ins_cost(150); ins_encode %{ @@ -8388,7 +8390,7 @@ instruct vshiftcnt(vecS dst, rRegI cnt) %{ // --------------------------------- Sqrt -------------------------------------- -// Floating point vector sqrt - double precision only +// Floating point vector sqrt instruct vsqrt2D_reg(vecX dst, vecX src) %{ predicate(UseAVX > 0 && n->as_Vector()->length() == 2); match(Set dst (SqrtVD src)); @@ -8455,6 +8457,94 @@ instruct vsqrt8D_mem(vecZ dst, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsqrt2F_reg(vecD dst, vecD src) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 2); + match(Set dst (SqrtVF src)); + format %{ "vsqrtps $dst,$src\t! sqrt packed2F" %} + ins_encode %{ + int vector_len = 0; + __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt2F_mem(vecD dst, memory mem) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 2); + match(Set dst (SqrtVF (LoadVector mem))); + format %{ "vsqrtps $dst,$mem\t! sqrt packed2F" %} + ins_encode %{ + int vector_len = 0; + __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt4F_reg(vecX dst, vecX src) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 4); + match(Set dst (SqrtVF src)); + format %{ "vsqrtps $dst,$src\t! sqrt packed4F" %} + ins_encode %{ + int vector_len = 0; + __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt4F_mem(vecX dst, memory mem) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 4); + match(Set dst (SqrtVF (LoadVector mem))); + format %{ "vsqrtps $dst,$mem\t! sqrt packed4F" %} + ins_encode %{ + int vector_len = 0; + __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt8F_reg(vecY dst, vecY src) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 8); + match(Set dst (SqrtVF src)); + format %{ "vsqrtps $dst,$src\t! sqrt packed8F" %} + ins_encode %{ + int vector_len = 1; + __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt8F_mem(vecY dst, memory mem) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 8); + match(Set dst (SqrtVF (LoadVector mem))); + format %{ "vsqrtps $dst,$mem\t! sqrt packed8F" %} + ins_encode %{ + int vector_len = 1; + __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt16F_reg(vecZ dst, vecZ src) %{ + predicate(UseAVX > 2 && n->as_Vector()->length() == 16); + match(Set dst (SqrtVF src)); + format %{ "vsqrtps $dst,$src\t! sqrt packed16F" %} + ins_encode %{ + int vector_len = 2; + __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsqrt16F_mem(vecZ dst, memory mem) %{ + predicate(UseAVX > 2 && n->as_Vector()->length() == 16); + match(Set dst (SqrtVF (LoadVector mem))); + format %{ "vsqrtps $dst,$mem\t! sqrt packed16F" %} + ins_encode %{ + int vector_len = 2; + __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + // ------------------------------ LeftShift ----------------------------------- // Shorts/Chars vector left shift diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 73229920040..fafd0ce7b27 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -4034,6 +4034,7 @@ int MatchRule::is_expensive() const { strcmp(opType,"ModF")==0 || strcmp(opType,"ModI")==0 || strcmp(opType,"SqrtD")==0 || + strcmp(opType,"SqrtF")==0 || strcmp(opType,"TanD")==0 || strcmp(opType,"ConvD2F")==0 || strcmp(opType,"ConvD2I")==0 || @@ -4167,7 +4168,7 @@ bool MatchRule::is_vector() const { "DivVF","DivVD", "AbsVF","AbsVD", "NegVF","NegVD", - "SqrtVD", + "SqrtVD","SqrtVF", "AndV" ,"XorV" ,"OrV", "AddReductionVI", "AddReductionVL", "AddReductionVF", "AddReductionVD", diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 123ea3e0f28..b95178b8879 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -252,6 +252,7 @@ macro(SafePoint) macro(SafePointScalarObject) macro(SCMemProj) macro(SqrtD) +macro(SqrtF) macro(Start) macro(StartOSR) macro(StoreB) @@ -320,6 +321,7 @@ macro(AbsVD) macro(NegVF) macro(NegVD) macro(SqrtVD) +macro(SqrtVF) macro(LShiftCntV) macro(RShiftCntV) macro(LShiftVB) diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 5d97a7330f2..aaf0cc2fc61 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -73,6 +73,21 @@ const Type* ConvD2FNode::Value(PhaseGVN* phase) const { return TypeF::make( (float)td->getd() ); } +//------------------------------Ideal------------------------------------------ +// If we see pattern ConvF2D SomeDoubleOp ConvD2F, do operation as float. +Node *ConvD2FNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if ( in(1)->Opcode() == Op_SqrtD ) { + Node* sqrtd = in(1); + if ( sqrtd->in(1)->Opcode() == Op_ConvF2D ) { + if ( Matcher::match_rule_supported(Op_SqrtF) ) { + Node* convf2d = sqrtd->in(1); + return new SqrtFNode(phase->C, sqrtd->in(0), convf2d->in(1)); + } + } + } + return NULL; +} + //------------------------------Identity--------------------------------------- // Float's can be converted to doubles with no loss of bits. Hence // converting a float to a double and back to a float is a NOP. diff --git a/src/hotspot/share/opto/convertnode.hpp b/src/hotspot/share/opto/convertnode.hpp index 0a3e78b18dc..6ae7de5882f 100644 --- a/src/hotspot/share/opto/convertnode.hpp +++ b/src/hotspot/share/opto/convertnode.hpp @@ -51,6 +51,7 @@ class ConvD2FNode : public Node { virtual const Type *bottom_type() const { return Type::FLOAT; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual uint ideal_reg() const { return Op_RegF; } }; diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index cc1d881f10d..f53c9eb14f8 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1595,3 +1595,12 @@ const Type* SqrtDNode::Value(PhaseGVN* phase) const { if( d < 0.0 ) return Type::DOUBLE; return TypeD::make( sqrt( d ) ); } + +const Type* SqrtFNode::Value(PhaseGVN* phase) const { + const Type *t1 = phase->type( in(1) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t1->base() != Type::FloatCon ) return Type::FLOAT; + float f = t1->getf(); + if( f < 0.0f ) return Type::FLOAT; + return TypeF::make( (float)sqrt( (double)f ) ); +} diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp index a4adbcf5a47..68aa1a3c0b8 100644 --- a/src/hotspot/share/opto/subnode.hpp +++ b/src/hotspot/share/opto/subnode.hpp @@ -442,6 +442,20 @@ public: virtual const Type* Value(PhaseGVN* phase) const; }; +//------------------------------SqrtFNode-------------------------------------- +// square root a float +class SqrtFNode : public Node { +public: + SqrtFNode(Compile* C, Node *c, Node *in1) : Node(c, in1) { + init_flags(Flag_is_expensive); + C->add_expensive_node(this); + } + virtual int Opcode() const; + const Type *bottom_type() const { return Type::FLOAT; } + virtual uint ideal_reg() const { return Op_RegF; } + virtual const Type* Value(PhaseGVN* phase) const; +}; + //-------------------------------ReverseBytesINode-------------------------------- // reverse bytes of an integer class ReverseBytesINode : public Node { diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index b0afe8e5c74..a66df0a979d 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -2307,7 +2307,7 @@ void SuperWord::output() { vn = VectorNode::make(opc, in1, in2, vlen, velt_basic_type(n)); vlen_in_bytes = vn->as_Vector()->length_in_bytes(); } - } else if (opc == Op_SqrtD || opc == Op_AbsF || opc == Op_AbsD || opc == Op_NegF || opc == Op_NegD) { + } else if (opc == Op_SqrtF || opc == Op_SqrtD || opc == Op_AbsF || opc == Op_AbsD || opc == Op_NegF || opc == Op_NegD) { // Promote operand to vector (Sqrt/Abs/Neg are 2 address instructions) Node* in = vector_opd(p, 1); vn = VectorNode::make(opc, in, NULL, vlen, velt_basic_type(n)); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index ecf8247825e..57b0ecf0e7b 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -113,6 +113,9 @@ int VectorNode::opcode(int sopc, BasicType bt) { case Op_NegD: assert(bt == T_DOUBLE, "must be"); return Op_NegVD; + case Op_SqrtF: + assert(bt == T_FLOAT, "must be"); + return Op_SqrtVF; case Op_SqrtD: assert(bt == T_DOUBLE, "must be"); return Op_SqrtVD; @@ -316,7 +319,7 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b case Op_NegVF: return new NegVFNode(n1, vt); case Op_NegVD: return new NegVDNode(n1, vt); - // Currently only supports double precision sqrt + case Op_SqrtVF: return new SqrtVFNode(n1, vt); case Op_SqrtVD: return new SqrtVDNode(n1, vt); case Op_LShiftVB: return new LShiftVBNode(n1, n2, vt); diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 7b65aa9a91d..93da7bc4b7c 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -373,6 +373,14 @@ class NegVDNode : public VectorNode { virtual int Opcode() const; }; +//------------------------------SqrtVFNode-------------------------------------- +// Vector Sqrt float +class SqrtVFNode : public VectorNode { + public: + SqrtVFNode(Node* in, const TypeVect* vt) : VectorNode(in,vt) {} + virtual int Opcode() const; +}; + //------------------------------SqrtVDNode-------------------------------------- // Vector Sqrt double class SqrtVDNode : public VectorNode { diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index f1b0eac2407..ee435d854f7 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1958,6 +1958,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_c2_type(NegFNode, NegNode) \ declare_c2_type(NegDNode, NegNode) \ declare_c2_type(AtanDNode, Node) \ + declare_c2_type(SqrtFNode, Node) \ declare_c2_type(SqrtDNode, Node) \ declare_c2_type(ReverseBytesINode, Node) \ declare_c2_type(ReverseBytesLNode, Node) \ From 5713b7c2bd6ea18885ab8a0f9911f559f539442f Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Wed, 22 Nov 2017 14:53:20 +0100 Subject: [PATCH 032/165] 8191202: Remove duplicated jmm.h Reviewed-by: erikj, coleenp --- make/hotspot/lib/CompileJvm.gmk | 1 + src/hotspot/share/services/jmm.h | 345 ---------------------- src/hotspot/share/services/management.cpp | 2 +- src/hotspot/share/services/management.hpp | 2 +- 4 files changed, 3 insertions(+), 347 deletions(-) delete mode 100644 src/hotspot/share/services/jmm.h diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk index e71f77be7fd..126ac4f431a 100644 --- a/make/hotspot/lib/CompileJvm.gmk +++ b/make/hotspot/lib/CompileJvm.gmk @@ -59,6 +59,7 @@ JVM_CFLAGS_INCLUDES += \ -I$(TOPDIR)/src/hotspot/share/precompiled \ -I$(TOPDIR)/src/java.base/share/native/include \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/include \ + -I$(TOPDIR)/src/java.management/share/native/include \ -I$(TOPDIR)/src/java.base/share/native/libjimage \ # diff --git a/src/hotspot/share/services/jmm.h b/src/hotspot/share/services/jmm.h deleted file mode 100644 index df232f6feec..00000000000 --- a/src/hotspot/share/services/jmm.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (c) 2003, 2017, 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. 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. - */ - -#ifndef _JAVA_JMM_H_ -#define _JAVA_JMM_H_ - -/* - * This is a private interface used by JDK for JVM monitoring - * and management. - * - * Bump the version number when either of the following happens: - * - * 1. There is a change in functions in JmmInterface. - * - * 2. There is a change in the contract between VM and Java classes. - */ - -#include "jni.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - JMM_VERSION_1 = 0x20010000, - JMM_VERSION_1_0 = 0x20010000, - JMM_VERSION_1_1 = 0x20010100, // JDK 6 - JMM_VERSION_1_2 = 0x20010200, // JDK 7 - JMM_VERSION_1_2_1 = 0x20010201, // JDK 7 GA - JMM_VERSION_1_2_2 = 0x20010202, - JMM_VERSION_2 = 0x20020000, // JDK 10 - JMM_VERSION = 0x20020000 -}; - -typedef struct { - unsigned int isLowMemoryDetectionSupported : 1; - unsigned int isCompilationTimeMonitoringSupported : 1; - unsigned int isThreadContentionMonitoringSupported : 1; - unsigned int isCurrentThreadCpuTimeSupported : 1; - unsigned int isOtherThreadCpuTimeSupported : 1; - unsigned int isObjectMonitorUsageSupported : 1; - unsigned int isSynchronizerUsageSupported : 1; - unsigned int isThreadAllocatedMemorySupported : 1; - unsigned int isRemoteDiagnosticCommandsSupported : 1; - unsigned int : 22; -} jmmOptionalSupport; - -typedef enum { - JMM_CLASS_LOADED_COUNT = 1, /* Total number of loaded classes */ - JMM_CLASS_UNLOADED_COUNT = 2, /* Total number of unloaded classes */ - JMM_THREAD_TOTAL_COUNT = 3, /* Total number of threads that have been started */ - JMM_THREAD_LIVE_COUNT = 4, /* Current number of live threads */ - JMM_THREAD_PEAK_COUNT = 5, /* Peak number of live threads */ - JMM_THREAD_DAEMON_COUNT = 6, /* Current number of daemon threads */ - JMM_JVM_INIT_DONE_TIME_MS = 7, /* Time when the JVM finished initialization */ - JMM_COMPILE_TOTAL_TIME_MS = 8, /* Total accumulated time spent in compilation */ - JMM_GC_TIME_MS = 9, /* Total accumulated time spent in collection */ - JMM_GC_COUNT = 10, /* Total number of collections */ - JMM_JVM_UPTIME_MS = 11, /* The JVM uptime in milliseconds */ - - JMM_INTERNAL_ATTRIBUTE_INDEX = 100, - JMM_CLASS_LOADED_BYTES = 101, /* Number of bytes loaded instance classes */ - JMM_CLASS_UNLOADED_BYTES = 102, /* Number of bytes unloaded instance classes */ - JMM_TOTAL_CLASSLOAD_TIME_MS = 103, /* Accumulated VM class loader time (TraceClassLoadingTime) */ - JMM_VM_GLOBAL_COUNT = 104, /* Number of VM internal flags */ - JMM_SAFEPOINT_COUNT = 105, /* Total number of safepoints */ - JMM_TOTAL_SAFEPOINTSYNC_TIME_MS = 106, /* Accumulated time spent getting to safepoints */ - JMM_TOTAL_STOPPED_TIME_MS = 107, /* Accumulated time spent at safepoints */ - JMM_TOTAL_APP_TIME_MS = 108, /* Accumulated time spent in Java application */ - JMM_VM_THREAD_COUNT = 109, /* Current number of VM internal threads */ - JMM_CLASS_INIT_TOTAL_COUNT = 110, /* Number of classes for which initializers were run */ - JMM_CLASS_INIT_TOTAL_TIME_MS = 111, /* Accumulated time spent in class initializers */ - JMM_METHOD_DATA_SIZE_BYTES = 112, /* Size of method data in memory */ - JMM_CLASS_VERIFY_TOTAL_TIME_MS = 113, /* Accumulated time spent in class verifier */ - JMM_SHARED_CLASS_LOADED_COUNT = 114, /* Number of shared classes loaded */ - JMM_SHARED_CLASS_UNLOADED_COUNT = 115, /* Number of shared classes unloaded */ - JMM_SHARED_CLASS_LOADED_BYTES = 116, /* Number of bytes loaded shared classes */ - JMM_SHARED_CLASS_UNLOADED_BYTES = 117, /* Number of bytes unloaded shared classes */ - - JMM_OS_ATTRIBUTE_INDEX = 200, - JMM_OS_PROCESS_ID = 201, /* Process id of the JVM */ - JMM_OS_MEM_TOTAL_PHYSICAL_BYTES = 202, /* Physical memory size */ - - JMM_GC_EXT_ATTRIBUTE_INFO_SIZE = 401 /* the size of the GC specific attributes for a given GC memory manager */ -} jmmLongAttribute; - -typedef enum { - JMM_VERBOSE_GC = 21, - JMM_VERBOSE_CLASS = 22, - JMM_THREAD_CONTENTION_MONITORING = 23, - JMM_THREAD_CPU_TIME = 24, - JMM_THREAD_ALLOCATED_MEMORY = 25 -} jmmBoolAttribute; - - -enum { - JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000, - JMM_THREAD_STATE_FLAG_NATIVE = 0x00400000 -}; - -#define JMM_THREAD_STATE_FLAG_MASK 0xFFF00000 - -typedef enum { - JMM_STAT_PEAK_THREAD_COUNT = 801, - JMM_STAT_THREAD_CONTENTION_COUNT = 802, - JMM_STAT_THREAD_CONTENTION_TIME = 803, - JMM_STAT_THREAD_CONTENTION_STAT = 804, - JMM_STAT_PEAK_POOL_USAGE = 805, - JMM_STAT_GC_STAT = 806 -} jmmStatisticType; - -typedef enum { - JMM_USAGE_THRESHOLD_HIGH = 901, - JMM_USAGE_THRESHOLD_LOW = 902, - JMM_COLLECTION_USAGE_THRESHOLD_HIGH = 903, - JMM_COLLECTION_USAGE_THRESHOLD_LOW = 904 -} jmmThresholdType; - -/* Should match what is allowed in globals.hpp */ -typedef enum { - JMM_VMGLOBAL_TYPE_UNKNOWN = 0, - JMM_VMGLOBAL_TYPE_JBOOLEAN = 1, - JMM_VMGLOBAL_TYPE_JSTRING = 2, - JMM_VMGLOBAL_TYPE_JLONG = 3, - JMM_VMGLOBAL_TYPE_JDOUBLE = 4 -} jmmVMGlobalType; - -typedef enum { - JMM_VMGLOBAL_ORIGIN_DEFAULT = 1, /* Default value */ - JMM_VMGLOBAL_ORIGIN_COMMAND_LINE = 2, /* Set at command line (or JNI invocation) */ - JMM_VMGLOBAL_ORIGIN_MANAGEMENT = 3, /* Set via management interface */ - JMM_VMGLOBAL_ORIGIN_ENVIRON_VAR = 4, /* Set via environment variables */ - JMM_VMGLOBAL_ORIGIN_CONFIG_FILE = 5, /* Set via config file (such as .hotspotrc) */ - JMM_VMGLOBAL_ORIGIN_ERGONOMIC = 6, /* Set via ergonomic */ - JMM_VMGLOBAL_ORIGIN_ATTACH_ON_DEMAND = 7, /* Set via attach */ - JMM_VMGLOBAL_ORIGIN_OTHER = 99 /* Set via some other mechanism */ -} jmmVMGlobalOrigin; - -typedef struct { - jstring name; - jvalue value; - jmmVMGlobalType type; /* Data type */ - jmmVMGlobalOrigin origin; /* Default or non-default value */ - unsigned int writeable : 1; /* dynamically writeable */ - unsigned int external : 1; /* external supported interface */ - unsigned int reserved : 30; - void *reserved1; - void *reserved2; -} jmmVMGlobal; - -typedef struct { - const char* name; - char type; - const char* description; -} jmmExtAttributeInfo; - -/* Caller has to set the following fields before calling GetLastGCStat - * o usage_before_gc - array of MemoryUsage objects - * o usage_after_gc - array of MemoryUsage objects - * o gc_ext_attribute_values_size - size of gc_ext_atttribute_values array - * o gc_ext_attribtue_values - array of jvalues - */ -typedef struct { - jlong gc_index; /* Index of the collections */ - jlong start_time; /* Start time of the GC */ - jlong end_time; /* End time of the GC */ - jobjectArray usage_before_gc; /* Memory usage array before GC */ - jobjectArray usage_after_gc; /* Memory usage array after GC */ - jint gc_ext_attribute_values_size; /* set by the caller of GetGCStat */ - jvalue* gc_ext_attribute_values; /* Array of jvalue for GC extension attributes */ - jint num_gc_ext_attributes; /* number of GC extension attribute values s are filled */ - /* -1 indicates gc_ext_attribute_values is not big enough */ -} jmmGCStat; - -typedef struct { - const char* name; /* Name of the diagnostic command */ - const char* description; /* Short description */ - const char* impact; /* Impact on the JVM */ - const char* permission_class; /* Class name of the required permission if any */ - const char* permission_name; /* Permission name of the required permission if any */ - const char* permission_action; /* Action name of the required permission if any*/ - int num_arguments; /* Number of supported options or arguments */ - jboolean enabled; /* True if the diagnostic command can be invoked, false otherwise */ -} dcmdInfo; - -typedef struct { - const char* name; /* Option/Argument name*/ - const char* description; /* Short description */ - const char* type; /* Type: STRING, BOOLEAN, etc. */ - const char* default_string; /* Default value in a parsable string */ - jboolean mandatory; /* True if the option/argument is mandatory */ - jboolean option; /* True if it is an option, false if it is an argument */ - /* (see diagnosticFramework.hpp for option/argument definitions) */ - jboolean multiple; /* True if the option can be specified several time */ - int position; /* Expected position for this argument (this field is */ - /* meaningless for options) */ -} dcmdArgInfo; - -typedef struct jmmInterface_1_ { - void* reserved1; - void* reserved2; - - jint (JNICALL *GetVersion) (JNIEnv *env); - - jint (JNICALL *GetOptionalSupport) (JNIEnv *env, - jmmOptionalSupport* support_ptr); - - jint (JNICALL *GetThreadInfo) (JNIEnv *env, - jlongArray ids, - jint maxDepth, - jobjectArray infoArray); - - jobjectArray (JNICALL *GetMemoryPools) (JNIEnv* env, jobject mgr); - - jobjectArray (JNICALL *GetMemoryManagers) (JNIEnv* env, jobject pool); - - jobject (JNICALL *GetMemoryPoolUsage) (JNIEnv* env, jobject pool); - jobject (JNICALL *GetPeakMemoryPoolUsage) (JNIEnv* env, jobject pool); - - void (JNICALL *GetThreadAllocatedMemory) - (JNIEnv *env, - jlongArray ids, - jlongArray sizeArray); - - jobject (JNICALL *GetMemoryUsage) (JNIEnv* env, jboolean heap); - - jlong (JNICALL *GetLongAttribute) (JNIEnv *env, jobject obj, jmmLongAttribute att); - jboolean (JNICALL *GetBoolAttribute) (JNIEnv *env, jmmBoolAttribute att); - jboolean (JNICALL *SetBoolAttribute) (JNIEnv *env, jmmBoolAttribute att, jboolean flag); - - jint (JNICALL *GetLongAttributes) (JNIEnv *env, - jobject obj, - jmmLongAttribute* atts, - jint count, - jlong* result); - - jobjectArray (JNICALL *FindCircularBlockedThreads) (JNIEnv *env); - - // Not used in JDK 6 or JDK 7 - jlong (JNICALL *GetThreadCpuTime) (JNIEnv *env, jlong thread_id); - - jobjectArray (JNICALL *GetVMGlobalNames) (JNIEnv *env); - jint (JNICALL *GetVMGlobals) (JNIEnv *env, - jobjectArray names, - jmmVMGlobal *globals, - jint count); - - jint (JNICALL *GetInternalThreadTimes) (JNIEnv *env, - jobjectArray names, - jlongArray times); - - jboolean (JNICALL *ResetStatistic) (JNIEnv *env, - jvalue obj, - jmmStatisticType type); - - void (JNICALL *SetPoolSensor) (JNIEnv *env, - jobject pool, - jmmThresholdType type, - jobject sensor); - - jlong (JNICALL *SetPoolThreshold) (JNIEnv *env, - jobject pool, - jmmThresholdType type, - jlong threshold); - jobject (JNICALL *GetPoolCollectionUsage) (JNIEnv* env, jobject pool); - - jint (JNICALL *GetGCExtAttributeInfo) (JNIEnv *env, - jobject mgr, - jmmExtAttributeInfo *ext_info, - jint count); - void (JNICALL *GetLastGCStat) (JNIEnv *env, - jobject mgr, - jmmGCStat *gc_stat); - - jlong (JNICALL *GetThreadCpuTimeWithKind) - (JNIEnv *env, - jlong thread_id, - jboolean user_sys_cpu_time); - void (JNICALL *GetThreadCpuTimesWithKind) - (JNIEnv *env, - jlongArray ids, - jlongArray timeArray, - jboolean user_sys_cpu_time); - - jint (JNICALL *DumpHeap0) (JNIEnv *env, - jstring outputfile, - jboolean live); - jobjectArray (JNICALL *FindDeadlocks) (JNIEnv *env, - jboolean object_monitors_only); - void (JNICALL *SetVMGlobal) (JNIEnv *env, - jstring flag_name, - jvalue new_value); - void* reserved6; - jobjectArray (JNICALL *DumpThreads) (JNIEnv *env, - jlongArray ids, - jboolean lockedMonitors, - jboolean lockedSynchronizers, - jint maxDepth); - void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env, - jobject mgr, - jboolean enabled); - jobjectArray (JNICALL *GetDiagnosticCommands) (JNIEnv *env); - void (JNICALL *GetDiagnosticCommandInfo) - (JNIEnv *env, - jobjectArray cmds, - dcmdInfo *infoArray); - void (JNICALL *GetDiagnosticCommandArgumentsInfo) - (JNIEnv *env, - jstring commandName, - dcmdArgInfo *infoArray); - jstring (JNICALL *ExecuteDiagnosticCommand) - (JNIEnv *env, - jstring command); - void (JNICALL *SetDiagnosticFrameworkNotificationEnabled) - (JNIEnv *env, - jboolean enabled); -} JmmInterface; - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* !_JAVA_JMM_H_ */ diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index bc50816d83d..2581c3da9ab 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "jmm.h" #include "classfile/systemDictionary.hpp" #include "compiler/compileBroker.hpp" #include "memory/iterator.hpp" @@ -46,7 +47,6 @@ #include "services/diagnosticFramework.hpp" #include "services/writeableFlags.hpp" #include "services/heapDumper.hpp" -#include "services/jmm.h" #include "services/lowMemoryDetector.hpp" #include "services/gcNotifier.hpp" #include "services/nmtDCmd.hpp" diff --git a/src/hotspot/share/services/management.hpp b/src/hotspot/share/services/management.hpp index f1168166281..4f475060d67 100644 --- a/src/hotspot/share/services/management.hpp +++ b/src/hotspot/share/services/management.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_VM_SERVICES_MANAGEMENT_HPP #define SHARE_VM_SERVICES_MANAGEMENT_HPP +#include "jmm.h" #include "memory/allocation.hpp" #include "runtime/handles.hpp" #include "runtime/timer.hpp" -#include "services/jmm.h" class OopClosure; class ThreadSnapshot; From 39de42151a518dfd87247a978b3fd9f2c8f2263e Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz <glaubitz@openjdk.org> Date: Fri, 24 Nov 2017 12:16:25 +0100 Subject: [PATCH 033/165] 8186461: Zero's atomic_copy64() should use SPE instructions on linux-powerpcspe Reviewed-by: aph --- src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp index 8e6dc325c4c..a36e4792efd 100644 --- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp +++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp @@ -36,12 +36,18 @@ // Atomically copy 64 bits of data static void atomic_copy64(const volatile void *src, volatile void *dst) { -#if defined(PPC32) +#if defined(PPC32) && !defined(__SPE__) double tmp; asm volatile ("lfd %0, %2\n" "stfd %0, %1\n" : "=&f"(tmp), "=Q"(*(volatile double*)dst) : "Q"(*(volatile double*)src)); +#elif defined(PPC32) && defined(__SPE__) + long tmp; + asm volatile ("evldd %0, %2\n" + "evstdd %0, %1\n" + : "=&r"(tmp), "=Q"(*(volatile long*)dst) + : "Q"(*(volatile long*)src)); #elif defined(S390) && !defined(_LP64) double tmp; asm volatile ("ld %0, 0(%1)\n" From 295112c91af0986da45f4966230118a1c2a39d7e Mon Sep 17 00:00:00 2001 From: Andrew Haley <aph@openjdk.org> Date: Fri, 24 Nov 2017 17:19:47 +0000 Subject: [PATCH 034/165] 8189596: AArch64: implementation for Thread-local handshakes Reviewed-by: adinn --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 42 +------------ .../cpu/aarch64/globalDefinitions_aarch64.hpp | 2 + src/hotspot/cpu/aarch64/globals_aarch64.hpp | 2 +- .../cpu/aarch64/interp_masm_aarch64.cpp | 33 +++++++++-- .../cpu/aarch64/interp_masm_aarch64.hpp | 7 ++- .../cpu/aarch64/macroAssembler_aarch64.cpp | 59 ++++++++++++++++--- .../cpu/aarch64/macroAssembler_aarch64.hpp | 4 ++ .../cpu/aarch64/nativeInst_aarch64.cpp | 5 ++ .../cpu/aarch64/sharedRuntime_aarch64.cpp | 46 +++++++++++---- .../templateInterpreterGenerator_aarch64.cpp | 29 ++------- .../cpu/aarch64/templateTable_aarch64.cpp | 16 ++--- 11 files changed, 147 insertions(+), 98 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 36fefc866aa..ecd4a8e19f2 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -494,42 +494,6 @@ void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) { } } -// Rather than take a segfault when the polling page is protected, -// explicitly check for a safepoint in progress and if there is one, -// fake a call to the handler as if a segfault had been caught. -void LIR_Assembler::poll_for_safepoint(relocInfo::relocType rtype, CodeEmitInfo* info) { - __ mov(rscratch1, SafepointSynchronize::address_of_state()); - __ ldrb(rscratch1, Address(rscratch1)); - Label nope, poll; - __ cbz(rscratch1, nope); - __ block_comment("safepoint"); - __ enter(); - __ push(0x3, sp); // r0 & r1 - __ push(0x3ffffffc, sp); // integer registers except lr & sp & r0 & r1 - __ adr(r0, poll); - __ str(r0, Address(rthread, JavaThread::saved_exception_pc_offset())); - __ mov(rscratch1, CAST_FROM_FN_PTR(address, SharedRuntime::get_poll_stub)); - __ blrt(rscratch1, 1, 0, 1); - __ maybe_isb(); - __ pop(0x3ffffffc, sp); // integer registers except lr & sp & r0 & r1 - __ mov(rscratch1, r0); - __ pop(0x3, sp); // r0 & r1 - __ leave(); - __ br(rscratch1); - address polling_page(os::get_polling_page()); - assert(os::is_poll_address(polling_page), "should be"); - unsigned long off; - __ adrp(rscratch1, Address(polling_page, rtype), off); - __ bind(poll); - if (info) - add_debug_info_for_branch(info); // This isn't just debug info: - // it's the oop map - else - __ code_section()->relocate(pc(), rtype); - __ ldrw(zr, Address(rscratch1, off)); - __ bind(nope); -} - void LIR_Assembler::return_op(LIR_Opr result) { assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,"); @@ -549,11 +513,9 @@ int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) { address polling_page(os::get_polling_page()); guarantee(info != NULL, "Shouldn't be NULL"); assert(os::is_poll_address(polling_page), "should be"); - unsigned long off; - __ adrp(rscratch1, Address(polling_page, relocInfo::poll_type), off); - assert(off == 0, "must be"); + __ get_polling_page(rscratch1, polling_page, relocInfo::poll_type); add_debug_info_for_branch(info); // This isn't just debug info: - // it's the oop map + // it's the oop map __ read_polling_page(rscratch1, relocInfo::poll_type); return __ offset(); } diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index ad6b12de22d..5977764c2d2 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -51,4 +51,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORT_RESERVED_STACK_AREA +#define THREAD_LOCAL_POLL + #endif // CPU_AARCH64_VM_GLOBALDEFINITIONS_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index bf34c157c9e..dfd984e4fb0 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -79,7 +79,7 @@ define_pd_global(bool, CompactStrings, true); // Clear short arrays bigger than one word in an arch-specific way define_pd_global(intx, InitArrayShortSize, BytesPerLong); -define_pd_global(bool, ThreadLocalHandshakes, false); +define_pd_global(bool, ThreadLocalHandshakes, true); #if defined(COMPILER1) || defined(COMPILER2) define_pd_global(intx, InlineSmallCode, 1000); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index b1b3c5e5273..39c41c3e21d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -30,12 +30,13 @@ #include "logging/log.hpp" #include "oops/arrayOop.hpp" #include "oops/markOop.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" +#include "oops/methodData.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" #include "runtime/basicLock.hpp" #include "runtime/biasedLocking.hpp" +#include "runtime/safepointMechanism.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/thread.inline.hpp" @@ -438,13 +439,26 @@ void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) { void InterpreterMacroAssembler::dispatch_base(TosState state, address* table, - bool verifyoop) { + bool verifyoop, + bool generate_poll) { if (VerifyActivationFrameSize) { Unimplemented(); } if (verifyoop) { verify_oop(r0, state); } + + Label safepoint; + address* const safepoint_table = Interpreter::safept_table(state); + bool needs_thread_local_poll = generate_poll && + SafepointMechanism::uses_thread_local_poll() && table != safepoint_table; + + if (needs_thread_local_poll) { + NOT_PRODUCT(block_comment("Thread-local Safepoint poll")); + ldr(rscratch2, Address(rthread, Thread::polling_page_offset())); + tbnz(rscratch2, exact_log2(SafepointMechanism::poll_bit()), safepoint); + } + if (table == Interpreter::dispatch_table(state)) { addw(rscratch2, rscratch1, Interpreter::distance_from_dispatch_table(state)); ldr(rscratch2, Address(rdispatch, rscratch2, Address::uxtw(3))); @@ -453,10 +467,17 @@ void InterpreterMacroAssembler::dispatch_base(TosState state, ldr(rscratch2, Address(rscratch2, rscratch1, Address::uxtw(3))); } br(rscratch2); + + if (needs_thread_local_poll) { + bind(safepoint); + lea(rscratch2, ExternalAddress((address)safepoint_table)); + ldr(rscratch2, Address(rscratch2, rscratch1, Address::uxtw(3))); + br(rscratch2); + } } -void InterpreterMacroAssembler::dispatch_only(TosState state) { - dispatch_base(state, Interpreter::dispatch_table(state)); +void InterpreterMacroAssembler::dispatch_only(TosState state, bool generate_poll) { + dispatch_base(state, Interpreter::dispatch_table(state), true, generate_poll); } void InterpreterMacroAssembler::dispatch_only_normal(TosState state) { @@ -468,10 +489,10 @@ void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) { } -void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { +void InterpreterMacroAssembler::dispatch_next(TosState state, int step, bool generate_poll) { // load next bytecode ldrb(rscratch1, Address(pre(rbcp, step))); - dispatch_base(state, Interpreter::dispatch_table(state)); + dispatch_base(state, Interpreter::dispatch_table(state), generate_poll); } void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 637ae481f5b..59b33a17d22 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -55,7 +55,8 @@ class InterpreterMacroAssembler: public MacroAssembler { bool check_exceptions); // base routine for all dispatches - void dispatch_base(TosState state, address* table, bool verifyoop = true); + void dispatch_base(TosState state, address* table, + bool verifyoop = true, bool generate_poll = false); public: InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} @@ -165,12 +166,12 @@ class InterpreterMacroAssembler: public MacroAssembler { void dispatch_prolog(TosState state, int step = 0); void dispatch_epilog(TosState state, int step = 0); // dispatch via rscratch1 - void dispatch_only(TosState state); + void dispatch_only(TosState state, bool generate_poll = false); // dispatch normal table via rscratch1 (assume rscratch1 is loaded already) void dispatch_only_normal(TosState state); void dispatch_only_noverify(TosState state); // load rscratch1 from [rbcp + step] and dispatch via rscratch1 - void dispatch_next(TosState state, int step = 0); + void dispatch_next(TosState state, int step = 0, bool generate_poll = false); // load rscratch1 from [esi] and dispatch via rscratch1 and table void dispatch_via (TosState state, address* table); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 81c00ffd106..bef4fd4e573 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -287,6 +287,40 @@ void MacroAssembler::serialize_memory(Register thread, Register tmp) { dsb(Assembler::SY); } +void MacroAssembler::safepoint_poll(Label& slow_path) { + if (SafepointMechanism::uses_thread_local_poll()) { + ldr(rscratch1, Address(rthread, Thread::polling_page_offset())); + tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path); + } else { + unsigned long offset; + adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset); + ldrw(rscratch1, Address(rscratch1, offset)); + assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code"); + cbnz(rscratch1, slow_path); + } +} + +// Just like safepoint_poll, but use an acquiring load for thread- +// local polling. +// +// We need an acquire here to ensure that any subsequent load of the +// global SafepointSynchronize::_state flag is ordered after this load +// of the local Thread::_polling page. We don't want this poll to +// return false (i.e. not safepointing) and a later poll of the global +// SafepointSynchronize::_state spuriously to return true. +// +// This is to avoid a race when we're in a native->Java transition +// racing the code which wakes up from a safepoint. +// +void MacroAssembler::safepoint_poll_acquire(Label& slow_path) { + if (SafepointMechanism::uses_thread_local_poll()) { + lea(rscratch1, Address(rthread, Thread::polling_page_offset())); + ldar(rscratch1, rscratch1); + tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path); + } else { + safepoint_poll(slow_path); + } +} void MacroAssembler::reset_last_Java_frame(bool clear_fp) { // we must set sp to zero to clear frame @@ -4336,15 +4370,26 @@ void MacroAssembler::bang_stack_size(Register size, Register tmp) { } -address MacroAssembler::read_polling_page(Register r, address page, relocInfo::relocType rtype) { - unsigned long off; - adrp(r, Address(page, rtype), off); - InstructionMark im(this); - code_section()->relocate(inst_mark(), rtype); - ldrw(zr, Address(r, off)); - return inst_mark(); +// Move the address of the polling page into dest. +void MacroAssembler::get_polling_page(Register dest, address page, relocInfo::relocType rtype) { + if (SafepointMechanism::uses_thread_local_poll()) { + ldr(dest, Address(rthread, Thread::polling_page_offset())); + } else { + unsigned long off; + adrp(dest, Address(page, rtype), off); + assert(off == 0, "polling page must be page aligned"); + } } +// Move the address of the polling page into r, then read the polling +// page. +address MacroAssembler::read_polling_page(Register r, address page, relocInfo::relocType rtype) { + get_polling_page(r, page, rtype); + return read_polling_page(r, rtype); +} + +// Read the polling page. The address of the polling page must +// already be in r. address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype) { InstructionMark im(this); code_section()->relocate(inst_mark(), rtype); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index f5cab401535..3db1711eea3 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -97,6 +97,9 @@ class MacroAssembler: public Assembler { virtual void check_and_handle_popframe(Register java_thread); virtual void check_and_handle_earlyret(Register java_thread); + void safepoint_poll(Label& slow_path); + void safepoint_poll_acquire(Label& slow_path); + // Biased locking support // lock_reg and obj_reg must be loaded up with the appropriate values. // swap_reg is killed. @@ -1199,6 +1202,7 @@ public: address read_polling_page(Register r, address page, relocInfo::relocType rtype); address read_polling_page(Register r, relocInfo::relocType rtype); + void get_polling_page(Register dest, address page, relocInfo::relocType rtype); // CRC32 code for java.util.zip.CRC32::updateBytes() instrinsic. void update_byte_crc32(Register crc, Register val, Register table); diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index 88e1d3d1460..6a313273ea6 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -245,6 +245,11 @@ bool NativeInstruction::is_safepoint_poll() { // mov(reg, polling_page); // ldr(zr, [reg, #offset]); // + // or + // + // ldr(reg, [rthread, #offset]); + // ldr(zr, [reg, #offset]); + // // however, we cannot rely on the polling page address load always // directly preceding the read from the page. C1 does that but C2 // has to do the load and read as two independent instruction diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 9b67fe7a5f2..8cc86c221fc 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -1952,7 +1952,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ strw(rscratch1, Address(rthread, JavaThread::thread_state_offset())); // Force this write out before the read below - __ dmb(Assembler::SY); + __ dmb(Assembler::ISH); } else { __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); @@ -1970,13 +1970,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // check for safepoint operation in progress and/or pending suspend requests Label safepoint_in_progress, safepoint_in_progress_done; { - assert(SafepointSynchronize::_not_synchronized == 0, "fix this code"); - unsigned long offset; - __ adrp(rscratch1, - ExternalAddress((address)SafepointSynchronize::address_of_state()), - offset); - __ ldrw(rscratch1, Address(rscratch1, offset)); - __ cbnzw(rscratch1, safepoint_in_progress); + __ safepoint_poll_acquire(safepoint_in_progress); __ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset())); __ cbnzw(rscratch1, safepoint_in_progress); __ bind(safepoint_in_progress_done); @@ -2932,8 +2926,11 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t if (!cause_return) { // overwrite the return address pushed by save_live_registers - __ ldr(c_rarg0, Address(rthread, JavaThread::saved_exception_pc_offset())); - __ str(c_rarg0, Address(rfp, wordSize)); + // Additionally, r20 is a callee-saved register so we can look at + // it later to determine if someone changed the return address for + // us! + __ ldr(r20, Address(rthread, JavaThread::saved_exception_pc_offset())); + __ str(r20, Address(rfp, wordSize)); } // Do the call @@ -2968,11 +2965,40 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // No exception case __ bind(noException); + Label no_adjust, bail; + if (SafepointMechanism::uses_thread_local_poll() && !cause_return) { + // If our stashed return pc was modified by the runtime we avoid touching it + __ ldr(rscratch1, Address(rfp, wordSize)); + __ cmp(r20, rscratch1); + __ br(Assembler::NE, no_adjust); + +#ifdef ASSERT + // Verify the correct encoding of the poll we're about to skip. + // See NativeInstruction::is_ldrw_to_zr() + __ ldrw(rscratch1, Address(r20)); + __ ubfx(rscratch2, rscratch1, 22, 10); + __ cmpw(rscratch2, 0b1011100101); + __ br(Assembler::NE, bail); + __ ubfx(rscratch2, rscratch1, 0, 5); + __ cmpw(rscratch2, 0b11111); + __ br(Assembler::NE, bail); +#endif + // Adjust return pc forward to step over the safepoint poll instruction + __ add(r20, r20, NativeInstruction::instruction_size); + __ str(r20, Address(rfp, wordSize)); + } + + __ bind(no_adjust); // Normal exit, restore registers and exit. RegisterSaver::restore_live_registers(masm, save_vectors); __ ret(lr); +#ifdef ASSERT + __ bind(bail); + __ stop("Attempting to adjust pc to skip safepoint poll but the return point is not what we expected"); +#endif + // Make sure all code is generated masm->flush(); diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 9174c554bcc..f9e12bd73a3 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -967,12 +967,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { Label slow_path; // If we need a safepoint check, generate full interpreter entry. - ExternalAddress state(SafepointSynchronize::address_of_state()); - unsigned long offset; - __ adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset); - __ ldrw(rscratch1, Address(rscratch1, offset)); - assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code"); - __ cbnz(rscratch1, slow_path); + __ safepoint_poll(slow_path); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -986,6 +981,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { __ ldrw(val, Address(esp, 0)); // byte value __ ldrw(crc, Address(esp, wordSize)); // Initial CRC + unsigned long offset; __ adrp(tbl, ExternalAddress(StubRoutines::crc_table_addr()), offset); __ add(tbl, tbl, offset); @@ -1020,12 +1016,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI Label slow_path; // If we need a safepoint check, generate full interpreter entry. - ExternalAddress state(SafepointSynchronize::address_of_state()); - unsigned long offset; - __ adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset); - __ ldrw(rscratch1, Address(rscratch1, offset)); - assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code"); - __ cbnz(rscratch1, slow_path); + __ safepoint_poll(slow_path); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -1375,7 +1366,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { if (os::is_MP()) { if (UseMembar) { // Force this write out before the read below - __ dsb(Assembler::SY); + __ dmb(Assembler::ISH); } else { // Write serialization page so VM thread can do a pseudo remote membar. // We use the current thread pointer to calculate a thread specific @@ -1387,16 +1378,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // check for safepoint operation in progress and/or pending suspend requests { - Label Continue; - { - unsigned long offset; - __ adrp(rscratch2, SafepointSynchronize::address_of_state(), offset); - __ ldrw(rscratch2, Address(rscratch2, offset)); - } - assert(SafepointSynchronize::_not_synchronized == 0, - "SafepointSynchronize::_not_synchronized"); - Label L; - __ cbnz(rscratch2, L); + Label L, Continue; + __ safepoint_poll_acquire(L); __ ldrw(rscratch2, Address(rthread, JavaThread::suspend_flags_offset())); __ cbz(rscratch2, Continue); __ bind(L); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 2ba42035e7b..9390d38b1b8 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -1717,7 +1717,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) __ push_i(r1); // Adjust the bcp by the 16-bit displacement in r2 __ add(rbcp, rbcp, r2); - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); return; } @@ -1833,7 +1833,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) // continue with the bytecode @ target // rscratch1: target bytecode // rbcp: target bcp - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); if (UseLoopCounter) { if (ProfileInterpreter) { @@ -1973,7 +1973,7 @@ void TemplateTable::ret() { __ ldr(rbcp, Address(rmethod, Method::const_offset())); __ lea(rbcp, Address(rbcp, r1)); __ add(rbcp, rbcp, in_bytes(ConstMethod::codes_offset())); - __ dispatch_next(vtos); + __ dispatch_next(vtos, 0, /*generate_poll*/true); } void TemplateTable::wide_ret() { @@ -1984,7 +1984,7 @@ void TemplateTable::wide_ret() { __ ldr(rbcp, Address(rmethod, Method::const_offset())); __ lea(rbcp, Address(rbcp, r1)); __ add(rbcp, rbcp, in_bytes(ConstMethod::codes_offset())); - __ dispatch_next(vtos); + __ dispatch_next(vtos, 0, /*generate_poll*/true); } @@ -2014,7 +2014,7 @@ void TemplateTable::tableswitch() { __ rev32(r3, r3); __ load_unsigned_byte(rscratch1, Address(rbcp, r3, Address::sxtw(0))); __ add(rbcp, rbcp, r3, ext::sxtw); - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); // handle default __ bind(default_case); __ profile_switch_default(r0); @@ -2064,7 +2064,7 @@ void TemplateTable::fast_linearswitch() { __ rev32(r3, r3); __ add(rbcp, rbcp, r3, ext::sxtw); __ ldrb(rscratch1, Address(rbcp, 0)); - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); } void TemplateTable::fast_binaryswitch() { @@ -2162,7 +2162,7 @@ void TemplateTable::fast_binaryswitch() { __ rev32(j, j); __ load_unsigned_byte(rscratch1, Address(rbcp, j, Address::sxtw(0))); __ lea(rbcp, Address(rbcp, j, Address::sxtw(0))); - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); // default case -> j = default offset __ bind(default_case); @@ -2171,7 +2171,7 @@ void TemplateTable::fast_binaryswitch() { __ rev32(j, j); __ load_unsigned_byte(rscratch1, Address(rbcp, j, Address::sxtw(0))); __ lea(rbcp, Address(rbcp, j, Address::sxtw(0))); - __ dispatch_only(vtos); + __ dispatch_only(vtos, /*generate_poll*/true); } From d08cb7efdf62b6c34cbf0fa6c20977b23c226cec Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach <jtulach@openjdk.org> Date: Fri, 10 Nov 2017 10:30:42 +0100 Subject: [PATCH 035/165] 8189116: Give the jdk.internal.vm.compiler.management only the permissions it really needs to expose the bean Reviewed-by: mchung, kvn --- src/java.base/share/lib/security/default.policy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy index c5d6fd9bf9b..a4a9cbcbccc 100644 --- a/src/java.base/share/lib/security/default.policy +++ b/src/java.base/share/lib/security/default.policy @@ -155,7 +155,10 @@ grant codeBase "jrt:/jdk.internal.vm.compiler" { }; grant codeBase "jrt:/jdk.internal.vm.compiler.management" { - permission java.security.AllPermission; + permission java.lang.RuntimePermission "accessClassInPackage.org.graalvm.compiler.hotspot"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.vm.ci.runtime"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.management.spi"; + permission java.lang.RuntimePermission "sun.management.spi.PlatformMBeanProvider.subclass"; }; grant codeBase "jrt:/jdk.jsobject" { From 1c25161036f3c27ee2013f7f9e4d793111f19ac0 Mon Sep 17 00:00:00 2001 From: Roland Westrelin <roland@openjdk.org> Date: Mon, 27 Nov 2017 10:44:19 -0800 Subject: [PATCH 036/165] 8191153: assert(u_ctrl != blk1 && u_ctrl != blk2) failed: won't converge Relax assert Reviewed-by: kvn --- src/hotspot/share/opto/split_if.cpp | 2 +- .../loopopts/TestSplitIfPinnedCMove.java | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 0634b5efb5e..c3a9965bdf3 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -169,7 +169,7 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { assert(u->in(1) == bol, ""); // Get control block of either the CMove or the If input Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u); - assert(u_ctrl != blk1 && u_ctrl != blk2, "won't converge"); + assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge"); Node *x = bol->clone(); register_new_node(x, u_ctrl); _igvn.replace_input_of(u, 1, x); diff --git a/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java b/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java new file mode 100644 index 00000000000..bce4fdf28c0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. 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 8191153 + * @summary too strong assert from 8186125 + * + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestSplitIfPinnedCMove::not_inlined -XX:CompileOnly=TestSplitIfPinnedCMove::test TestSplitIfPinnedCMove + * + */ + +public class TestSplitIfPinnedCMove { + static void not_inlined() {} + + static class A { + A(int f) { + this.f = f; + } + int f; + } + + static int test(int i1, int i3, A a1, A a2) { + // loops to trigger more loop optimization passes + int res = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + res++; + } + } + } + // null check a1 and a2 + res += a1.f + a2.f; + + boolean f2 = false; + if (i1 > 0) { + not_inlined(); + f2 = true; + } + + // Should become CMoveP and be pinned here + res += (i3 > 0 ? a1 : a2).f; + + // f2 should be split through phi with above if + if (f2) { + not_inlined(); + res += 42; + } + + // Another use for i3 > 0 + if (i3 > 0) { + res++; + } + return res; + } + + public static void main(String[] args) { + A a = new A(42); + for (int i = 0; i < 20_000; i++) { + test((i % 2) == 0 ? 0 : 1, (i % 2) == 0 ? 0 : 1, a, a); + } + } +} From d0120db1fd35cec85618d49eb498f5fd5cc15774 Mon Sep 17 00:00:00 2001 From: Jini George <jgeorge@openjdk.org> Date: Mon, 27 Nov 2017 15:26:55 -0500 Subject: [PATCH 037/165] 8191919: Include TestJhsdbJstackLock.java in ProblemList.txt Reviewed-by: dcubed --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 76192ac93bd..f79739c5ffc 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -90,6 +90,7 @@ serviceability/sa/TestInstanceKlassSizeForInterface.java 8184042 macosx-all serviceability/sa/TestPrintMdo.java 8184042 macosx-all serviceability/sa/TestRevPtrsForInvokeDynamic.java 8191270 generic-all serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java 8184042 macosx-all +serviceability/sa/TestJhsdbJstackLock.java 8191914 windows-all ############################################################################# # :hotspot_misc From 8a150bbd05d408d0b31d11f72a25414c2168d198 Mon Sep 17 00:00:00 2001 From: Sangheon Kim <sangheki@openjdk.org> Date: Mon, 27 Nov 2017 13:19:08 -0800 Subject: [PATCH 038/165] 8178497: Bug in MutableNUMASpace::ensure_parsability Changed problematic variable type from intptr_t to HeapWord* Reviewed-by: tschatzl, sjohanss --- src/hotspot/share/gc/parallel/mutableNUMASpace.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 7ccd428936a..7d5fd87d5c1 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -91,14 +91,14 @@ void MutableNUMASpace::ensure_parsability() { MutableSpace *s = ls->space(); if (s->top() < top()) { // For all spaces preceding the one containing top() if (s->free_in_words() > 0) { - intptr_t cur_top = (intptr_t)s->top(); + HeapWord* cur_top = s->top(); size_t words_left_to_fill = pointer_delta(s->end(), s->top());; while (words_left_to_fill > 0) { size_t words_to_fill = MIN2(words_left_to_fill, CollectedHeap::filler_array_max_size()); assert(words_to_fill >= CollectedHeap::min_fill_size(), "Remaining size (" SIZE_FORMAT ") is too small to fill (based on " SIZE_FORMAT " and " SIZE_FORMAT ")", words_to_fill, words_left_to_fill, CollectedHeap::filler_array_max_size()); - CollectedHeap::fill_with_object((HeapWord*)cur_top, words_to_fill); + CollectedHeap::fill_with_object(cur_top, words_to_fill); if (!os::numa_has_static_binding()) { size_t touched_words = words_to_fill; #ifndef ASSERT @@ -108,19 +108,19 @@ void MutableNUMASpace::ensure_parsability() { } #endif MemRegion invalid; - HeapWord *crossing_start = align_up((HeapWord*)cur_top, os::vm_page_size()); - HeapWord *crossing_end = align_down((HeapWord*)(cur_top + touched_words), os::vm_page_size()); + HeapWord *crossing_start = align_up(cur_top, os::vm_page_size()); + HeapWord *crossing_end = align_down(cur_top + touched_words, os::vm_page_size()); if (crossing_start != crossing_end) { // If object header crossed a small page boundary we mark the area // as invalid rounding it to a page_size(). - HeapWord *start = MAX2(align_down((HeapWord*)cur_top, page_size()), s->bottom()); - HeapWord *end = MIN2(align_up((HeapWord*)(cur_top + touched_words), page_size()), s->end()); + HeapWord *start = MAX2(align_down(cur_top, page_size()), s->bottom()); + HeapWord *end = MIN2(align_up(cur_top + touched_words, page_size()), s->end()); invalid = MemRegion(start, end); } ls->add_invalid_region(invalid); } - cur_top = cur_top + (words_to_fill * HeapWordSize); + cur_top += words_to_fill; words_left_to_fill -= words_to_fill; } } From eb10d407d34232517110f4edf01bea98aef2923d Mon Sep 17 00:00:00 2001 From: Roland Westrelin <roland@openjdk.org> Date: Mon, 27 Nov 2017 16:05:11 -0800 Subject: [PATCH 039/165] 8191887: assert(b->is_Bool()) in PhaseIdealLoop::clone_iff() due to Opaque4 node Add special handling for graph shape If->Opaque4->Bool->CmpP Reviewed-by: kvn --- src/hotspot/share/opto/loopnode.hpp | 2 +- src/hotspot/share/opto/loopopts.cpp | 108 +++++++++++------- .../compiler/unsafe/TestLoopUnswitching.java | 102 +++++++++++++++++ 3 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 70168186cca..f5027592c18 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1098,7 +1098,7 @@ public: // "Nearly" because all Nodes have been cloned from the original in the loop, // but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs // through the Phi recursively, and return a Bool. - BoolNode *clone_iff( PhiNode *phi, IdealLoopTree *loop ); + Node *clone_iff( PhiNode *phi, IdealLoopTree *loop ); CmpNode *clone_bool( PhiNode *phi, IdealLoopTree *loop ); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 45acc2fcae2..552eaf91bfe 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1407,47 +1407,56 @@ void PhaseIdealLoop::split_if_with_blocks( VectorSet &visited, Node_Stack &nstac // "Nearly" because all Nodes have been cloned from the original in the loop, // but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs // through the Phi recursively, and return a Bool. -BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) { +Node* PhaseIdealLoop::clone_iff(PhiNode *phi, IdealLoopTree *loop) { // Convert this Phi into a Phi merging Bools uint i; - for( i = 1; i < phi->req(); i++ ) { + for (i = 1; i < phi->req(); i++) { Node *b = phi->in(i); - if( b->is_Phi() ) { - _igvn.replace_input_of(phi, i, clone_iff( b->as_Phi(), loop )); + if (b->is_Phi()) { + _igvn.replace_input_of(phi, i, clone_iff(b->as_Phi(), loop)); } else { - assert( b->is_Bool(), "" ); + assert(b->is_Bool() || b->Opcode() == Op_Opaque4, ""); } } - Node *sample_bool = phi->in(1); - Node *sample_cmp = sample_bool->in(1); + Node* n = phi->in(1); + Node* sample_opaque = NULL; + Node *sample_bool = NULL; + if (n->Opcode() == Op_Opaque4) { + sample_opaque = n; + sample_bool = n->in(1); + assert(sample_bool->is_Bool(), "wrong type"); + } else { + sample_bool = n; + } + Node *sample_cmp = sample_bool->in(1); // Make Phis to merge the Cmp's inputs. - PhiNode *phi1 = new PhiNode( phi->in(0), Type::TOP ); - PhiNode *phi2 = new PhiNode( phi->in(0), Type::TOP ); - for( i = 1; i < phi->req(); i++ ) { - Node *n1 = phi->in(i)->in(1)->in(1); - Node *n2 = phi->in(i)->in(1)->in(2); - phi1->set_req( i, n1 ); - phi2->set_req( i, n2 ); - phi1->set_type( phi1->type()->meet_speculative(n1->bottom_type())); - phi2->set_type( phi2->type()->meet_speculative(n2->bottom_type())); + PhiNode *phi1 = new PhiNode(phi->in(0), Type::TOP); + PhiNode *phi2 = new PhiNode(phi->in(0), Type::TOP); + for (i = 1; i < phi->req(); i++) { + Node *n1 = sample_opaque == NULL ? phi->in(i)->in(1)->in(1) : phi->in(i)->in(1)->in(1)->in(1); + Node *n2 = sample_opaque == NULL ? phi->in(i)->in(1)->in(2) : phi->in(i)->in(1)->in(1)->in(2); + phi1->set_req(i, n1); + phi2->set_req(i, n2); + phi1->set_type(phi1->type()->meet_speculative(n1->bottom_type())); + phi2->set_type(phi2->type()->meet_speculative(n2->bottom_type())); } // See if these Phis have been made before. // Register with optimizer Node *hit1 = _igvn.hash_find_insert(phi1); - if( hit1 ) { // Hit, toss just made Phi + if (hit1) { // Hit, toss just made Phi _igvn.remove_dead_node(phi1); // Remove new phi - assert( hit1->is_Phi(), "" ); + assert(hit1->is_Phi(), "" ); phi1 = (PhiNode*)hit1; // Use existing phi } else { // Miss _igvn.register_new_node_with_optimizer(phi1); } Node *hit2 = _igvn.hash_find_insert(phi2); - if( hit2 ) { // Hit, toss just made Phi + if (hit2) { // Hit, toss just made Phi _igvn.remove_dead_node(phi2); // Remove new phi - assert( hit2->is_Phi(), "" ); + assert(hit2->is_Phi(), "" ); phi2 = (PhiNode*)hit2; // Use existing phi } else { // Miss _igvn.register_new_node_with_optimizer(phi2); @@ -1457,8 +1466,8 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) { set_ctrl(phi2, phi->in(0)); // Make a new Cmp Node *cmp = sample_cmp->clone(); - cmp->set_req( 1, phi1 ); - cmp->set_req( 2, phi2 ); + cmp->set_req(1, phi1); + cmp->set_req(2, phi2); _igvn.register_new_node_with_optimizer(cmp); set_ctrl(cmp, phi->in(0)); @@ -1468,8 +1477,16 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) { _igvn.register_new_node_with_optimizer(b); set_ctrl(b, phi->in(0)); - assert( b->is_Bool(), "" ); - return (BoolNode*)b; + if (sample_opaque != NULL) { + Node* opaque = sample_opaque->clone(); + opaque->set_req(1, b); + _igvn.register_new_node_with_optimizer(opaque); + set_ctrl(opaque, phi->in(0)); + return opaque; + } + + assert(b->is_Bool(), ""); + return b; } //------------------------------clone_bool------------------------------------- @@ -1749,21 +1766,24 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd // in the loop to break the loop, then test is again outside of the // loop to determine which way the loop exited. // Loop predicate If node connects to Bool node through Opaque1 node. - if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use)) { + if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) { // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. - if( !split_if_set ) + if (!split_if_set) { split_if_set = new Node_List(area); + } split_if_set->push(use); } - if( use->is_Bool() ) { - if( !split_bool_set ) + if (use->is_Bool()) { + if (!split_bool_set) { split_bool_set = new Node_List(area); + } split_bool_set->push(use); } - if( use->Opcode() == Op_CreateEx ) { - if( !split_cex_set ) + if (use->Opcode() == Op_CreateEx) { + if (!split_cex_set) { split_cex_set = new Node_List(area); + } split_cex_set->push(use); } @@ -1852,31 +1872,31 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd // takes control from one or more OLD Regions (which in turn get from NEW // Regions). In any case, there will be a set of Phis for each merge point // from the IF up to where the original BOOL def exists the loop. - if( split_if_set ) { - while( split_if_set->size() ) { + if (split_if_set) { + while (split_if_set->size()) { Node *iff = split_if_set->pop(); - if( iff->in(1)->is_Phi() ) { - BoolNode *b = clone_iff( iff->in(1)->as_Phi(), loop ); + if (iff->in(1)->is_Phi()) { + Node *b = clone_iff(iff->in(1)->as_Phi(), loop); _igvn.replace_input_of(iff, 1, b); } } } - if( split_bool_set ) { - while( split_bool_set->size() ) { + if (split_bool_set) { + while (split_bool_set->size()) { Node *b = split_bool_set->pop(); Node *phi = b->in(1); - assert( phi->is_Phi(), "" ); - CmpNode *cmp = clone_bool( (PhiNode*)phi, loop ); + assert(phi->is_Phi(), ""); + CmpNode *cmp = clone_bool((PhiNode*)phi, loop); _igvn.replace_input_of(b, 1, cmp); } } - if( split_cex_set ) { - while( split_cex_set->size() ) { + if (split_cex_set) { + while (split_cex_set->size()) { Node *b = split_cex_set->pop(); - assert( b->in(0)->is_Region(), "" ); - assert( b->in(1)->is_Phi(), "" ); - assert( b->in(0)->in(0) == b->in(1)->in(0), "" ); - split_up( b, b->in(0), NULL ); + assert(b->in(0)->is_Region(), ""); + assert(b->in(1)->is_Phi(), ""); + assert(b->in(0)->in(0) == b->in(1)->in(0), ""); + split_up(b, b->in(0), NULL); } } diff --git a/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java b/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java new file mode 100644 index 00000000000..4c35789c718 --- /dev/null +++ b/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. 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 8191887 + * @summary loop cloning misses support for Opaque4 node + * @modules java.base/jdk.internal.misc:+open + * + * @run main/othervm -XX:-BackgroundCompilation TestLoopUnswitching + * + */ + +import jdk.internal.misc.Unsafe; +import java.lang.reflect.Field; +import java.util.Arrays; + +public class TestLoopUnswitching { + + static final jdk.internal.misc.Unsafe UNSAFE = Unsafe.getUnsafe(); + static final long F_OFFSET; + + static class A { + int f; + A(int f) { + this.f = f; + } + } + + static { + try { + Field fField = A.class.getDeclaredField("f"); + F_OFFSET = UNSAFE.objectFieldOffset(fField); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static int test1(A[] arr, boolean flag1, boolean flag2) { + int res = 0; + for (int i = 0; i < 10; i++) { + A a = arr[i]; + if (flag1) { // triggers unswitching + res += UNSAFE.getInt(a, F_OFFSET); + } + if (flag2) { + // Opaque4 node here is in the loop but If is out of the loop + res += UNSAFE.getInt(a, F_OFFSET); + break; + } + res += UNSAFE.getInt(a, F_OFFSET); + } + return res; + } + + static int test2(A[] arr, boolean flag1, boolean flag2) { + int res = 0; + for (int i = 0; i < 10; i++) { + A a = arr[i]; + if (flag1) { // triggers unswitching + res += UNSAFE.getInt(a, F_OFFSET); + } + if (flag2) { + // Opaque4 node here is out of the loop but Bool is in the the loop + res += UNSAFE.getInt(a, F_OFFSET); + break; + } + res += a.f; + } + return res; + } + + static public void main(String[] args) { + A[] arr = new A[1000]; + Arrays.fill(arr, new A(0x42)); + for (int i = 0; i < 20000; i++) { + test1(arr, (i%2) == 0, (i%2) == 0); + test2(arr, (i%2) == 0, (i%2) == 0); + } + } + +} From 27d7667a34322246349ed5e9b9256cb9621390cc Mon Sep 17 00:00:00 2001 From: Yang Zhang <yang.zhang@linaro.org> Date: Tue, 20 Jun 2017 16:25:53 +0800 Subject: [PATCH 040/165] 8181633: Vectorization fails for some multiplication with constant cases Reviewed-by: kvn --- src/hotspot/share/opto/superword.cpp | 16 +++++++++++++++- src/hotspot/share/opto/superword.hpp | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index a66df0a979d..86145b7647a 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1102,7 +1102,7 @@ bool SuperWord::stmts_can_pack(Node* s1, Node* s2, int align) { } if (isomorphic(s1, s2)) { - if (independent(s1, s2) || reduction(s1, s2)) { + if ((independent(s1, s2) && have_similar_inputs(s1, s2)) || reduction(s1, s2)) { if (!exists_at(s1, 0) && !exists_at(s2, 1)) { if (!s1->is_Mem() || are_adjacent_refs(s1, s2)) { int s1_align = alignment(s1); @@ -1180,6 +1180,20 @@ bool SuperWord::independent(Node* s1, Node* s2) { return independent_path(shallow, deep); } +//--------------------------have_similar_inputs----------------------- +// For a node pair (s1, s2) which is isomorphic and independent, +// do s1 and s2 have similar input edges? +bool SuperWord::have_similar_inputs(Node* s1, Node* s2) { + // assert(isomorphic(s1, s2) == true, "check isomorphic"); + // assert(independent(s1, s2) == true, "check independent"); + if (s1->req() > 1 && !s1->is_Store() && !s1->is_Load()) { + for (uint i = 1; i < s1->req(); i++) { + if (s1->in(i)->Opcode() != s2->in(i)->Opcode()) return false; + } + } + return true; +} + //------------------------------reduction--------------------------- // Is there a data path between s1 and s2 and the nodes reductions? bool SuperWord::reduction(Node* s1, Node* s2) { diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp index 28a4f3c28de..8a4241afdf4 100644 --- a/src/hotspot/share/opto/superword.hpp +++ b/src/hotspot/share/opto/superword.hpp @@ -442,6 +442,9 @@ class SuperWord : public ResourceObj { bool isomorphic(Node* s1, Node* s2); // Is there no data path from s1 to s2 or s2 to s1? bool independent(Node* s1, Node* s2); + // For a node pair (s1, s2) which is isomorphic and independent, + // do s1 and s2 have similar input edges? + bool have_similar_inputs(Node* s1, Node* s2); // Is there a data path between s1 and s2 and both are reductions? bool reduction(Node* s1, Node* s2); // Helper for independent From 7fd078b1445c8c0e35f41e6613094480251f0c72 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Mon, 4 Dec 2017 19:12:57 +0100 Subject: [PATCH 041/165] 8146977: Move the output "Building configuration X (matching Y)" to lower log level Reviewed-by: erikj --- make/InitSupport.gmk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk index f4605766ccf..48af7838450 100644 --- a/make/InitSupport.gmk +++ b/make/InitSupport.gmk @@ -279,7 +279,9 @@ ifeq ($(HAS_SPEC),) # generated files. ifeq ($$(MAKE_RESTARTS),) ifeq ($$(words $$(matching_confs)), 1) - $$(info Building configuration '$$(matching_confs)' (matching CONF=$$(CONF))) + ifneq ($$(findstring $$(LOG_LEVEL), info debug trace),) + $$(info Building configuration '$$(matching_confs)' (matching CONF=$$(CONF))) + endif else $$(info Building these configurations (matching CONF=$$(CONF)):) $$(foreach var, $$(matching_confs), $$(info * $$(var))) From 6d82950756865b9f77c841bb66cf2ad78c45b8de Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Mon, 4 Dec 2017 19:49:01 +0100 Subject: [PATCH 042/165] 8192995: run-test gtest should use all jvm variants, not just "server" Reviewed-by: erikj --- doc/testing.html | 1 + doc/testing.md | 6 ++++++ make/RunTests.gmk | 29 ++++++++++++++++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/doc/testing.html b/doc/testing.html index d71f7e569e8..be4c23302bd 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -57,6 +57,7 @@ $ make exploded-run-test TEST=hotspot_tier1</code></pre> <h3 id="gtest">Gtest</h3> <p>Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just <code>gtest</code>, or as a fully qualified test descriptor <code>gtest:all</code>.</p> <p>If you want, you can single out an individual test or a group of tests, for instance <code>gtest:LogDecorations</code> or <code>gtest:LogDecorations.level_test_vm</code>. This can be particularly useful if you want to run a shaky test repeatedly.</p> +<p>For Gtest, there is a separate test suite for each JVM variant. The JVM variant is defined by adding <code>/<variant></code> to the test descriptor, e.g. <code>gtest:Log/client</code>. If you specify no variant, gtest will run once for each JVM variant present (e.g. server, client). So if you only have the server JVM present, then <code>gtest:all</code> will be equivalent to <code>gtest:all/server</code>.</p> <h2 id="test-results-and-summary">Test results and summary</h2> <p>At the end of the test run, a summary of all tests run will be presented. This will have a consistent look, regardless of what test suites were used. This is a sample summary:</p> <pre><code>============================== diff --git a/doc/testing.md b/doc/testing.md index ffd685ff516..da9b8ca7624 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -81,6 +81,12 @@ If you want, you can single out an individual test or a group of tests, for instance `gtest:LogDecorations` or `gtest:LogDecorations.level_test_vm`. This can be particularly useful if you want to run a shaky test repeatedly. +For Gtest, there is a separate test suite for each JVM variant. The JVM variant +is defined by adding `/<variant>` to the test descriptor, e.g. +`gtest:Log/client`. If you specify no variant, gtest will run once for each JVM +variant present (e.g. server, client). So if you only have the server JVM +present, then `gtest:all` will be equivalent to `gtest:all/server`. + ## Test results and summary At the end of the test run, a summary of all tests run will be presented. This diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 1153bae8f02..2475106fbc5 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -88,6 +88,9 @@ ifneq ($(wildcard $(JTREG_FAILURE_HANDLER)), ) -timeoutHandlerTimeout:0 endif +GTEST_LAUNCHER_DIRS := $(patsubst %/gtestLauncher, %, $(wildcard $(TEST_IMAGE_DIR)/hotspot/gtest/*/gtestLauncher)) +GTEST_VARIANTS := $(strip $(patsubst $(TEST_IMAGE_DIR)/hotspot/gtest/%, %, $(GTEST_LAUNCHER_DIRS))) + ################################################################################ # Parse control variables ################################################################################ @@ -166,16 +169,23 @@ hotspot_JTREG_PROBLEM_LIST += $(TOPDIR)/test/hotspot/jtreg/ProblemList.txt # Helper function to determine if a test specification is a Gtest test # # It is a Gtest test if it is either "gtest", or "gtest:" followed by an optional -# test filter string. +# test filter string, and an optional "/<variant>" to select a specific JVM +# variant. If no variant is specified, all found variants are tested. define ParseGtestTestSelection $(if $(filter gtest%, $1), \ $(if $(filter gtest, $1), \ - gtest:all \ + $(addprefix gtest:all/, $(GTEST_VARIANTS)) \ , \ - $(if $(filter gtest:, $1), \ - gtest:all \ + $(if $(strip $(or $(filter gtest/%, $1) $(filter gtest:/%, $1))), \ + $(patsubst gtest:/%, gtest:all/%, $(patsubst gtest/%, gtest:/%, $1)) \ , \ - $1 \ + $(if $(filter gtest:%, $1), \ + $(if $(findstring /, $1), \ + $1 \ + , \ + $(addprefix $1/, $(GTEST_VARIANTS)) \ + ) \ + ) \ ) \ ) \ ) @@ -320,7 +330,12 @@ define SetupRunGtestTestBody $1_TEST_SUPPORT_DIR := $$(TEST_SUPPORT_DIR)/$1 $1_EXITCODE := $$($1_TEST_RESULTS_DIR)/exitcode.txt - $1_TEST_NAME := $$(strip $$(patsubst gtest:%, %, $$($1_TEST))) + $1_VARIANT := $$(lastword $$(subst /, , $$($1_TEST))) + ifeq ($$(filter $$($1_VARIANT), $$(GTEST_VARIANTS)), ) + $$(error Invalid gtest variant '$$($1_VARIANT)'. Valid variants: $$(GTEST_VARIANTS)) + endif + $1_TEST_NAME := $$(strip $$(patsubst %/$$($1_VARIANT), %, \ + $$(patsubst gtest:%, %, $$($1_TEST)))) ifneq ($$($1_TEST_NAME), all) $1_GTEST_FILTER := --gtest_filter=$$($1_TEST_NAME)* endif @@ -334,7 +349,7 @@ define SetupRunGtestTestBody $$(call LogWarn, Running test '$$($1_TEST)') $$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR)) $$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/gtest, \ - $$(FIXPATH) $$(TEST_IMAGE_DIR)/hotspot/gtest/server/gtestLauncher \ + $$(FIXPATH) $$(TEST_IMAGE_DIR)/hotspot/gtest/$$($1_VARIANT)/gtestLauncher \ -jdk $(JDK_IMAGE_DIR) $$($1_GTEST_FILTER) \ --gtest_output=xml:$$($1_TEST_RESULTS_DIR)/gtest.xml \ $$($1_GTEST_REPEAT) $$(GTEST_OPTIONS) $$(GTEST_VM_OPTIONS) \ From f4db9575d11d7c00d74cdfdbb05e111b199b38b6 Mon Sep 17 00:00:00 2001 From: Stuart Marks <smarks@openjdk.org> Date: Mon, 4 Dec 2017 11:50:04 -0800 Subject: [PATCH 043/165] 8177290: add copy factory methods for unmodifiable List, Set, Map 8184690: add Collectors for collecting into unmodifiable List, Set, and Map Reviewed-by: alanb, briangoetz, dholmes, jrose, rriggs, scolebourne --- .../share/classes/java/util/Collection.java | 100 +++++++++++-- .../share/classes/java/util/Collections.java | 40 ++--- .../share/classes/java/util/List.java | 86 +++++++---- .../share/classes/java/util/Map.java | 97 +++++++----- .../share/classes/java/util/Set.java | 87 +++++++---- .../classes/java/util/stream/Collectors.java | 140 +++++++++++++++++- test/jdk/java/util/Collection/MOAT.java | 56 ++++++- .../java/util/Collection/SetFactories.java | 58 +++++++- test/jdk/java/util/List/ListFactories.java | 54 ++++++- test/jdk/java/util/Map/MapFactories.java | 110 ++++++++++++-- 10 files changed, 675 insertions(+), 153 deletions(-) diff --git a/src/java.base/share/classes/java/util/Collection.java b/src/java.base/share/classes/java/util/Collection.java index e66bd49aa78..d016f8c374b 100644 --- a/src/java.base/share/classes/java/util/Collection.java +++ b/src/java.base/share/classes/java/util/Collection.java @@ -54,19 +54,15 @@ import java.util.stream.StreamSupport; * constructors) but all of the general-purpose {@code Collection} * implementations in the Java platform libraries comply. * - * <p>The "destructive" methods contained in this interface, that is, the - * methods that modify the collection on which they operate, are specified to - * throw {@code UnsupportedOperationException} if this collection does not - * support the operation. If this is the case, these methods may, but are not - * required to, throw an {@code UnsupportedOperationException} if the - * invocation would have no effect on the collection. For example, invoking - * the {@link #addAll(Collection)} method on an unmodifiable collection may, - * but is not required to, throw the exception if the collection to be added - * is empty. + * <p>Certain methods are specified to be + * <i>optional</i>. If a collection implementation doesn't implement a + * particular operation, it should define the corresponding method to throw + * {@code UnsupportedOperationException}. Such methods are marked "optional + * operation" in method specifications of the collections interfaces. * - * <p><a id="optional-restrictions"> - * Some collection implementations have restrictions on the elements that - * they may contain.</a> For example, some implementations prohibit null elements, + * <p><a id="optional-restrictions"></a>Some collection implementations + * have restrictions on the elements that they may contain. + * For example, some implementations prohibit null elements, * and some have restrictions on the types of their elements. Attempting to * add an ineligible element throws an unchecked exception, typically * {@code NullPointerException} or {@code ClassCastException}. Attempting @@ -111,6 +107,86 @@ import java.util.stream.StreamSupport; * methods. Implementations may optionally handle the self-referential scenario, * however most current implementations do not do so. * + * <h2><a id="view">View Collections</a></h2> + * + * <p>Most collections manage storage for elements they contain. By contrast, <i>view + * collections</i> themselves do not store elements, but instead they rely on a + * backing collection to store the actual elements. Operations that are not handled + * by the view collection itself are delegated to the backing collection. Examples of + * view collections include the wrapper collections returned by methods such as + * {@link Collections#checkedCollection Collections.checkedCollection}, + * {@link Collections#synchronizedCollection Collections.synchronizedCollection}, and + * {@link Collections#unmodifiableCollection Collections.unmodifiableCollection}. + * Other examples of view collections include collections that provide a + * different representation of the same elements, for example, as + * provided by {@link List#subList List.subList}, + * {@link NavigableSet#subSet NavigableSet.subSet}, or + * {@link Map#entrySet Map.entrySet}. + * Any changes made to the backing collection are visible in the view collection. + * Correspondingly, any changes made to the view collection — if changes + * are permitted — are written through to the backing collection. + * Although they technically aren't collections, instances of + * {@link Iterator} and {@link ListIterator} can also allow modifications + * to be written through to the backing collection, and in some cases, + * modifications to the backing collection will be visible to the Iterator + * during iteration. + * + * <h2><a id="unmodifiable">Unmodifiable Collections</a></h2> + * + * <p>Certain methods of this interface are considered "destructive" and are called + * "mutator" methods in that they modify the group of objects contained within + * the collection on which they operate. They can be specified to throw + * {@code UnsupportedOperationException} if this collection implementation + * does not support the operation. Such methods should (but are not required + * to) throw an {@code UnsupportedOperationException} if the invocation would + * have no effect on the collection. For example, consider a collection that + * does not support the {@link #add add} operation. What will happen if the + * {@link #addAll addAll} method is invoked on this collection, with an empty + * collection as the argument? The addition of zero elements has no effect, + * so it is permissible for this collection simply to do nothing and not to throw + * an exception. However, it is recommended that such cases throw an exception + * unconditionally, as throwing only in certain cases can lead to + * programming errors. + * + * <p>An <i>unmodifiable collection</i> is a collection, all of whose + * mutator methods (as defined above) are specified to throw + * {@code UnsupportedOperationException}. Such a collection thus cannot be + * modified by calling any methods on it. For a collection to be properly + * unmodifiable, any view collections derived from it must also be unmodifiable. + * For example, if a List is unmodifiable, the List returned by + * {@link List#subList List.subList} is also unmodifiable. + * + * <p>An unmodifiable collection is not necessarily immutable. If the + * contained elements are mutable, the entire collection is clearly + * mutable, even though it might be unmodifiable. For example, consider + * two unmodifiable lists containing mutable elements. The result of calling + * {@code list1.equals(list2)} might differ from one call to the next if + * the elements had been mutated, even though both lists are unmodifiable. + * However, if an unmodifiable collection contains all immutable elements, + * it can be considered effectively immutable. + * + * <h2><a id="unmodview">Unmodifiable View Collections</a></h2> + * + * <p>An <i>unmodifiable view collection</i> is a collection that is unmodifiable + * and that is also a view onto a backing collection. Its mutator methods throw + * {@code UnsupportedOperationException}, as described above, while + * reading and querying methods are delegated to the backing collection. + * The effect is to provide read-only access to the backing collection. + * This is useful for a component to provide users with read access to + * an internal collection, while preventing them from modifying such + * collections unexpectedly. Examples of unmodifiable view collections + * are those returned by the + * {@link Collections#unmodifiableCollection Collections.unmodifiableCollection}, + * {@link Collections#unmodifiableList Collections.unmodifiableList}, and + * related methods. + * + * <p>Note that changes to the backing collection might still be possible, + * and if they occur, they are visible through the unmodifiable view. Thus, + * an unmodifiable view collection is not necessarily immutable. However, + * if the backing collection of an unmodifiable view is effectively immutable, + * or if the only reference to the backing collection is through an + * unmodifiable view, the view can be considered effectively immutable. + * * <p>This interface is a member of the * <a href="{@docRoot}/java/util/package-summary.html#CollectionsFramework"> * Java Collections Framework</a>. diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java index 8aa9c53c455..4904ef56c18 100644 --- a/src/java.base/share/classes/java/util/Collections.java +++ b/src/java.base/share/classes/java/util/Collections.java @@ -989,9 +989,8 @@ public class Collections { // Unmodifiable Wrappers /** - * Returns an unmodifiable view of the specified collection. This method - * allows modules to provide users with "read-only" access to internal - * collections. Query operations on the returned collection "read through" + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified collection. Query operations on the returned collection "read through" * to the specified collection, and attempts to modify the returned * collection, whether direct or via its iterator, result in an * {@code UnsupportedOperationException}.<p> @@ -1102,9 +1101,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified set. This method allows - * modules to provide users with "read-only" access to internal sets. - * Query operations on the returned set "read through" to the specified + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified set. Query operations on the returned set "read through" to the specified * set, and attempts to modify the returned set, whether direct or via its * iterator, result in an {@code UnsupportedOperationException}.<p> * @@ -1132,9 +1130,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified sorted set. This method - * allows modules to provide users with "read-only" access to internal - * sorted sets. Query operations on the returned sorted set "read + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified sorted set. Query operations on the returned sorted set "read * through" to the specified sorted set. Attempts to modify the returned * sorted set, whether direct, via its iterator, or via its * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in @@ -1180,9 +1177,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified navigable set. This method - * allows modules to provide users with "read-only" access to internal - * navigable sets. Query operations on the returned navigable set "read + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified navigable set. Query operations on the returned navigable set "read * through" to the specified navigable set. Attempts to modify the returned * navigable set, whether direct, via its iterator, or via its * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in @@ -1269,9 +1265,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified list. This method allows - * modules to provide users with "read-only" access to internal - * lists. Query operations on the returned list "read through" to the + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified list. Query operations on the returned list "read through" to the * specified list, and attempts to modify the returned list, whether * direct or via its iterator, result in an * {@code UnsupportedOperationException}.<p> @@ -1415,9 +1410,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified map. This method - * allows modules to provide users with "read-only" access to internal - * maps. Query operations on the returned map "read through" + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified map. Query operations on the returned map "read through" * to the specified map, and attempts to modify the returned * map, whether direct or via its collection views, result in an * {@code UnsupportedOperationException}.<p> @@ -1765,9 +1759,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified sorted map. This method - * allows modules to provide users with "read-only" access to internal - * sorted maps. Query operations on the returned sorted map "read through" + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified sorted map. Query operations on the returned sorted map "read through" * to the specified sorted map. Attempts to modify the returned * sorted map, whether direct, via its collection views, or via its * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in @@ -1809,9 +1802,8 @@ public class Collections { } /** - * Returns an unmodifiable view of the specified navigable map. This method - * allows modules to provide users with "read-only" access to internal - * navigable maps. Query operations on the returned navigable map "read + * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the + * specified navigable map. Query operations on the returned navigable map "read * through" to the specified navigable map. Attempts to modify the returned * navigable map, whether direct, via its collection views, or via its * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 8ba368bf6f6..5672508afb1 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.java @@ -87,15 +87,16 @@ import java.util.function.UnaryOperator; * Such exceptions are marked as "optional" in the specification for this * interface. * - * <h2><a id="immutable">Immutable List Static Factory Methods</a></h2> - * <p>The {@link List#of(Object...) List.of()} static factory methods - * provide a convenient way to create immutable lists. The {@code List} + * <h2><a id="unmodifiable">Unmodifiable Lists</a></h2> + * <p>The {@link List#of(Object...) List.of} and + * {@link List#copyOf List.copyOf} static factory methods + * provide a convenient way to create unmodifiable lists. The {@code List} * instances created by these methods have the following characteristics: * * <ul> - * <li>They are <em>structurally immutable</em>. Elements cannot be added, removed, - * or replaced. Calling any mutator method will always cause - * {@code UnsupportedOperationException} to be thrown. + * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Elements cannot + * be added, removed, or replaced. Calling any mutator method on the List + * will always cause {@code UnsupportedOperationException} to be thrown. * However, if the contained elements are themselves mutable, * this may cause the List's contents to appear to change. * <li>They disallow {@code null} elements. Attempts to create them with @@ -777,9 +778,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing zero elements. + * Returns an unmodifiable list containing zero elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @return an empty {@code List} @@ -791,9 +792,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing one element. + * Returns an unmodifiable list containing one element. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the single element @@ -807,9 +808,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing two elements. + * Returns an unmodifiable list containing two elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -824,9 +825,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing three elements. + * Returns an unmodifiable list containing three elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -842,9 +843,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing four elements. + * Returns an unmodifiable list containing four elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -861,9 +862,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing five elements. + * Returns an unmodifiable list containing five elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -881,9 +882,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing six elements. + * Returns an unmodifiable list containing six elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -903,9 +904,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing seven elements. + * Returns an unmodifiable list containing seven elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -926,9 +927,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing eight elements. + * Returns an unmodifiable list containing eight elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -950,9 +951,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing nine elements. + * Returns an unmodifiable list containing nine elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -975,9 +976,9 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing ten elements. + * Returns an unmodifiable list containing ten elements. * - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @param <E> the {@code List}'s element type * @param e1 the first element @@ -1001,8 +1002,8 @@ public interface List<E> extends Collection<E> { } /** - * Returns an immutable list containing an arbitrary number of elements. - * See <a href="#immutable">Immutable List Static Factory Methods</a> for details. + * Returns an unmodifiable list containing an arbitrary number of elements. + * See <a href="#unmodifiable">Unmodifiable Lists</a> for details. * * @apiNote * This method also accepts a single array as an argument. The element type of @@ -1039,4 +1040,29 @@ public interface List<E> extends Collection<E> { return new ImmutableCollections.ListN<>(elements); } } + + /** + * Returns an <a href="#unmodifiable">unmodifiable List</a> containing the elements of + * the given Collection, in its iteration order. The given Collection must not be null, + * and it must not contain any null elements. If the given Collection is subsequently + * modified, the returned List will not reflect such modifications. + * + * @implNote + * If the given Collection is an <a href="#unmodifiable">unmodifiable List</a>, + * calling copyOf will generally not create a copy. + * + * @param <E> the {@code List}'s element type + * @param coll a {@code Collection} from which elements are drawn, must be non-null + * @return a {@code List} containing the elements of the given {@code Collection} + * @throws NullPointerException if coll is null, or if it contains any nulls + * @since 10 + */ + @SuppressWarnings("unchecked") + static <E> List<E> copyOf(Collection<? extends E> coll) { + if (coll instanceof ImmutableCollections.AbstractImmutableList) { + return (List<E>)coll; + } else { + return (List<E>)List.of(coll.toArray()); + } + } } diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index e3bee23b7ea..efe330e80ad 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.java @@ -110,17 +110,18 @@ import java.io.Serializable; * Implementations may optionally handle the self-referential scenario, however * most current implementations do not do so. * - * <h2><a id="immutable">Immutable Map Static Factory Methods</a></h2> - * <p>The {@link Map#of() Map.of()} and - * {@link Map#ofEntries(Map.Entry...) Map.ofEntries()} - * static factory methods provide a convenient way to create immutable maps. + * <h2><a id="unmodifiable">Unmodifiable Maps</a></h2> + * <p>The {@link Map#of() Map.of}, + * {@link Map#ofEntries(Map.Entry...) Map.ofEntries}, and + * {@link Map#copyOf Map.copyOf} + * static factory methods provide a convenient way to create unmodifiable maps. * The {@code Map} * instances created by these methods have the following characteristics: * * <ul> - * <li>They are <em>structurally immutable</em>. Keys and values cannot be added, - * removed, or updated. Calling any mutator method will always cause - * {@code UnsupportedOperationException} to be thrown. + * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Keys and values + * cannot be added, removed, or updated. Calling any mutator method on the Map + * will always cause {@code UnsupportedOperationException} to be thrown. * However, if the contained keys or values are themselves mutable, this may cause the * Map to behave inconsistently or its contents to appear to change. * <li>They disallow {@code null} keys and values. Attempts to create them with @@ -1276,8 +1277,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing zero mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing zero mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1290,8 +1291,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing a single mapping. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing a single mapping. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1307,8 +1308,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing two mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing two mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1327,8 +1328,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing three mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing three mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1349,8 +1350,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing four mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing four mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1373,8 +1374,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing five mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing five mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1399,8 +1400,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing six mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing six mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1429,8 +1430,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing seven mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing seven mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1461,8 +1462,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing eight mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing eight mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1495,8 +1496,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing nine mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing nine mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1531,8 +1532,8 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing ten mappings. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * Returns an unmodifiable map containing ten mappings. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @param <K> the {@code Map}'s key type * @param <V> the {@code Map}'s value type @@ -1569,9 +1570,9 @@ public interface Map<K, V> { } /** - * Returns an immutable map containing keys and values extracted from the given entries. + * Returns an unmodifiable map containing keys and values extracted from the given entries. * The entries themselves are not stored in the map. - * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details. + * See <a href="#unmodifiable">Unmodifiable Maps</a> for details. * * @apiNote * It is convenient to create the map entries using the {@link Map#entry Map.entry()} method. @@ -1602,15 +1603,17 @@ public interface Map<K, V> { @SafeVarargs @SuppressWarnings("varargs") static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) { - if (entries.length == 0) { // implicit null check of entries + if (entries.length == 0) { // implicit null check of entries array return ImmutableCollections.Map0.instance(); } else if (entries.length == 1) { + // implicit null check of the array slot return new ImmutableCollections.Map1<>(entries[0].getKey(), entries[0].getValue()); } else { Object[] kva = new Object[entries.length << 1]; int a = 0; for (Entry<? extends K, ? extends V> entry : entries) { + // implicit null checks of each array slot kva[a++] = entry.getKey(); kva[a++] = entry.getValue(); } @@ -1619,7 +1622,7 @@ public interface Map<K, V> { } /** - * Returns an immutable {@link Entry} containing the given key and value. + * Returns an unmodifiable {@link Entry} containing the given key and value. * These entries are suitable for populating {@code Map} instances using the * {@link Map#ofEntries Map.ofEntries()} method. * The {@code Entry} instances created by this method have the following characteristics: @@ -1627,7 +1630,7 @@ public interface Map<K, V> { * <ul> * <li>They disallow {@code null} keys and values. Attempts to create them using a {@code null} * key or value result in {@code NullPointerException}. - * <li>They are immutable. Calls to {@link Entry#setValue Entry.setValue()} + * <li>They are unmodifiable. Calls to {@link Entry#setValue Entry.setValue()} * on a returned {@code Entry} result in {@code UnsupportedOperationException}. * <li>They are not serializable. * <li>They are <a href="../lang/doc-files/ValueBased.html">value-based</a>. @@ -1655,4 +1658,30 @@ public interface Map<K, V> { // KeyValueHolder checks for nulls return new KeyValueHolder<>(k, v); } + + /** + * Returns an <a href="#unmodifiable">unmodifiable Map</a> containing the entries + * of the given Map. The given Map must not be null, and it must not contain any + * null keys or values. If the given Map is subsequently modified, the returned + * Map will not reflect such modifications. + * + * @implNote + * If the given Map is an <a href="#unmodifiable">unmodifiable Map</a>, + * calling copyOf will generally not create a copy. + * + * @param <K> the {@code Map}'s key type + * @param <V> the {@code Map}'s value type + * @param map a {@code Map} from which entries are drawn, must be non-null + * @return a {@code Map} containing the entries of the given {@code Map} + * @throws NullPointerException if map is null, or if it contains any null keys or values + * @since 10 + */ + @SuppressWarnings({"rawtypes","unchecked"}) + static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) { + if (map instanceof ImmutableCollections.AbstractImmutableMap) { + return (Map<K,V>)map; + } else { + return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0])); + } + } } diff --git a/src/java.base/share/classes/java/util/Set.java b/src/java.base/share/classes/java/util/Set.java index 32966e993e3..b662896dd7f 100644 --- a/src/java.base/share/classes/java/util/Set.java +++ b/src/java.base/share/classes/java/util/Set.java @@ -63,15 +63,16 @@ package java.util; * Such exceptions are marked as "optional" in the specification for this * interface. * - * <h2><a id="immutable">Immutable Set Static Factory Methods</a></h2> - * <p>The {@link Set#of(Object...) Set.of()} static factory methods - * provide a convenient way to create immutable sets. The {@code Set} + * <h2><a id="unmodifiable">Unmodifiable Sets</a></h2> + * <p>The {@link Set#of(Object...) Set.of} and + * {@link Set#copyOf Set.copyOf} static factory methods + * provide a convenient way to create unmodifiable sets. The {@code Set} * instances created by these methods have the following characteristics: * * <ul> - * <li>They are <em>structurally immutable</em>. Elements cannot be added or - * removed. Calling any mutator method will always cause - * {@code UnsupportedOperationException} to be thrown. + * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Elements cannot + * be added or removed. Calling any mutator method on the Set + * will always cause {@code UnsupportedOperationException} to be thrown. * However, if the contained elements are themselves mutable, this may cause the * Set to behave inconsistently or its contents to appear to change. * <li>They disallow {@code null} elements. Attempts to create them with @@ -439,8 +440,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing zero elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing zero elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @return an empty {@code Set} @@ -452,8 +453,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing one element. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing one element. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the single element @@ -467,8 +468,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing two elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing two elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -484,8 +485,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing three elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing three elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -502,8 +503,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing four elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing four elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -521,8 +522,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing five elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing five elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -541,8 +542,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing six elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing six elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -563,8 +564,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing seven elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing seven elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -586,8 +587,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing eight elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing eight elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -610,8 +611,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing nine elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing nine elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -635,8 +636,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing ten elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing ten elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @param <E> the {@code Set}'s element type * @param e1 the first element @@ -661,8 +662,8 @@ public interface Set<E> extends Collection<E> { } /** - * Returns an immutable set containing an arbitrary number of elements. - * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details. + * Returns an unmodifiable set containing an arbitrary number of elements. + * See <a href="#unmodifiable">Unmodifiable Sets</a> for details. * * @apiNote * This method also accepts a single array as an argument. The element type of @@ -700,4 +701,30 @@ public interface Set<E> extends Collection<E> { return new ImmutableCollections.SetN<>(elements); } } + + /** + * Returns an <a href="#unmodifiable">unmodifiable Set</a> containing the elements + * of the given Collection. The given Collection must not be null, and it must not + * contain any null elements. If the given Collection contains duplicate elements, + * an arbitrary element of the duplicates is preserved. If the given Collection is + * subsequently modified, the returned Set will not reflect such modifications. + * + * @implNote + * If the given Collection is an <a href="#unmodifiable">unmodifiable Set</a>, + * calling copyOf will generally not create a copy. + * + * @param <E> the {@code Set}'s element type + * @param coll a {@code Collection} from which elements are drawn, must be non-null + * @return a {@code Set} containing the elements of the given {@code Collection} + * @throws NullPointerException if coll is null, or if it contains any nulls + * @since 10 + */ + @SuppressWarnings("unchecked") + static <E> Set<E> copyOf(Collection<? extends E> coll) { + if (coll instanceof ImmutableCollections.AbstractImmutableSet) { + return (Set<E>)coll; + } else { + return (Set<E>)Set.of(new HashSet<>(coll).toArray()); + } + } } diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index 02f9ada39c0..26d98bf6d42 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -116,6 +116,8 @@ public final class Collectors { = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH)); static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet(); + static final Set<Collector.Characteristics> CH_UNORDERED_NOID + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED)); private Collectors() { } @@ -278,6 +280,26 @@ public final class Collectors { CH_ID); } + /** + * Returns a {@code Collector} that accumulates the input elements into an + * <a href="../List.html#unmodifiable">unmodifiable List</a> in encounter + * order. The returned Collector disallows null values and will throw + * {@code NullPointerException} if it is presented with a null value. + * + * @param <T> the type of the input elements + * @return a {@code Collector} that accumulates the input elements into an + * <a href="../List.html#unmodifiable">unmodifiable List</a> in encounter order + * @since 10 + */ + @SuppressWarnings("unchecked") + public static <T> + Collector<T, ?, List<T>> toUnmodifiableList() { + return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, + (left, right) -> { left.addAll(right); return left; }, + list -> (List<T>)List.of(list.toArray()), + CH_NOID); + } + /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code Set}. There are no guarantees on the type, mutability, @@ -305,6 +327,36 @@ public final class Collectors { CH_UNORDERED_ID); } + /** + * Returns a {@code Collector} that accumulates the input elements into an + * <a href="../Set.html#unmodifiable">unmodifiable Set</a>. The returned + * Collector disallows null values and will throw {@code NullPointerException} + * if it is presented with a null value. If the input contains duplicate elements, + * an arbitrary element of the duplicates is preserved. + * + * <p>This is an {@link Collector.Characteristics#UNORDERED unordered} + * Collector. + * + * @param <T> the type of the input elements + * @return a {@code Collector} that accumulates the input elements into an + * <a href="../Set.html#unmodifiable">unmodifiable Set</a> + * @since 10 + */ + @SuppressWarnings("unchecked") + public static <T> + Collector<T, ?, Set<T>> toUnmodifiableSet() { + return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, + (left, right) -> { + if (left.size() < right.size()) { + right.addAll(left); return right; + } else { + left.addAll(right); return left; + } + }, + set -> (Set<T>)Set.of(set.toArray()), + CH_UNORDERED_NOID); + } + /** * Returns a {@code Collector} that concatenates the input elements into a * {@code String}, in encounter order. @@ -1353,7 +1405,7 @@ public final class Collectors { * <p>If the mapped keys contain duplicates (according to * {@link Object#equals(Object)}), an {@code IllegalStateException} is * thrown when the collection operation is performed. If the mapped keys - * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)} + * might have duplicates, use {@link #toMap(Function, Function, BinaryOperator)} * instead. * * <p>There are no guarantees on the type, mutability, serializability, @@ -1410,6 +1462,45 @@ public final class Collectors { CH_ID); } + /** + * Returns a {@code Collector} that accumulates the input elements into an + * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, + * whose keys and values are the result of applying the provided + * mapping functions to the input elements. + * + * <p>If the mapped keys contain duplicates (according to + * {@link Object#equals(Object)}), an {@code IllegalStateException} is + * thrown when the collection operation is performed. If the mapped keys + * might have duplicates, use {@link #toUnmodifiableMap(Function, Function, BinaryOperator)} + * to handle merging of the values. + * + * <p>The returned Collector disallows null keys and values. If either mapping function + * returns null, {@code NullPointerException} will be thrown. + * + * @param <T> the type of the input elements + * @param <K> the output type of the key mapping function + * @param <U> the output type of the value mapping function + * @param keyMapper a mapping function to produce keys, must be non-null + * @param valueMapper a mapping function to produce values, must be non-null + * @return a {@code Collector} that accumulates the input elements into an + * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, whose keys and values + * are the result of applying the provided mapping functions to the input elements + * @throws NullPointerException if either keyMapper or valueMapper is null + * + * @see #toUnmodifiableMap(Function, Function, BinaryOperator) + * @since 10 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <T, K, U> + Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper, + Function<? super T, ? extends U> valueMapper) { + Objects.requireNonNull(keyMapper, "keyMapper"); + Objects.requireNonNull(valueMapper, "valueMapper"); + return collectingAndThen( + toMap(keyMapper, valueMapper), + map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0]))); + } + /** * Returns a {@code Collector} that accumulates elements into a * {@code Map} whose keys and values are the result of applying the provided @@ -1473,6 +1564,51 @@ public final class Collectors { return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); } + + /** + * Returns a {@code Collector} that accumulates the input elements into an + * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, + * whose keys and values are the result of applying the provided + * mapping functions to the input elements. + * + * <p>If the mapped + * keys contain duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. + * + * <p>The returned Collector disallows null keys and values. If either mapping function + * returns null, {@code NullPointerException} will be thrown. + * + * @param <T> the type of the input elements + * @param <K> the output type of the key mapping function + * @param <U> the output type of the value mapping function + * @param keyMapper a mapping function to produce keys, must be non-null + * @param valueMapper a mapping function to produce values, must be non-null + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)}, + * must be non-null + * @return a {@code Collector} that accumulates the input elements into an + * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, whose keys and values + * are the result of applying the provided mapping functions to the input elements + * @throws NullPointerException if the keyMapper, valueMapper, or mergeFunction is null + * + * @see #toUnmodifiableMap(Function, Function) + * @since 10 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <T, K, U> + Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper, + Function<? super T, ? extends U> valueMapper, + BinaryOperator<U> mergeFunction) { + Objects.requireNonNull(keyMapper, "keyMapper"); + Objects.requireNonNull(valueMapper, "valueMapper"); + Objects.requireNonNull(mergeFunction, "mergeFunction"); + return collectingAndThen( + toMap(keyMapper, valueMapper, mergeFunction, HashMap::new), + map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0]))); + } + /** * Returns a {@code Collector} that accumulates elements into a * {@code Map} whose keys and values are the result of applying the provided diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java index b250759939f..8693f7483ed 100644 --- a/test/jdk/java/util/Collection/MOAT.java +++ b/test/jdk/java/util/Collection/MOAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -57,6 +57,8 @@ import java.util.*; import java.util.concurrent.*; import static java.util.Collections.*; import java.lang.reflect.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class MOAT { // Collections under test must not be initialized to contain this value, @@ -230,6 +232,17 @@ public class MOAT { testListMutatorsAlwaysThrow(list); } + List<Integer> listCopy = List.copyOf(Arrays.asList(1, 2, 3)); + testCollection(listCopy); + testImmutableList(listCopy); + testListMutatorsAlwaysThrow(listCopy); + + List<Integer> listCollected = Stream.of(1, 2, 3).collect(Collectors.toUnmodifiableList()); + equal(listCollected, List.of(1, 2, 3)); + testCollection(listCollected); + testImmutableList(listCollected); + testListMutatorsAlwaysThrow(listCollected); + // Immutable Set testEmptySet(Set.of()); testCollMutatorsAlwaysThrow(Set.of()); @@ -252,6 +265,18 @@ public class MOAT { testCollMutatorsAlwaysThrow(set); } + Set<Integer> setCopy = Set.copyOf(Arrays.asList(1, 2, 3)); + testCollection(setCopy); + testImmutableSet(setCopy); + testCollMutatorsAlwaysThrow(setCopy); + + Set<Integer> setCollected = Stream.of(1, 1, 2, 3, 2, 3) + .collect(Collectors.toUnmodifiableSet()); + equal(setCollected, Set.of(1, 2, 3)); + testCollection(setCollected); + testImmutableSet(setCollected); + testCollMutatorsAlwaysThrow(setCollected); + // Immutable Map @SuppressWarnings("unchecked") @@ -280,6 +305,35 @@ public class MOAT { testImmutableMap(map); testMapMutatorsAlwaysThrow(map); } + + Map<Integer,Integer> mapCopy = Map.copyOf(new HashMap<>(Map.of(1, 101, 2, 202, 3, 303))); + testMap(mapCopy); + testImmutableMap(mapCopy); + testMapMutatorsAlwaysThrow(mapCopy); + + Map<Integer,Integer> mapCollected1 = + Stream.of(1, 2, 3) + .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i)); + equal(mapCollected1, Map.of(1, 101, 2, 202, 3, 303)); + testMap(mapCollected1); + testImmutableMap(mapCollected1); + testMapMutatorsAlwaysThrow(mapCollected1); + + try { + Stream.of(1, 1, 2, 3, 2, 3) + .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i)); + fail("duplicates should have thrown an exception"); + } catch (IllegalStateException ise) { + pass(); + } + + Map<Integer,Integer> mapCollected2 = + Stream.of(1, 1, 2, 3, 2, 3) + .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i, Integer::sum)); + equal(mapCollected2, Map.of(1, 202, 2, 404, 3, 606)); + testMap(mapCollected2); + testImmutableMap(mapCollected2); + testMapMutatorsAlwaysThrow(mapCollected2); } private static void checkContainsSelf(Collection<Integer> c) { diff --git a/test/jdk/java/util/Collection/SetFactories.java b/test/jdk/java/util/Collection/SetFactories.java index 25a884501e0..80a57bc6adf 100644 --- a/test/jdk/java/util/Collection/SetFactories.java +++ b/test/jdk/java/util/Collection/SetFactories.java @@ -40,6 +40,9 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -275,9 +278,9 @@ public class SetFactories { static <T> T serialClone(T obj) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - oos.close(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(obj); + } ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return (T) ois.readObject(); @@ -285,4 +288,53 @@ public class SetFactories { throw new AssertionError(e); } } + + Set<Integer> genSet() { + return new HashSet<>(Arrays.asList(1, 2, 3)); + } + + @Test + public void copyOfResultsEqual() { + Set<Integer> orig = genSet(); + Set<Integer> copy = Set.copyOf(orig); + + assertEquals(orig, copy); + assertEquals(copy, orig); + } + + @Test + public void copyOfModifiedUnequal() { + Set<Integer> orig = genSet(); + Set<Integer> copy = Set.copyOf(orig); + orig.add(4); + + assertNotEquals(orig, copy); + assertNotEquals(copy, orig); + } + + @Test + public void copyOfIdentity() { + Set<Integer> orig = genSet(); + Set<Integer> copy1 = Set.copyOf(orig); + Set<Integer> copy2 = Set.copyOf(copy1); + + assertNotSame(orig, copy1); + assertSame(copy1, copy2); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullCollection() { + Set<Integer> set = Set.copyOf(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullElements() { + Set<Integer> set = Set.copyOf(Arrays.asList(1, null, 3)); + } + + @Test + public void copyOfAcceptsDuplicates() { + Set<Integer> set = Set.copyOf(Arrays.asList(1, 1, 2, 3, 3, 3)); + assertEquals(set, Set.of(1, 2, 3)); + } } diff --git a/test/jdk/java/util/List/ListFactories.java b/test/jdk/java/util/List/ListFactories.java index e34ca660705..63e3ecd91ac 100644 --- a/test/jdk/java/util/List/ListFactories.java +++ b/test/jdk/java/util/List/ListFactories.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -39,6 +39,9 @@ import static java.util.Arrays.asList; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -221,9 +224,9 @@ public class ListFactories { static <T> T serialClone(T obj) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - oos.close(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(obj); + } ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return (T) ois.readObject(); @@ -231,4 +234,47 @@ public class ListFactories { throw new AssertionError(e); } } + + List<Integer> genList() { + return new ArrayList<>(Arrays.asList(1, 2, 3)); + } + + @Test + public void copyOfResultsEqual() { + List<Integer> orig = genList(); + List<Integer> copy = List.copyOf(orig); + + assertEquals(orig, copy); + assertEquals(copy, orig); + } + + @Test + public void copyOfModifiedUnequal() { + List<Integer> orig = genList(); + List<Integer> copy = List.copyOf(orig); + orig.add(4); + + assertNotEquals(orig, copy); + assertNotEquals(copy, orig); + } + + @Test + public void copyOfIdentity() { + List<Integer> orig = genList(); + List<Integer> copy1 = List.copyOf(orig); + List<Integer> copy2 = List.copyOf(copy1); + + assertNotSame(orig, copy1); + assertSame(copy1, copy2); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullCollection() { + List<Integer> list = List.copyOf(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullElements() { + List<Integer> list = List.copyOf(Arrays.asList(1, null, 3)); + } } diff --git a/test/jdk/java/util/Map/MapFactories.java b/test/jdk/java/util/Map/MapFactories.java index 7495aa386c3..008a9651250 100644 --- a/test/jdk/java/util/Map/MapFactories.java +++ b/test/jdk/java/util/Map/MapFactories.java @@ -42,6 +42,9 @@ import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -70,6 +73,12 @@ public class MapFactories { } // for varargs Map.Entry methods + + @SuppressWarnings("unchecked") + Map.Entry<Integer,String>[] genEmptyEntryArray1() { + return (Map.Entry<Integer,String>[])new Map.Entry<?,?>[1]; + } + @SuppressWarnings("unchecked") Map.Entry<Integer,String>[] genEntries(int n) { return IntStream.range(0, n) @@ -322,21 +331,41 @@ public class MapFactories { } @Test(expectedExceptions=NullPointerException.class) - public void nullKeyDisallowedN() { - Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES); - entries[0] = new AbstractMap.SimpleImmutableEntry(null, "a"); + public void nullKeyDisallowedVar1() { + Map.Entry<Integer,String>[] entries = genEmptyEntryArray1(); + entries[0] = new AbstractMap.SimpleImmutableEntry<>(null, "a"); Map<Integer, String> map = Map.ofEntries(entries); } @Test(expectedExceptions=NullPointerException.class) - public void nullValueDisallowedN() { - Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES); - entries[0] = new AbstractMap.SimpleImmutableEntry(0, null); + public void nullValueDisallowedVar1() { + Map.Entry<Integer,String>[] entries = genEmptyEntryArray1(); + entries[0] = new AbstractMap.SimpleImmutableEntry<>(0, null); Map<Integer, String> map = Map.ofEntries(entries); } @Test(expectedExceptions=NullPointerException.class) - public void nullEntryDisallowedN() { + public void nullEntryDisallowedVar1() { + Map.Entry<Integer,String>[] entries = genEmptyEntryArray1(); + Map<Integer, String> map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowedVarN() { + Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry<>(null, "a"); + Map<Integer, String> map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowedVarN() { + Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry<>(0, null); + Map<Integer, String> map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullEntryDisallowedVarN() { Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES); entries[5] = null; Map<Integer, String> map = Map.ofEntries(entries); @@ -344,7 +373,7 @@ public class MapFactories { @Test(expectedExceptions=NullPointerException.class) public void nullArrayDisallowed() { - Map.ofEntries(null); + Map.ofEntries((Map.Entry<?,?>[])null); } @Test(dataProvider="all") @@ -359,9 +388,9 @@ public class MapFactories { static <T> T serialClone(T obj) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(obj); - oos.close(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(obj); + } ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return (T) ois.readObject(); @@ -370,6 +399,62 @@ public class MapFactories { } } + Map<Integer, String> genMap() { + Map<Integer, String> map = new HashMap<>(); + map.put(1, "a"); + map.put(2, "b"); + map.put(3, "c"); + return map; + } + + @Test + public void copyOfResultsEqual() { + Map<Integer, String> orig = genMap(); + Map<Integer, String> copy = Map.copyOf(orig); + + assertEquals(orig, copy); + assertEquals(copy, orig); + } + + @Test + public void copyOfModifiedUnequal() { + Map<Integer, String> orig = genMap(); + Map<Integer, String> copy = Map.copyOf(orig); + orig.put(4, "d"); + + assertNotEquals(orig, copy); + assertNotEquals(copy, orig); + } + + @Test + public void copyOfIdentity() { + Map<Integer, String> orig = genMap(); + Map<Integer, String> copy1 = Map.copyOf(orig); + Map<Integer, String> copy2 = Map.copyOf(copy1); + + assertNotSame(orig, copy1); + assertSame(copy1, copy2); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullMap() { + Map<Integer, String> map = Map.copyOf(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullKey() { + Map<Integer, String> map = genMap(); + map.put(null, "x"); + Map<Integer, String> copy = Map.copyOf(map); + } + + @Test(expectedExceptions=NullPointerException.class) + public void copyOfRejectsNullValue() { + Map<Integer, String> map = genMap(); + map.put(-1, null); + Map<Integer, String> copy = Map.copyOf(map); + } + // Map.entry() tests @Test(expectedExceptions=NullPointerException.class) @@ -386,7 +471,7 @@ public class MapFactories { public void entryBasicTests() { Map.Entry<String,String> kvh1 = Map.entry("xyzzy", "plugh"); Map.Entry<String,String> kvh2 = Map.entry("foobar", "blurfl"); - Map.Entry<String,String> sie = new AbstractMap.SimpleImmutableEntry("xyzzy", "plugh"); + Map.Entry<String,String> sie = new AbstractMap.SimpleImmutableEntry<>("xyzzy", "plugh"); assertTrue(kvh1.equals(sie)); assertTrue(sie.equals(kvh1)); @@ -404,5 +489,4 @@ public class MapFactories { Map<Number,Number> map = Map.ofEntries(e1, e2); assertEquals(map.size(), 2); } - } From 4fbcc568d3f8386c96369fd5811a79e4592aba4d Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Mon, 4 Dec 2017 10:04:51 -0800 Subject: [PATCH 044/165] 8184683: Add @since and default methods of Compiler Tree API methods Reviewed-by: darcy, jjg --- .../sun/source/doctree/DocTreeVisitor.java | 40 +++++++++++++++++-- .../sun/source/util/SimpleDocTreeVisitor.java | 8 ++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java index 29ccba7474d..b11ea0de1ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java @@ -145,11 +145,19 @@ public interface DocTreeVisitor<R,P> { /** * Visits a HiddenTree node. + * + * @implSpec Visits a {@code HiddenTree} node + * by calling {@code visitOther(node, p)}. + * * @param node the node being visited * @param p a parameter value * @return a result value + * + * @since 9 */ - R visitHidden(HiddenTree node, P p); + default R visitHidden(HiddenTree node, P p) { + return visitOther(node, p); + } /** * Visits an IdentifierTree node. @@ -161,11 +169,19 @@ public interface DocTreeVisitor<R,P> { /** * Visits an IndexTree node. + * + * @implSpec Visits an {@code IndexTree} node + * by calling {@code visitOther(node, p)}. + * * @param node the node being visited * @param p a parameter value * @return a result value + * + * @since 9 */ - R visitIndex(IndexTree node, P p); + default R visitIndex(IndexTree node, P p) { + return visitOther(node, p); + } /** * Visits an InheritDocTree node. @@ -201,11 +217,19 @@ public interface DocTreeVisitor<R,P> { /** * Visits a ProvidesTree node. + * + * @implSpec Visits a {@code ProvidesTree} node + * by calling {@code visitOther(node, p)}. + * * @param node the node being visited * @param p a parameter value * @return a result value + * + * @since 9 */ - R visitProvides(ProvidesTree node, P p); + default R visitProvides(ProvidesTree node, P p) { + return visitOther(node, p); + } /** * Visits a ReferenceTree node. @@ -320,11 +344,19 @@ public interface DocTreeVisitor<R,P> { /** * Visits a UsesTree node. + * + * @implSpec Visits a {@code UsesTree} node + * by calling {@code visitOther(node, p)}. + * * @param node the node being visited * @param p a parameter value * @return a result value + * + * @since 9 */ - R visitUses(UsesTree node, P p); + default R visitUses(UsesTree node, P p) { + return visitOther(node, p); + } /** * Visits a ValueTree node. diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java index d180accc329..07abab5de04 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java @@ -220,6 +220,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> { * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * + * @since 9 */ @Override public R visitHidden(HiddenTree node, P p) { @@ -244,6 +246,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> { * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * + * @since 9 */ @Override public R visitIndex(IndexTree node, P p) { @@ -304,6 +308,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> { * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * + * @since 9 */ @Override public R visitProvides(ProvidesTree node, P p) { @@ -473,6 +479,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> { * @param node {@inheritDoc} * @param p {@inheritDoc} * @return the result of {@code defaultAction} + * + * @since 9 */ @Override public R visitUses(UsesTree node, P p) { From 50d6957528b5de6a5f70ff6483f0313d5824f489 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Mon, 4 Dec 2017 22:31:13 +0100 Subject: [PATCH 045/165] 8193014: Add "special" tests to run-test to cover odd cases Reviewed-by: erikj --- make/RunTests.gmk | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 2475106fbc5..81cb8ff2ba8 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -263,6 +263,15 @@ define ParseJtregTestSelection ) endef +# Helper function to determine if a test specification is a special test +# +# It is a special test if it is "special:" followed by a test name. +define ParseSpecialTestSelection + $(if $(filter special:%, $1), \ + $1 \ + ) +endef + ifeq ($(TEST), ) $(info No test selection given in TEST!) $(info Please use e.g. 'run-test TEST=tier1' or 'run-test-tier1') @@ -281,6 +290,9 @@ $(foreach test, $(TEST), \ $(if $(strip $(PARSED_TESTS)), , \ $(eval PARSED_TESTS += $(call ParseJtregTestSelection, $(test))) \ ) \ + $(if $(strip $(PARSED_TESTS)), , \ + $(eval PARSED_TESTS += $(call ParseSpecialTestSelection, $(test))) \ + ) \ $(if $(strip $(PARSED_TESTS)), , \ $(eval UNKNOWN_TEST := $(test)) \ ) \ @@ -565,6 +577,69 @@ define SetupRunJtregTestBody TARGETS += $1 endef +################################################################################ + +### Rules for special tests + +SetupRunSpecialTest = $(NamedParamsMacroTemplate) +define SetupRunSpecialTestBody + $1_TEST_RESULTS_DIR := $$(TEST_RESULTS_DIR)/$1 + $1_TEST_SUPPORT_DIR := $$(TEST_SUPPORT_DIR)/$1 + $1_EXITCODE := $$($1_TEST_RESULTS_DIR)/exitcode.txt + + $1_FULL_TEST_NAME := $$(strip $$(patsubst special:%, %, $$($1_TEST))) + ifneq ($$(findstring :, $$($1_FULL_TEST_NAME)), ) + $1_TEST_NAME := $$(firstword $$(subst :, ,$$($1_FULL_TEST_NAME))) + $1_TEST_ARGS := $$(strip $$(patsubst special:$$($1_TEST_NAME):%, %, $$($1_TEST))) + else + $1_TEST_NAME := $$($1_FULL_TEST_NAME) + $1_TEST_ARGS := + endif + + ifeq ($$($1_TEST_NAME), hotspot-internal) + $1_TEST_COMMAND_LINE := \ + $$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \ + -XX:+ShowMessageBoxOnError -version + else ifeq ($$($1_TEST_NAME), failure-handler) + $1_TEST_COMMAND_LINE := \ + ($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \ + BuildFailureHandler.gmk test) + else ifeq ($$($1_TEST_NAME), make) + $1_TEST_COMMAND_LINE := \ + ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \ + TestMake.gmk $$($1_TEST_ARGS)) + else + $$(error Invalid special test specification: $$($1_TEST_NAME)) + endif + + run-test-$1: + $$(call LogWarn) + $$(call LogWarn, Running test '$$($1_TEST)') + $$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR)) + $$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/test-execution, \ + $$($1_TEST_COMMAND_LINE) \ + > >($(TEE) $$($1_TEST_RESULTS_DIR)/test-output.txt) \ + && $$(ECHO) $$$$? > $$($1_EXITCODE) \ + || $$(ECHO) $$$$? > $$($1_EXITCODE) \ + ) + + $1_RESULT_FILE := $$($1_TEST_RESULTS_DIR)/gtest.txt + + # We can not parse the various "special" tests. + parse-test-$1: run-test-$1 + $$(call LogWarn, Finished running test '$$($1_TEST)') + $$(call LogWarn, Test report is stored in $$(strip \ + $$(subst $$(TOPDIR)/, , $$($1_TEST_RESULTS_DIR)))) + $$(call LogWarn, Warning: Special test results are not properly parsed!) + $$(eval $1_PASSED := 0) + $$(eval $1_FAILED := 0) + $$(eval $1_ERROR := 0) + $$(eval $1_TOTAL := 0) + + $1: run-test-$1 parse-test-$1 + + TARGETS += $1 +endef ################################################################################ # Setup and execute make rules for all selected tests @@ -577,6 +652,9 @@ UseGtestTestHandler = \ UseJtregTestHandler = \ $(if $(filter jtreg:%, $1), true) +UseSpecialTestHandler = \ + $(if $(filter special:%, $1), true) + # Now process each test to run and setup a proper make rule $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ @@ -597,6 +675,11 @@ $(foreach test, $(TESTS_TO_RUN), \ TEST := $(test), \ )) \ ) \ + $(if $(call UseSpecialTestHandler, $(test)), \ + $(eval $(call SetupRunSpecialTest, $(TEST_ID), \ + TEST := $(test), \ + )) \ + ) \ ) # Sort also removes duplicates, so if there is any we'll get fewer words. From c4bab8bce7bb7405615b6530f33125ed1a3cfb1d Mon Sep 17 00:00:00 2001 From: Weijun Wang <weijun@openjdk.org> Date: Tue, 5 Dec 2017 17:19:48 +0800 Subject: [PATCH 046/165] 8190674: sun/security/tools/jarsigner/TimestampCheck.java failed with java.nio.file.NoSuchFileException: ts2.cert Reviewed-by: mullan --- .../sun/security/tools/jarsigner/TimestampCheck.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java index f0774029606..86d72f53b71 100644 --- a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java +++ b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java @@ -694,11 +694,15 @@ public class TimestampCheck { gencert("ts", "-ext eku:critical=ts"); - // Issue another cert for "ts" with a different EKU. - // Length should be the same. Try several times. - keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " + - "-ext eku:critical=1.3.6.1.5.5.7.3.9"); for (int i = 0; i < 5; i++) { + // Issue another cert for "ts" with a different EKU. + // Length might be different because serial number is + // random. Try several times until a cert with the same + // length is generated so we can substitute ts.cert + // embedded in the PKCS7 block with ts2.cert. + // If cannot create one, related test will be ignored. + keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " + + "-ext eku:critical=1.3.6.1.5.5.7.3.9"); if (Files.size(Paths.get("ts.cert")) != Files.size(Paths.get("ts2.cert"))) { Files.delete(Paths.get("ts2.cert")); System.out.println("Warning: cannot create same length"); From 9da6eea9fedc384cb7b823a7db221aa5c4bec172 Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier <goetz@openjdk.org> Date: Tue, 21 Nov 2017 17:39:04 +0100 Subject: [PATCH 047/165] 8191678: [TESTBUG] Add keyword headful in java/awt and javax tests Also fix some NPE thrown if run headless. Reviewed-by: serb --- .../ComponentGetLocationOnScreenNPETest.java | 1 + .../Dialog/SiblingChildOrder/SiblingChildOrderTest.java | 4 +++- .../Focus/FocusTransitionTest/FocusTransitionTest.java | 4 +++- .../DefaultButtonModel/DefaultButtonModelCrashTest.java | 7 +++++-- .../TestMultiScreenGConfigNotify.java | 4 +++- test/jdk/javax/swing/JButton/TestGlyphBreak.java | 4 +++- .../javax/swing/JComboBox/8182031/ComboPopupTest.java | 7 +++++-- test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java | 9 +++++++-- test/jdk/javax/swing/JTextArea/TestTabSize.java | 8 ++++++-- .../javax/swing/dnd/8139050/NativeErrorsInTableDnD.java | 2 ++ test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java | 5 ++++- .../HidingSelection/HidingSelectionTest.java | 1 + 12 files changed, 43 insertions(+), 13 deletions(-) diff --git a/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java b/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java index a5131184794..6458b308bf1 100644 --- a/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java +++ b/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java @@ -25,6 +25,7 @@ * @test * @bug 8189204 * @summary Possible NPE in Component::getLocationOnScreen() + * @key headful * @run main ComponentGetLocationOnScreenNPETest */ diff --git a/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java b/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java index c336a0d9bc6..701d1e2deb3 100644 --- a/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java +++ b/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java @@ -21,9 +21,11 @@ * questions. */ -/* @test +/** + * @test * @bug 8190230 * @summary [macosx] Order of overlapping of modal dialogs is wrong + * @key headful * @run main SiblingChildOrderTest */ diff --git a/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java b/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java index 6610981c408..8352a2bf6fb 100644 --- a/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java +++ b/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java @@ -21,10 +21,12 @@ * questions. */ -/* @test +/** + * @test * @bug 8155197 * @summary Tests whether the value of mostRecentFocusOwner for a window is correct, if * another window is displayed during focus transition + * @key headful * @library ../../regtesthelpers * @build Util * @run main FocusTransitionTest diff --git a/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java b/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java index eb68e59650a..efcf335a54d 100644 --- a/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java +++ b/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java @@ -20,12 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8182577 * @summary Verifies if moving focus via custom ButtonModel causes crash + * @key headful * @run main DefaultButtonModelCrashTest */ + import java.awt.BorderLayout; import java.awt.Container; import java.awt.Point; @@ -61,7 +64,7 @@ public class DefaultButtonModelCrashTest { robot.keyPress(KeyEvent.VK_TAB); robot.keyRelease(KeyEvent.VK_TAB); } finally { - SwingUtilities.invokeAndWait(()->frame .dispose()); + if (frame != null) { SwingUtilities.invokeAndWait(()->frame.dispose()); } } } diff --git a/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java index adadd7201df..3d1742cfe6d 100644 --- a/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java +++ b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8178025 * @summary Verifies if graphicsConfiguration property notification is sent * when frame is moved from one screen to another in multiscreen * environment. + * @key headful * @run main TestMultiScreenGConfigNotify */ diff --git a/test/jdk/javax/swing/JButton/TestGlyphBreak.java b/test/jdk/javax/swing/JButton/TestGlyphBreak.java index a3bf249611d..bf87b88e56b 100644 --- a/test/jdk/javax/swing/JButton/TestGlyphBreak.java +++ b/test/jdk/javax/swing/JButton/TestGlyphBreak.java @@ -20,10 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8191428 * @summary Verifies if text view is not borken into multiple lines + * @key headful * @run main/othervm -Dsun.java2d.uiScale=1.2 TestGlyphBreak */ diff --git a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java index 3e1f9b80040..9321bd154be 100644 --- a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java +++ b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java @@ -20,12 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8182031 * @summary Verifies if ComboBox Popup opens and closes immediately + * @key headful * @run main ComboPopupTest */ + import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; @@ -80,7 +83,7 @@ public class ComboPopupTest { throw new RuntimeException("combobox popup is not visible"); } } finally { - SwingUtilities.invokeAndWait(()->frame.dispose()); + if (frame != null) { SwingUtilities.invokeAndWait(()->frame.dispose()); } } } diff --git a/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java b/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java index 83477848a34..dc42877e96b 100644 --- a/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java +++ b/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java @@ -20,13 +20,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8178430 * @summary JMenu in GridBagLayout flickers when label text shows "..." and * is updated + * @key headful * @run main LabelDotTest */ + import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -112,7 +115,9 @@ public class LabelDotTest SwingUtilities.invokeAndWait(() -> createUI()); runTest(50); } finally { - SwingUtilities.invokeAndWait(() -> frame.dispose()); + if (frame != null) { + SwingUtilities.invokeAndWait(() -> frame.dispose()); + } if (isException) throw new RuntimeException("Size of Menu bar is not correct."); } diff --git a/test/jdk/javax/swing/JTextArea/TestTabSize.java b/test/jdk/javax/swing/JTextArea/TestTabSize.java index e69fe318eb0..0893680e92e 100644 --- a/test/jdk/javax/swing/JTextArea/TestTabSize.java +++ b/test/jdk/javax/swing/JTextArea/TestTabSize.java @@ -20,10 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8187957 * @summary Verifies Tab Size works correctly in JTextArea + * @key headful * @run main TestTabSize */ @@ -96,7 +98,9 @@ public class TestTabSize { } catch (BadLocationException ex) { excpnthrown = true; } finally { - f.dispose(); + if (f != null) { + f.dispose(); + } } }); if (excpnthrown) { diff --git a/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java b/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java index fe84ed4c976..5e0230a29e6 100644 --- a/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java +++ b/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java @@ -40,10 +40,12 @@ import static javax.swing.UIManager.getInstalledLookAndFeels; /** * @test * @bug 8139050 8153871 + * @key headful * @library ../../../../lib/testlibrary * @build ExtendedRobot * @run main/othervm/timeout=360 -Xcheck:jni NativeErrorsInTableDnD */ + public final class NativeErrorsInTableDnD { private static JFrame frame; diff --git a/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java b/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java index 17a9d10a9d2..2cf8f0cfa24 100644 --- a/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java +++ b/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java @@ -20,13 +20,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -/* + +/** * @test * @bug 8043315 * @summary Verifies if setting Nimbus.Overrides property affects * keymap installation + * @key headful * @run main TestNimbusOverride */ + import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; diff --git a/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java b/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java index a422eed798b..6b2331f390d 100644 --- a/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java +++ b/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java @@ -30,6 +30,7 @@ import java.awt.image.BufferedImage; * @test * @bug 8188081 * @summary Text selection does not clear after focus is lost + * @key headful * @run main HidingSelectionTest */ From 6351f58092e836473cabe241a150da2847300d3b Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Tue, 5 Dec 2017 14:10:11 +0100 Subject: [PATCH 048/165] 8193055: ADD_JVM_ARG_IF_OK always fails Reviewed-by: dholmes --- make/autoconf/boot-jdk.m4 | 3 --- make/autoconf/generated-configure.sh | 19 +------------------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4 index 0c0abd61fa6..161b0f18f2a 100644 --- a/make/autoconf/boot-jdk.m4 +++ b/make/autoconf/boot-jdk.m4 @@ -353,9 +353,6 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS], AC_MSG_CHECKING([flags for boot jdk java command] ) - # Disable special log output when a debug build is used as Boot JDK... - ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA]) - # Force en-US environment ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA]) diff --git a/make/autoconf/generated-configure.sh b/make/autoconf/generated-configure.sh index d3cb5f606c6..e856469fd0b 100644 --- a/make/autoconf/generated-configure.sh +++ b/make/autoconf/generated-configure.sh @@ -5159,7 +5159,7 @@ VS_SDK_PLATFORM_NAME_2013= #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1512410983 +DATE_WHEN_GENERATED=1512479382 ############################################################################### # @@ -67379,23 +67379,6 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command " >&5 $as_echo_n "checking flags for boot jdk java command ... " >&6; } - # Disable special log output when a debug build is used as Boot JDK... - - $ECHO "Check if jvm arg is ok: -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" >&5 - $ECHO "Command: $JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version" >&5 - OUTPUT=`$JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs="$boot_jdk_jvmargs -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - # Force en-US environment $ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5 From 7759531e5c1ad5918cc9948d9b3ae115cbc670ba Mon Sep 17 00:00:00 2001 From: Claes Redestad <redestad@openjdk.org> Date: Tue, 5 Dec 2017 14:25:16 +0100 Subject: [PATCH 049/165] 8176188: jdk/internal/misc/JavaLangAccess/NewUnsafeString.java failing since 9-b93 Reviewed-by: psandoz, sherman --- .../share/classes/java/lang/String.java | 13 --- .../share/classes/java/lang/System.java | 3 - .../share/classes/java/util/StringJoiner.java | 9 +- .../jdk/internal/misc/JavaLangAccess.java | 10 --- src/java.sql/share/classes/java/sql/Date.java | 6 +- src/java.sql/share/classes/java/sql/Time.java | 6 +- .../share/classes/java/sql/Timestamp.java | 6 +- test/jdk/ProblemList.txt | 2 - .../misc/JavaLangAccess/NewUnsafeString.java | 88 ------------------- 9 files changed, 5 insertions(+), 138 deletions(-) delete mode 100644 test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index db55267ee83..e3da498617c 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -645,19 +645,6 @@ public final class String this(builder, null); } - /* - * Package private constructor which shares value array for speed. - * this constructor is always expected to be called with share==true. - * a separate constructor is needed because we already have a public - * String(char[]) constructor that makes a copy of the given char[]. - */ - // TBD: this is kept for package internal use (Thread/System), - // should be removed if they all have a byte[] version - String(char[] val, boolean share) { - // assert share : "unshared not supported"; - this(val, 0, val.length, null); - } - /** * Returns the length of this string. * The length is equal to the number of <a href="Character.html#unicode">Unicode diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 0232642b42c..6271f87a6f0 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2109,9 +2109,6 @@ public final class System { public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) { Shutdown.add(slot, registerShutdownInProgress, hook); } - public String newStringUnsafe(char[] chars) { - return new String(chars, true); - } public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) { return new Thread(target, acc); } diff --git a/src/java.base/share/classes/java/util/StringJoiner.java b/src/java.base/share/classes/java/util/StringJoiner.java index 3dca2df4d46..c72d5a213bd 100644 --- a/src/java.base/share/classes/java/util/StringJoiner.java +++ b/src/java.base/share/classes/java/util/StringJoiner.java @@ -24,9 +24,6 @@ */ package java.util; -import jdk.internal.misc.JavaLangAccess; -import jdk.internal.misc.SharedSecrets; - /** * {@code StringJoiner} is used to construct a sequence of characters separated * by a delimiter and optionally starting with a supplied prefix @@ -86,8 +83,6 @@ public final class StringJoiner { */ private String emptyValue; - private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - /** * Constructs a {@code StringJoiner} with no characters in it, with no * {@code prefix} or {@code suffix}, and a copy of the supplied @@ -189,7 +184,7 @@ public final class StringJoiner { } } k += getChars(suffix, chars, k); - return jla.newStringUnsafe(chars); + return new String(chars); } /** @@ -252,7 +247,7 @@ public final class StringJoiner { elts[i] = null; } while (++i < size); size = 1; - elts[0] = jla.newStringUnsafe(chars); + elts[0] = new String(chars); } } diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java index 90ec8f40761..7561b2109ad 100644 --- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java @@ -123,16 +123,6 @@ public interface JavaLangAccess { */ void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook); - /** - * Returns a new string backed by the provided character array. The - * character array is not copied and must never be modified after the - * String is created, in order to fulfill String's contract. - * - * @param chars the character array to back the string - * @return a newly created string whose content is the character array - */ - String newStringUnsafe(char[] chars); - /** * Returns a new Thread with the given Runnable and an * inherited AccessControlContext. diff --git a/src/java.sql/share/classes/java/sql/Date.java b/src/java.sql/share/classes/java/sql/Date.java index a6d8d8f4016..f8da3e7f94b 100644 --- a/src/java.sql/share/classes/java/sql/Date.java +++ b/src/java.sql/share/classes/java/sql/Date.java @@ -27,8 +27,6 @@ package java.sql; import java.time.Instant; import java.time.LocalDate; -import jdk.internal.misc.SharedSecrets; -import jdk.internal.misc.JavaLangAccess; /** * <P>A thin wrapper around a millisecond value that allows @@ -46,8 +44,6 @@ import jdk.internal.misc.JavaLangAccess; */ public class Date extends java.util.Date { - private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - /** * Constructs a <code>Date</code> object initialized with the given * year, month, and day. @@ -168,7 +164,7 @@ public class Date extends java.util.Date { buf[7] = '-'; Date.formatDecimalInt(day, buf, 8, 2); - return jla.newStringUnsafe(buf); + return new String(buf); } /** diff --git a/src/java.sql/share/classes/java/sql/Time.java b/src/java.sql/share/classes/java/sql/Time.java index 3d1f237933c..281d031c7c1 100644 --- a/src/java.sql/share/classes/java/sql/Time.java +++ b/src/java.sql/share/classes/java/sql/Time.java @@ -27,8 +27,6 @@ package java.sql; import java.time.Instant; import java.time.LocalTime; -import jdk.internal.misc.SharedSecrets; -import jdk.internal.misc.JavaLangAccess; /** * <P>A thin wrapper around the <code>java.util.Date</code> class that allows the JDBC @@ -43,8 +41,6 @@ import jdk.internal.misc.JavaLangAccess; */ public class Time extends java.util.Date { - private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - /** * Constructs a <code>Time</code> object initialized with the * given values for the hour, minute, and second. @@ -134,7 +130,7 @@ public class Time extends java.util.Date { buf[5] = ':'; Date.formatDecimalInt(second, buf, 6, 2); - return jla.newStringUnsafe(buf); + return new String(buf); } // Override all the date operations inherited from java.util.Date; diff --git a/src/java.sql/share/classes/java/sql/Timestamp.java b/src/java.sql/share/classes/java/sql/Timestamp.java index 968d6e76b9f..4b4c334dd45 100644 --- a/src/java.sql/share/classes/java/sql/Timestamp.java +++ b/src/java.sql/share/classes/java/sql/Timestamp.java @@ -27,8 +27,6 @@ package java.sql; import java.time.Instant; import java.time.LocalDateTime; -import jdk.internal.misc.SharedSecrets; -import jdk.internal.misc.JavaLangAccess; /** * <P>A thin wrapper around {@code java.util.Date} that allows @@ -74,8 +72,6 @@ import jdk.internal.misc.JavaLangAccess; */ public class Timestamp extends java.util.Date { - private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - /** * Constructs a {@code Timestamp} object initialized * with the given values. @@ -313,7 +309,7 @@ public class Timestamp extends java.util.Date { buf[yearSize + 15] = '.'; Date.formatDecimalInt(tmpNanos, buf, yearSize + 16, 9 - trailingZeros); - return jla.newStringUnsafe(buf); + return new String(buf); } /** diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 07fb5b5d51f..7071399994e 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -124,8 +124,6 @@ java/beans/Introspector/8132566/OverrideUserDefPropertyInfoTest.java 8132565 gen java/lang/StringCoding/CheckEncodings.sh 7008363 generic-all -jdk/internal/misc/JavaLangAccess/NewUnsafeString.java 8176188 generic-all - java/lang/String/nativeEncoding/StringPlatformChars.java 8182569 windows-all,solaris-all ############################################################################ diff --git a/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java b/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java deleted file mode 100644 index 302fb185809..00000000000 --- a/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2012, 2017, 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. - */ - -import java.util.Objects; -import java.util.Comparator; -import jdk.internal.misc.JavaLangAccess; -import jdk.internal.misc.SharedSecrets; - -/* - * @test - * @bug 8013528 - * @summary Test JavaLangAccess.newUnsafeString - * @modules java.base/jdk.internal.misc - * @compile -XDignore.symbol.file NewUnsafeString.java - * @run main NewUnsafeString - */ -public class NewUnsafeString { - - static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); - - public static void testNewUnsafeString() { - String benchmark = "exemplar"; - String constructorCopy = new String(benchmark); - char[] jlaChars = benchmark.toCharArray(); - String jlaCopy = jla.newStringUnsafe(jlaChars); - - if (benchmark == constructorCopy) { - throw new Error("should be different instances"); - } - if (!benchmark.equals(constructorCopy)) { - throw new Error("Copy not equal"); - } - if (0 != Objects.compare(benchmark, constructorCopy, Comparator.naturalOrder())) { - throw new Error("Copy not equal"); - } - - if (benchmark == jlaCopy) { - throw new Error("should be different instances"); - } - if (!benchmark.equals(jlaCopy)) { - throw new Error("Copy not equal"); - } - if (0 != Objects.compare(benchmark, jlaCopy, Comparator.naturalOrder())) { - throw new Error("Copy not equal"); - } - - if (constructorCopy == jlaCopy) { - throw new Error("should be different instances"); - } - if (!constructorCopy.equals(jlaCopy)) { - throw new Error("Copy not equal"); - } - if (0 != Objects.compare(constructorCopy, jlaCopy, Comparator.naturalOrder())) { - throw new Error("Copy not equal"); - } - - // The following is extremely "evil". Never ever do this in non-test code. - jlaChars[0] = 'X'; - if (!"Xxemplar".equals(jlaCopy)) { - throw new Error("jla.newStringUnsafe did not use provided string"); - } - - } - - public static void main(String[] args) { - testNewUnsafeString(); - } -} From 1a819fcd7a3ef13302affb82fc3deb066ff865c8 Mon Sep 17 00:00:00 2001 From: Brent Christian <bchristi@openjdk.org> Date: Tue, 5 Dec 2017 09:44:32 -0800 Subject: [PATCH 050/165] 8187222: ClassLoader.getSystemClassLoader not clear if recursive initialization leads to ISE or unspecified error Reviewed-by: alanb, mchung --- .../share/classes/java/lang/ClassLoader.java | 19 +++++-- .../ClassLoader/RecursiveSystemLoader.java | 56 +++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 74c46417631..c36b54d31f0 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.io.File; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.security.AccessController; import java.security.AccessControlContext; @@ -1867,7 +1868,7 @@ public abstract class ClassLoader { * to be the system class loader. During construction, the class loader * should take great care to avoid calling {@code getSystemClassLoader()}. * If circular initialization of the system class loader is detected then - * an unspecified error or exception is thrown. + * an {@code IllegalStateException} is thrown. * * @implNote The system property to override the system class loader is not * examined until the VM is almost fully initialized. Code that executes @@ -1918,8 +1919,8 @@ public abstract class ClassLoader { // the system class loader is the built-in app class loader during startup return getBuiltinAppClassLoader(); case 3: - String msg = "getSystemClassLoader should only be called after VM booted"; - throw new InternalError(msg); + String msg = "getSystemClassLoader cannot be called during the system class loader instantiation"; + throw new IllegalStateException(msg); case 4: // system fully initialized assert VM.isBooted() && scl != null; @@ -1969,7 +1970,17 @@ public abstract class ClassLoader { .getDeclaredConstructor(ClassLoader.class); scl = (ClassLoader) ctor.newInstance(builtinLoader); } catch (Exception e) { - throw new Error(e); + Throwable cause = e; + if (e instanceof InvocationTargetException) { + cause = e.getCause(); + if (cause instanceof Error) { + throw (Error) cause; + } + } + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } + throw new Error(cause.getMessage(), cause); } } else { scl = builtinLoader; diff --git a/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java b/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java new file mode 100644 index 00000000000..16ca74c8a51 --- /dev/null +++ b/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, 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 8187222 + * @run main/othervm -Djava.system.class.loader=RecursiveSystemLoader RecursiveSystemLoader + * @summary Test for IllegalStateException if a custom system loader recursively calls getSystemClassLoader() + */ +public class RecursiveSystemLoader extends ClassLoader { + public static void main(String[] args) { + ClassLoader sys = ClassLoader.getSystemClassLoader(); + if (!(sys instanceof RecursiveSystemLoader)) { + throw new RuntimeException("Unexpected system classloader: " + sys); + } + } + public RecursiveSystemLoader(ClassLoader classLoader) { + super("RecursiveSystemLoader", classLoader); + + // Calling ClassLoader.getSystemClassLoader() before the VM is booted + // should throw an IllegalStateException. + try { + ClassLoader.getSystemClassLoader(); + } catch(IllegalStateException ise) { + System.err.println("Caught expected exception:"); + ise.printStackTrace(); + return; + } + throw new RuntimeException("Expected IllegalStateException was not thrown."); + } + + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } +} From c6aa80664337e053cba9e773c3fb624d9f58aa76 Mon Sep 17 00:00:00 2001 From: Claes Redestad <redestad@openjdk.org> Date: Tue, 5 Dec 2017 22:26:17 +0100 Subject: [PATCH 051/165] 8193064: JarFile::getEntry0 method reference use cause for startup regression Reviewed-by: sherman, mchung --- .../share/classes/java/util/jar/JarFile.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index 09060f9d209..bea26632957 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -43,17 +43,12 @@ import java.security.CodeSource; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Enumeration; -import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.stream.Collector; +import java.util.function.Function; import java.util.stream.Stream; -import java.util.stream.StreamSupport; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; @@ -566,7 +561,14 @@ class JarFile extends ZipFile { * given entry name or {@code null} if not found. */ private JarFileEntry getEntry0(String name) { - return (JarFileEntry)JUZFA.getEntry(this, name, JarFileEntry::new); + // Not using a lambda/method reference here to optimize startup time + Function<String, JarEntry> newJarFileEntryFn = new Function<>() { + @Override + public JarEntry apply(String name) { + return new JarFileEntry(name); + } + }; + return (JarFileEntry)JUZFA.getEntry(this, name, newJarFileEntryFn); } private String getBasename(String name) { From 18129b77761739a794262ffd33d6425425ca190b Mon Sep 17 00:00:00 2001 From: Erik Joelsson <erikj@openjdk.org> Date: Tue, 5 Dec 2017 23:11:27 +0100 Subject: [PATCH 052/165] 8191439: Race in building jdk.rmic.interim Reviewed-by: mchung, tbell, ihse --- make/CompileInterimLangtools.gmk | 4 ++-- make/CompileInterimRmic.gmk | 4 ++-- make/autoconf/spec.gmk.in | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/make/CompileInterimLangtools.gmk b/make/CompileInterimLangtools.gmk index afdb14a936a..b8ff3af48ad 100644 --- a/make/CompileInterimLangtools.gmk +++ b/make/CompileInterimLangtools.gmk @@ -69,8 +69,8 @@ define SetupInterimModule Standard.java, \ EXTRA_FILES := $(BUILDTOOLS_OUTPUTDIR)/gensrc/$1.interim/module-info.java, \ COPY := .gif .png .xml .css .js javax.tools.JavaCompilerTool, \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_modules/$1.interim, \ - ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \ + BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules/$1.interim, \ + ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \ $$(INTERIM_LANGTOOLS_ADD_EXPORTS) \ -Xlint:-module, \ )) diff --git a/make/CompileInterimRmic.gmk b/make/CompileInterimRmic.gmk index acc02c34bc8..6127e930c09 100644 --- a/make/CompileInterimRmic.gmk +++ b/make/CompileInterimRmic.gmk @@ -65,10 +65,10 @@ $(eval $(call SetupJavaCompilation, BUILD_jdk.rmic.interim, \ EXCLUDE_FILES := $(TOPDIR)/src/jdk.rmic/share/classes/module-info.java, \ EXTRA_FILES := $(BUILDTOOLS_OUTPUTDIR)/gensrc/jdk.rmic.interim/module-info.java, \ INCLUDES := $(RMIC_PKGS), \ - BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_modules/jdk.rmic.interim, \ + BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules/jdk.rmic.interim, \ COPY := .properties, \ ADD_JAVAC_FLAGS := \ - --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \ + --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules \ --add-modules java.corba \ --add-exports java.corba/com.sun.corba.se.impl.util=jdk.rmic.interim \ $(INTERIM_RMIC_ADD_EXPORTS), \ diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index b8449f78048..84ff78339a5 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -577,7 +577,7 @@ INTERIM_LANGTOOLS_MODULES_COMMA := $(strip $(subst $(SPACE),$(COMMA),$(strip \ INTERIM_LANGTOOLS_ARGS := \ --limit-modules java.base,jdk.zipfs,$(INTERIM_LANGTOOLS_MODULES_COMMA) \ --add-modules $(INTERIM_LANGTOOLS_MODULES_COMMA) \ - --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \ + --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \ $(INTERIM_LANGTOOLS_ADD_EXPORTS) \ # JAVAC_MAIN_CLASS = -m jdk.compiler.interim/com.sun.tools.javac.Main @@ -588,8 +588,10 @@ INTERIM_RMIC_MODULES := $(addsuffix .interim, $(INTERIM_RMIC_BASE_MODULES)) INTERIM_RMIC_ADD_EXPORTS := \ --add-exports java.corba/com.sun.corba.se.impl.util=jdk.rmic.interim \ # -INTERIM_RMIC_ARGS := --limit-modules java.base,jdk.compiler,jdk.javadoc,java.corba \ - --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \ +# Use = to delay expansion of PathList since it's not available in this file. +INTERIM_RMIC_ARGS = --limit-modules java.base,jdk.compiler,jdk.javadoc,java.corba \ + --module-path $(call PathList, $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules \ + $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules) \ $(INTERIM_RMIC_ADD_EXPORTS) \ # From 9ddcde90e0d259d820a67f299faf927fc297c906 Mon Sep 17 00:00:00 2001 From: Erik Joelsson <erikj@openjdk.org> Date: Tue, 5 Dec 2017 23:14:35 +0100 Subject: [PATCH 053/165] 8192771: Boot JDK jar tool used to construct the modular JAR for java.jnlp Reviewed-by: ihse, psandoz, mchung --- make/CreateJmods.gmk | 5 +++-- make/autoconf/spec.gmk.in | 1 + make/common/JarArchive.gmk | 12 +++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk index d1e05718dcf..0b530bb8b6b 100644 --- a/make/CreateJmods.gmk +++ b/make/CreateJmods.gmk @@ -1,5 +1,4 @@ - -# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2017, 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 @@ -33,6 +32,8 @@ ifeq ($(MODULE), ) $(error MODULE must be set when calling CreateJmods.gmk) endif +$(eval $(call IncludeCustomExtension, CreateJmods.gmk)) + ################################################################################ JMODS_DIR := $(IMAGES_OUTPUTDIR)/jmods diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 84ff78339a5..dda3fbebaca 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -565,6 +565,7 @@ JAVAC_FLAGS?=@JAVAC_FLAGS@ BUILD_JAVA_FLAGS := @BOOTCYCLE_JVM_ARGS_BIG@ BUILD_JAVA=@FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS) +BUILD_JAR=@FIXPATH@ $(BUILD_JDK)/bin/jar # Interim langtools and rmic modules and arguments INTERIM_LANGTOOLS_BASE_MODULES := java.compiler jdk.compiler jdk.javadoc diff --git a/make/common/JarArchive.gmk b/make/common/JarArchive.gmk index 1e1c017ab61..b6487913dd4 100644 --- a/make/common/JarArchive.gmk +++ b/make/common/JarArchive.gmk @@ -56,6 +56,7 @@ FALSE_FIND_PATTERN:=-name FILE_NAME_THAT_DOESNT_EXIST # added to the archive. # EXTRA_MANIFEST_ATTR:=Extra attribute to add to manifest. # CHECK_COMPRESS_JAR Check the COMPRESS_JAR variable +# JAR_CMD:=Optionally override the jar command to use when creating the archive. SetupJarArchive = $(NamedParamsMacroTemplate) define SetupJarArchiveBody @@ -65,6 +66,7 @@ define SetupJarArchiveBody $1_DELETESS_FILE:=$$(dir $$($1_JAR))_the.$$($1_JARNAME)_deletess $1_DELETES_FILE:=$$(dir $$($1_JAR))_the.$$($1_JARNAME)_deletes $1_BIN:=$$(dir $$($1_JAR)) + $$(call SetIfEmpty, $1_JAR_CMD, $$(JAR)) ifeq (,$$($1_SUFFIXES)) # No suffix was set, default to classes. @@ -109,7 +111,7 @@ define SetupJarArchiveBody # Check if this jar needs to have its index generated. ifneq (,$$($1_JARINDEX)) - $1_JARINDEX = (cd $$(dir $$@) && $(JAR) -i $$(notdir $$@)) + $1_JARINDEX = (cd $$(dir $$@) && $$($1_JAR_CMD) -i $$(notdir $$@)) else $1_JARINDEX = true endif @@ -189,7 +191,7 @@ define SetupJarArchiveBody $1_UPDATE_CONTENTS=\ if [ "`$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'`" -gt "0" ]; then \ $(ECHO) " updating" `$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'` files && \ - $(JAR) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents; \ + $$($1_JAR_CMD) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents; \ fi $$(NEWLINE) # The s-variants of the above macros are used when the jar is created from scratch. # NOTICE: please leave the parentheses space separated otherwise the AIX build will break! @@ -208,7 +210,7 @@ define SetupJarArchiveBody | $(SED) 's|$$(src)/|-C $$(src) |g' >> \ $$($1_BIN)/_the.$$($1_JARNAME)_contents) $$(NEWLINE) ) endif - $1_SUPDATE_CONTENTS=$(JAR) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE) + $1_SUPDATE_CONTENTS=$$($1_JAR_CMD) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE) # Use a slightly shorter name for logging, but with enough path to identify this jar. $1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_JAR)) @@ -226,7 +228,7 @@ define SetupJarArchiveBody endif # Include all variables of significance in the vardeps file - $1_VARDEPS := $(JAR) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) \ + $1_VARDEPS := $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) \ $$($1_JARMAIN) $$($1_EXTRA_MANIFEST_ATTR) $$($1_ORIG_DEPS) $$($1_SRCS) \ $$($1_INCLUDES) $$($1_EXCLUDES) $$($1_EXCLUDE_FILES) $$($1_EXTRA_FILES) $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$(dir $$($1_JAR))_the.$$($1_JARNAME).vardeps) @@ -250,7 +252,7 @@ define SetupJarArchiveBody $$(if $$($1_EXTRA_MANIFEST_ATTR), \ $(PRINTF) "$$($1_EXTRA_MANIFEST_ATTR)\n" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \ $(ECHO) Creating $$($1_NAME) $$(NEWLINE) \ - $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ + $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \ $$($1_SCAPTURE_CONTENTS) \ $$($1_SCAPTURE_METAINF) \ $$($1_SUPDATE_CONTENTS) \ From ed7a7fb89f346ec78e5c42fcc419960fd63cd490 Mon Sep 17 00:00:00 2001 From: Paul Sandoz <psandoz@openjdk.org> Date: Tue, 5 Dec 2017 15:31:50 -0800 Subject: [PATCH 054/165] 8015667: Stream.toArray(IntFunction) ArrayStoreException should refer to component type of array Reviewed-by: smarks --- .../share/classes/java/util/stream/Stream.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/util/stream/Stream.java b/src/java.base/share/classes/java/util/stream/Stream.java index 01b6ac5a43a..f313fc6aeff 100644 --- a/src/java.base/share/classes/java/util/stream/Stream.java +++ b/src/java.base/share/classes/java/util/stream/Stream.java @@ -671,7 +671,8 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> { * <p>This is a <a href="package-summary.html#StreamOps">terminal * operation</a>. * - * @return an array containing the elements of this stream + * @return an array, whose {@linkplain Class#getComponentType runtime component + * type} is {@code Object}, containing the elements of this stream */ Object[] toArray(); @@ -694,13 +695,13 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> { * .toArray(Person[]::new); * }</pre> * - * @param <A> the element type of the resulting array + * @param <A> the component type of the resulting array * @param generator a function which produces a new array of the desired * type and the provided length * @return an array containing the elements in this stream - * @throws ArrayStoreException if the runtime type of the array returned - * from the array generator is not a supertype of the runtime type of every - * element in this stream + * @throws ArrayStoreException if the runtime type of any element of this + * stream is not assignable to the {@linkplain Class#getComponentType + * runtime component type} of the generated array */ <A> A[] toArray(IntFunction<A[]> generator); From 448f3c1fcdb188e1b272654bacf4678731d45766 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan <sundar@openjdk.org> Date: Wed, 6 Dec 2017 08:04:35 +0530 Subject: [PATCH 055/165] 8185130: jlink should throw error if target image and current JDK versions don't match Reviewed-by: redestad, alanb, mchung --- .../jdk/tools/jlink/internal/JlinkTask.java | 11 +++-- .../plugins/GenerateJLIClassesPlugin.java | 44 ------------------- .../tools/jlink/resources/jlink.properties | 2 + .../tools/jlink/resources/plugins.properties | 11 +---- .../JLinkMRJavaBaseVersionTest.java | 23 ++++++---- 5 files changed, 25 insertions(+), 66 deletions(-) diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 1efcffa110f..7db0934a54a 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -430,7 +430,7 @@ public class JlinkTask { Set<String> roots) { if (Objects.requireNonNull(paths).isEmpty()) { - throw new IllegalArgumentException("Empty module path"); + throw new IllegalArgumentException(taskHelper.getMessage("err.empty.module.path")); } Path[] entries = paths.toArray(new Path[0]); @@ -447,8 +447,13 @@ public class JlinkTask { // java.base version is different than the current runtime version version = Runtime.Version.parse(v.toString()); - if (Runtime.version().major() != version.major()) { - finder = ModulePath.of(version, true, entries); + if (Runtime.version().major() != version.major() || + Runtime.version().minor() != version.minor()) { + // jlink version and java.base version do not match. + // We do not (yet) support this mode. + throw new IllegalArgumentException(taskHelper.getMessage("err.jlink.version.mismatch", + Runtime.version().major(), Runtime.version().minor(), + version.major(), version.minor())); } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java index a5c4597efef..edcbb6fdf61 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java @@ -54,11 +54,8 @@ import jdk.tools.jlink.plugin.Plugin; public final class GenerateJLIClassesPlugin implements Plugin { private static final String NAME = "generate-jli-classes"; - private static final String IGNORE_VERSION = "ignore-version"; private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME); - private static final String IGNORE_VERSION_WARNING = NAME + ".ignore.version.warn"; - private static final String VERSION_MISMATCH_WARNING = NAME + ".version.mismatch.warn"; private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt"; @@ -85,8 +82,6 @@ public final class GenerateJLIClassesPlugin implements Plugin { String mainArgument; - boolean ignoreVersion; - public GenerateJLIClassesPlugin() { } @@ -170,7 +165,6 @@ public final class GenerateJLIClassesPlugin implements Plugin { @Override public void configure(Map<String, String> config) { mainArgument = config.get(NAME); - ignoreVersion = Boolean.parseBoolean(config.get(IGNORE_VERSION)); } public void initialize(ResourcePool in) { @@ -208,26 +202,6 @@ public final class GenerateJLIClassesPlugin implements Plugin { } } - private boolean checkVersion(Runtime.Version linkedVersion) { - Runtime.Version baseVersion = Runtime.version(); - if (baseVersion.major() != linkedVersion.major() || - baseVersion.minor() != linkedVersion.minor()) { - return false; - } - return true; - } - - private Runtime.Version getLinkedVersion(ResourcePool in) { - ModuleDescriptor.Version version = in.moduleView() - .findModule("java.base") - .get() - .descriptor() - .version() - .orElseThrow(() -> new PluginException("No version defined in " - + "the java.base being linked")); - return Runtime.Version.parse(version.toString()); - } - private void readTraceConfig(Stream<String> lines) { // Use TreeSet/TreeMap to keep things sorted in a deterministic // order to avoid scrambling the layout on small changes and to @@ -315,24 +289,6 @@ public final class GenerateJLIClassesPlugin implements Plugin { @Override public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { - if (ignoreVersion) { - System.out.println( - PluginsResourceBundle - .getMessage(IGNORE_VERSION_WARNING)); - } else if (!checkVersion(getLinkedVersion(in))) { - // The linked images are not version compatible - if (mainArgument != null) { - // Log a mismatch warning if an argument was specified - System.out.println( - PluginsResourceBundle - .getMessage(VERSION_MISMATCH_WARNING, - getLinkedVersion(in), - Runtime.version())); - } - in.transformAndCopy(entry -> entry, out); - return out.build(); - } - initialize(in); // Copy all but DMH_ENTRY to out in.transformAndCopy(entry -> { diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties index 1a976a3aa57..ec8121a15fe 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties @@ -107,6 +107,8 @@ main.extended.help.footer=\ error.prefix=Error: warn.prefix=Warning: +err.empty.module.path=empty module path +err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3} err.automatic.module:automatic module cannot be used with jlink: {0} from {1} err.unknown.byte.order:unknown byte order {0} err.launcher.main.class.empty:launcher main class name cannot be empty: {0} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index 819c3530fc9..72815fc5be0 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -75,7 +75,7 @@ where <section-name> is \"man\" or \"headers". exclude-jmod-section.description=\ Specify a JMOD section to exclude -generate-jli-classes.argument=@filename[:ignore-version=<true|false>] +generate-jli-classes.argument=@filename generate-jli-classes.description=\ Specify a file listing the java.lang.invoke classes to pre-generate. \n\ @@ -84,15 +84,6 @@ If this plugin runs on a different runtime version than the image being \n\ created then code generation will be disabled by default to guarantee \n\ correctness - add ignore-version=true to override this. -generate-jli-classes.ignore.version.warn=\ -WARNING: --generate-jli-classes set to ignore version mismatch between \n\ -JDK running jlink and target image. - -generate-jli-classes.version.mismatch.warn=\ -WARNING: Pre-generation of JLI classes is only supported when linking \n\ -the same version of java.base ({0}) as the JDK running jlink ({1}), \n\ -class generation skipped - specify ignore-version to override. - system-modules.argument=retainModuleTarget system-modules.description=Fast loading of module descriptors (always enabled) diff --git a/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java b/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java index dd8d66391e3..46912a68033 100644 --- a/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java +++ b/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java @@ -25,6 +25,8 @@ * @test * @bug 8177471 * @summary jlink should use the version from java.base.jmod to find modules + * @bug 8185130 + * @summary jlink should throw error if target image and current JDK versions don't match * @modules java.base/jdk.internal.module * @library /test/lib * @build jdk.test.lib.process.* CheckRuntimeVersion @@ -122,7 +124,9 @@ public class JLinkMRJavaBaseVersionTest { System.out.println("Testing jlink with " + getJmods() + " of target version " + version); // use jlink to build image from multi-release jar - jlink("m1.jar", "myimage"); + if (jlink("m1.jar", "myimage")) { + return; + } // validate runtime image Path java = Paths.get("myimage", "bin", "java"); @@ -130,12 +134,7 @@ public class JLinkMRJavaBaseVersionTest { // validate the image linked with the proper MR version - if (version.equalsIgnoreOptional(Runtime.version())) { - ProcessTools.executeProcess(java.toString(), "-cp", System.getProperty("test.classes"), - "CheckRuntimeVersion", String.valueOf(version.major()), - "java.base", "java.logging", "m1") - .shouldHaveExitValue(0); - } else { + if (!version.equalsIgnoreOptional(Runtime.version())) { ProcessTools.executeProcess(java.toString(), "-cp", System.getProperty("test.classes"), "CheckRuntimeVersion", String.valueOf(version.major()), "java.base", "m1") @@ -151,13 +150,19 @@ public class JLinkMRJavaBaseVersionTest { return Runtime.Version.parse(mref.descriptor().version().get().toString()); } - private void jlink(String jar, String image) { + private boolean jlink(String jar, String image) { List<String> args = List.of("--output", image, "--add-modules", "m1", "--module-path", getJmods().toString() + File.pathSeparator + jar); System.out.println("jlink " + args.stream().collect(Collectors.joining(" "))); int exitCode = JLINK_TOOL.run(System.out, System.err, args.toArray(new String[0])); - Assert.assertEquals(exitCode, 0); + boolean isJDK9 = System.getProperty("java9.home") != null; + if (isJDK9) { + Assert.assertNotEquals(exitCode, 0); + } else { + Assert.assertEquals(exitCode, 0); + } + return isJDK9; } } From fa64310889d5a116f2080a942c2fde4d2d98b8c6 Mon Sep 17 00:00:00 2001 From: Nishit Jain <nishit.jain@oracle.com> Date: Wed, 6 Dec 2017 11:21:04 +0530 Subject: [PATCH 056/165] 8187551: MessageFormat.setFormat(int, Format) AIOOBE not thrown when documented Reviewed-by: naoto, rriggs --- .../classes/java/text/MessageFormat.java | 4 ++ .../MessageFormat/MessageRegression.java | 44 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index 5dd78c87432..4560abd822b 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -701,6 +701,10 @@ public class MessageFormat extends Format { * larger than the number of format elements in the pattern string */ public void setFormat(int formatElementIndex, Format newFormat) { + + if (formatElementIndex > maxOffset) { + throw new ArrayIndexOutOfBoundsException(formatElementIndex); + } formats[formatElementIndex] = newFormat; } diff --git a/test/jdk/java/text/Format/MessageFormat/MessageRegression.java b/test/jdk/java/text/Format/MessageFormat/MessageRegression.java index 1d71cc09892..f013ba00607 100644 --- a/test/jdk/java/text/Format/MessageFormat/MessageRegression.java +++ b/test/jdk/java/text/Format/MessageFormat/MessageRegression.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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,7 +25,7 @@ * @test * @bug 4031438 4058973 4074764 4094906 4104976 4105380 4106659 4106660 4106661 * 4111739 4112104 4113018 4114739 4114743 4116444 4118592 4118594 4120552 - * 4142938 4169959 4232154 4293229 + * 4142938 4169959 4232154 4293229 8187551 * @summary Regression tests for MessageFormat and associated classes * @library /java/text/testlib * @run main MessageRegression @@ -642,4 +642,44 @@ public class MessageRegression extends IntlTest { expected + "\", got \"" + result + "\""); } } + + /** + * @bug 8187551 + * test MessageFormat.setFormat() method to throw AIOOBE on invalid index. + */ + public void test8187551() { + //invalid cases ("pattern", "invalid format element index") + String[][] invalidCases = {{"The disk \"{1}\" contains {0}.", "2"}, + {"The disk \"{1}\" contains {0}.", "9"}, + {"On {1}, there are {0} and {2} folders", "3"}}; + + //invalid cases (must throw exception) + Arrays.stream(invalidCases).forEach(entry -> messageSetFormat(entry[0], + Integer.valueOf(entry[1]))); + } + + // test MessageFormat.setFormat() method for the given pattern and + // format element index + private void messageSetFormat(String pattern, int elemIndex) { + MessageFormat form = new MessageFormat(pattern); + + double[] fileLimits = {0, 1, 2}; + String[] filePart = {"no files", "one file", "{0,number} files"}; + ChoiceFormat fileForm = new ChoiceFormat(fileLimits, filePart); + + boolean AIOOBEThrown = false; + try { + form.setFormat(elemIndex, fileForm); + } catch (ArrayIndexOutOfBoundsException ex) { + AIOOBEThrown = true; + } + + if (!AIOOBEThrown) { + throw new RuntimeException("[FAILED: Must throw" + + " ArrayIndexOutOfBoundsException for" + + " invalid index " + elemIndex + " used in" + + " MessageFormat.setFormat(index, format)]"); + } + } + } From 993e8f206c046a84e2993b0f9660efbff1bc8888 Mon Sep 17 00:00:00 2001 From: Abdul Kolarkunnu <abdul.kolarkunnu@oracle.com> Date: Wed, 6 Dec 2017 12:05:46 +0530 Subject: [PATCH 057/165] 8192958: TEST.groups, group jdk_util_other:file not found: jdk/internal/uti Removed the package jdk/internal/util from the group jdk_util_other Reviewed-by: mchung, dholmes --- test/jdk/TEST.groups | 1 - 1 file changed, 1 deletion(-) diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 7fe1bcbac36..b4b1143a727 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -89,7 +89,6 @@ jdk_util = \ jdk_util_other = \ java/util \ sun/util \ - jdk/internal/util \ -:jdk_collections \ -:jdk_concurrent \ -:jdk_stream From d960d2031c3df6f71b79650ef0d998b2a13cdfeb Mon Sep 17 00:00:00 2001 From: Alan Bateman <alanb@openjdk.org> Date: Wed, 6 Dec 2017 08:28:46 +0000 Subject: [PATCH 058/165] 8192973: Adding "Module Resolution" to javadoc search index Reviewed-by: mchung, jjg --- src/java.base/share/classes/java/lang/module/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/module/package-info.java b/src/java.base/share/classes/java/lang/module/package-info.java index 74021cd0bef..41278ffda0f 100644 --- a/src/java.base/share/classes/java/lang/module/package-info.java +++ b/src/java.base/share/classes/java/lang/module/package-info.java @@ -34,7 +34,7 @@ * will cause a {@code NullPointerException}, unless otherwise specified. </p> * * - * <h1><a id="resolution">Resolution</a></h1> + * <h1><a id="resolution">{@index "Module Resolution"}</a></h1> * * <p> Resolution is the process of computing how modules depend on each other. * The process occurs at compile time and run time. </p> From d26e4ff9b5ea2b9cc5b7b899ed5df27e2b6882be Mon Sep 17 00:00:00 2001 From: Alan Bateman <alanb@openjdk.org> Date: Wed, 6 Dec 2017 08:33:04 +0000 Subject: [PATCH 059/165] 8186736: Spec clarifications for IllegalArgumentException throwing - ModuleLayer.defineX methods Reviewed-by: mchung --- .../share/classes/java/lang/ModuleLayer.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index d9b1b3a3714..41ce156b372 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -322,8 +322,8 @@ public final class ModuleLayer { * @return The newly created layer * * @throws IllegalArgumentException - * If the parent of the given configuration is not the configuration - * for this layer + * If the given configuration has more than one parent or the parent + * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithOneLoader} method @@ -364,8 +364,8 @@ public final class ModuleLayer { * @return The newly created layer * * @throws IllegalArgumentException - * If the parent of the given configuration is not the configuration - * for this layer + * If the given configuration has more than one parent or the parent + * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModulesWithManyLoaders} method @@ -403,8 +403,8 @@ public final class ModuleLayer { * @return The newly created layer * * @throws IllegalArgumentException - * If the parent of the given configuration is not the configuration - * for this layer + * If the given configuration has more than one parent or the parent + * of the configuration is not the configuration for this layer * @throws LayerInstantiationException * If the layer cannot be created for any of the reasons specified * by the static {@code defineModules} method @@ -473,8 +473,8 @@ public final class ModuleLayer { * @return A controller that controls the newly created layer * * @throws IllegalArgumentException - * If the parent configurations do not match the configuration of - * the parent layers, including order + * If the parent(s) of the given configuration do not match the + * configuration of the parent layers, including order * @throws LayerInstantiationException * If all modules cannot be defined to the same class loader for any * of the reasons listed above @@ -546,8 +546,8 @@ public final class ModuleLayer { * @return A controller that controls the newly created layer * * @throws IllegalArgumentException - * If the parent configurations do not match the configuration of - * the parent layers, including order + * If the parent(s) of the given configuration do not match the + * configuration of the parent layers, including order * @throws LayerInstantiationException * If the layer cannot be created because the configuration contains * a module named "{@code java.base}" or a module contains a package @@ -637,8 +637,8 @@ public final class ModuleLayer { * @return A controller that controls the newly created layer * * @throws IllegalArgumentException - * If the parent configurations do not match the configuration of - * the parent layers, including order + * If the parent(s) of the given configuration do not match the + * configuration of the parent layers, including order * @throws LayerInstantiationException * If creating the layer fails for any of the reasons listed above * @throws SecurityException From 7e9984c1126c2aaaa03f560eedca3ecfe654a947 Mon Sep 17 00:00:00 2001 From: Alan Bateman <alanb@openjdk.org> Date: Wed, 6 Dec 2017 08:36:09 +0000 Subject: [PATCH 060/165] 8182742: ClassLoader.getResourceXXX throws NPE when ClassLoader created by defineModulesWithXXX Reviewed-by: redestad, mchung --- .../classes/jdk/internal/loader/Loader.java | 44 +++------ .../lang/ModuleLayer/LayerAndLoadersTest.java | 94 +++++++++++-------- 2 files changed, 71 insertions(+), 67 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/loader/Loader.java b/src/java.base/share/classes/jdk/internal/loader/Loader.java index dc46f6630b0..7c67c007575 100644 --- a/src/java.base/share/classes/jdk/internal/loader/Loader.java +++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -28,7 +28,6 @@ package jdk.internal.loader; import java.io.File; import java.io.FilePermission; import java.io.IOException; -import java.io.UncheckedIOException; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleReader; @@ -58,12 +57,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Spliterator; -import java.util.Spliterators; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import java.util.stream.Stream; -import java.util.stream.StreamSupport; import jdk.internal.misc.SharedSecrets; import jdk.internal.module.Resources; @@ -403,12 +398,15 @@ public final class Loader extends SecureClassLoader { // this loader URL url = findResource(name); - if (url != null) { - return url; - } else { + if (url == null) { // parent loader - return parent.getResource(name); + if (parent != null) { + url = parent.getResource(name); + } else { + url = BootLoader.findResource(name); + } } + return url; } @Override @@ -419,7 +417,12 @@ public final class Loader extends SecureClassLoader { List<URL> urls = findResourcesAsList(name); // parent loader - Enumeration<URL> e = parent.getResources(name); + Enumeration<URL> e; + if (parent != null) { + e = parent.getResources(name); + } else { + e = BootLoader.findResources(name); + } // concat the URLs with the URLs returned by the parent return new Enumeration<>() { @@ -439,25 +442,6 @@ public final class Loader extends SecureClassLoader { }; } - @Override - public Stream<URL> resources(String name) { - Objects.requireNonNull(name); - // ordering not specified - int characteristics = (Spliterator.NONNULL | Spliterator.IMMUTABLE | - Spliterator.SIZED | Spliterator.SUBSIZED); - Supplier<Spliterator<URL>> supplier = () -> { - try { - List<URL> urls = findResourcesAsList(name); - return Spliterators.spliterator(urls, characteristics); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }; - Stream<URL> s1 = StreamSupport.stream(supplier, characteristics, false); - Stream<URL> s2 = parent.resources(name); - return Stream.concat(s1, s2); - } - /** * Finds the resources with the given name in this class loader. */ diff --git a/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java b/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java index e318d7570a5..96449bda594 100644 --- a/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java +++ b/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java @@ -52,6 +52,7 @@ import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; + import jdk.test.lib.compiler.CompilerUtils; import org.testng.annotations.BeforeTest; @@ -78,7 +79,7 @@ public class LayerAndLoadersTest { * Basic test of ModuleLayer.defineModulesWithOneLoader * * Test scenario: - * m1 requires m2 and m3 + * m1 requires m2 and m3 */ public void testWithOneLoader() throws Exception { Configuration cf = resolve("m1"); @@ -105,7 +106,7 @@ public class LayerAndLoadersTest { * Basic test of ModuleLayer.defineModulesWithManyLoaders * * Test scenario: - * m1 requires m2 and m3 + * m1 requires m2 and m3 */ public void testWithManyLoaders() throws Exception { Configuration cf = resolve("m1"); @@ -136,9 +137,9 @@ public class LayerAndLoadersTest { * modules is a service provider module. * * Test scenario: - * m1 requires m2 and m3 - * m1 uses S - * m4 provides S with ... + * m1 requires m2 and m3 + * m1 uses S + * m4 provides S with ... */ public void testServicesWithOneLoader() throws Exception { Configuration cf = resolveAndBind("m1"); @@ -175,9 +176,9 @@ public class LayerAndLoadersTest { * modules is a service provider module. * * Test scenario: - * m1 requires m2 and m3 - * m1 uses S - * m4 provides S with ... + * m1 requires m2 and m3 + * m1 uses S + * m4 provides S with ... */ public void testServicesWithManyLoaders() throws Exception { Configuration cf = resolveAndBind("m1"); @@ -234,7 +235,7 @@ public class LayerAndLoadersTest { ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent); testLoad(layer, cn); - // one loader with boot loader as parent + // one loader with boot loader as parent layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, null); testLoadFail(layer, cn); @@ -252,21 +253,21 @@ public class LayerAndLoadersTest { * Test defineModulesWithXXX when modules that have overlapping packages. * * Test scenario: - * m1 exports p - * m2 exports p + * m1 exports p + * m2 exports p */ public void testOverlappingPackages() { ModuleDescriptor descriptor1 - = ModuleDescriptor.newModule("m1").exports("p").build(); + = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.newModule("m2").exports("p").build(); + = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf = ModuleLayer.boot() - .configuration() - .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2")); + .configuration() + .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2")); // cannot define both module m1 and m2 to the same class loader try { @@ -284,35 +285,35 @@ public class LayerAndLoadersTest { * Test ModuleLayer.defineModulesWithXXX with split delegation. * * Test scenario: - * layer1: m1 exports p, m2 exports p - * layer2: m3 reads m1, m4 reads m2 + * layer1: m1 exports p, m2 exports p + * layer2: m3 reads m1, m4 reads m2 */ public void testSplitDelegation() { ModuleDescriptor descriptor1 - = ModuleDescriptor.newModule("m1").exports("p").build(); + = ModuleDescriptor.newModule("m1").exports("p").build(); ModuleDescriptor descriptor2 - = ModuleDescriptor.newModule("m2").exports("p").build(); + = ModuleDescriptor.newModule("m2").exports("p").build(); ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); Configuration cf1 = ModuleLayer.boot() - .configuration() - .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2")); + .configuration() + .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2")); ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null); checkLayer(layer1, "m1", "m2"); ModuleDescriptor descriptor3 - = ModuleDescriptor.newModule("m3").requires("m1").build(); + = ModuleDescriptor.newModule("m3").requires("m1").build(); ModuleDescriptor descriptor4 - = ModuleDescriptor.newModule("m4").requires("m2").build(); + = ModuleDescriptor.newModule("m4").requires("m2").build(); ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4); Configuration cf2 = cf1.resolve(finder2, ModuleFinder.of(), - Set.of("m3", "m4")); + Set.of("m3", "m4")); // package p cannot be supplied by two class loaders try { @@ -331,8 +332,8 @@ public class LayerAndLoadersTest { * named modules in the parent layer. * * Test scenario: - * layer1: m1, m2, m3 => same loader - * layer2: m1, m2, m4 => same loader + * layer1: m1, m2, m3 => same loader + * layer2: m1, m2, m4 => same loader */ public void testOverriding1() throws Exception { Configuration cf1 = resolve("m1"); @@ -342,7 +343,7 @@ public class LayerAndLoadersTest { ModuleFinder finder = ModuleFinder.of(MODS_DIR); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), - Set.of("m1")); + Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null); checkLayer(layer2, "m1", "m2", "m3"); @@ -378,8 +379,8 @@ public class LayerAndLoadersTest { * named modules in the parent layer. * * Test scenario: - * layer1: m1, m2, m3 => loader pool - * layer2: m1, m2, m3 => loader pool + * layer1: m1, m2, m3 => loader pool + * layer2: m1, m2, m3 => loader pool */ public void testOverriding2() throws Exception { Configuration cf1 = resolve("m1"); @@ -389,7 +390,7 @@ public class LayerAndLoadersTest { ModuleFinder finder = ModuleFinder.of(MODS_DIR); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), - Set.of("m1")); + Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); checkLayer(layer2, "m1", "m2", "m3"); @@ -482,7 +483,7 @@ public class LayerAndLoadersTest { ModuleFinder finder = finderFor("m1", "m3"); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), - Set.of("m1")); + Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null); checkLayer(layer2, "m1", "m3"); @@ -517,7 +518,7 @@ public class LayerAndLoadersTest { ModuleFinder finder = finderFor("m1", "m3"); Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(), - Set.of("m1")); + Set.of("m1")); ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null); checkLayer(layer2, "m1", "m3"); @@ -550,18 +551,27 @@ public class LayerAndLoadersTest { assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6); } - /** * Basic test for locating resources with a class loader created by * defineModulesWithOneLoader. */ public void testResourcesWithOneLoader() throws Exception { + testResourcesWithOneLoader(ClassLoader.getSystemClassLoader()); + testResourcesWithOneLoader(null); + } + + /** + * Test locating resources with the class loader created by + * defineModulesWithOneLoader. The class loader has the given class + * loader as its parent. + */ + void testResourcesWithOneLoader(ClassLoader parent) throws Exception { Configuration cf = resolve("m1"); - ClassLoader scl = ClassLoader.getSystemClassLoader(); - ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl); + ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent); ClassLoader loader = layer.findLoader("m1"); assertNotNull(loader); + assertTrue(loader.getParent() == parent); // check that getResource and getResources are consistent URL url1 = loader.getResource("module-info.class"); @@ -607,14 +617,24 @@ public class LayerAndLoadersTest { * defineModulesWithManyLoaders. */ public void testResourcesWithManyLoaders() throws Exception { + testResourcesWithManyLoaders(ClassLoader.getSystemClassLoader()); + testResourcesWithManyLoaders(null); + } + + /** + * Test locating resources with class loaders created by + * defineModulesWithManyLoaders. The class loaders have the given class + * loader as their parent. + */ + void testResourcesWithManyLoaders(ClassLoader parent) throws Exception { Configuration cf = resolve("m1"); - ClassLoader scl = ClassLoader.getSystemClassLoader(); - ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl); + ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, parent); for (Module m : layer.modules()) { String name = m.getName(); ClassLoader loader = m.getClassLoader(); assertNotNull(loader); + assertTrue(loader.getParent() == parent); // getResource should find the module-info.class for the module URL url = loader.getResource("module-info.class"); From 32bf9b9c6af8e357b3e2a850e4eae34109b8c052 Mon Sep 17 00:00:00 2001 From: Serguei Spitsyn <sspitsyn@openjdk.org> Date: Wed, 6 Dec 2017 02:09:08 -0800 Subject: [PATCH 061/165] 8182413: jdwp-protocol is generated without a DOCTYPE directive Add a DOCTYPE directive Reviewed-by: alanb --- make/jdk/src/classes/build/tools/jdwpgen/RootNode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java index 8fbd71036b8..2361f335949 100644 --- a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java +++ b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java @@ -40,6 +40,7 @@ class RootNode extends AbstractNamedNode { } void document(PrintWriter writer) { + writer.println("<!DOCTYPE html>"); writer.println("<html><head><title>" + comment() + "</title></head>"); writer.println("<body bgcolor=\"white\">"); for (Node node : components) { From 5ea9e32d6c145cb3bc85df10e9549951ff6579a7 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Wed, 6 Dec 2017 13:45:46 +0100 Subject: [PATCH 062/165] 8193061: Add run-test-prebuilt functionality Reviewed-by: erikj --- make/Help.gmk | 9 +- make/RunTestsPrebuilt.gmk | 283 ++++++++++++++++++++++++++++++++++ make/RunTestsPrebuiltSpec.gmk | 175 +++++++++++++++++++++ make/common/MakeBase.gmk | 11 ++ 4 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 make/RunTestsPrebuilt.gmk create mode 100644 make/RunTestsPrebuiltSpec.gmk diff --git a/make/Help.gmk b/make/Help.gmk index e7f0bfa29de..b6974c71de4 100644 --- a/make/Help.gmk +++ b/make/Help.gmk @@ -115,6 +115,13 @@ print-configurations: # We need a dummy rule otherwise make will complain @true -ALL_GLOBAL_TARGETS := help print-configurations +# This is not really a "help" target, but it is a global target, and those are +# all contained in this file. +run-test-prebuilt: + @( cd $(topdir) && \ + $(MAKE) --no-print-directory -r -R -I make/common/ -f make/RunTestsPrebuilt.gmk \ + run-test-prebuilt TEST="$(TEST)" ) + +ALL_GLOBAL_TARGETS := help print-configurations run-test-prebuilt .PHONY: $(ALL_GLOBAL_TARGETS) diff --git a/make/RunTestsPrebuilt.gmk b/make/RunTestsPrebuilt.gmk new file mode 100644 index 00000000000..ca5bc6f49e7 --- /dev/null +++ b/make/RunTestsPrebuilt.gmk @@ -0,0 +1,283 @@ +# +# Copyright (c) 2017, 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. 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. +# + +################################################################################ +# Initial bootstrapping, copied and stripped down from Makefile and Init.gmk +################################################################################ + +# In Cygwin, the MAKE variable gets prepended with the current directory if the +# make executable is called using a Windows mixed path (c:/cygwin/bin/make.exe). +ifneq ($(findstring :, $(MAKE)), ) + export MAKE := $(patsubst $(CURDIR)%, %, $(patsubst $(CURDIR)/%, %, $(MAKE))) +endif + +# Locate this Makefile +ifeq ($(filter /%, $(lastword $(MAKEFILE_LIST))),) + makefile_path := $(CURDIR)/$(strip $(lastword $(MAKEFILE_LIST))) +else + makefile_path := $(lastword $(MAKEFILE_LIST)) +endif +TOPDIR := $(strip $(patsubst %/make/, %, $(dir $(makefile_path)))) + +################################################################################ +# Functions +################################################################################ + +# Setup a required or optional variable, and/or check that it is properly +# given. +# Note: No spaces are allowed around the arguments. +# +# $1: The name of the argument +# $2: The default value, if any, or OPTIONAL (do not provide a default but +# do not exit if it is missing) +# $3: If NO_CHECK, disable checking for target file/directory existence +define SetupVariable + ifeq ($$($1), ) + ifeq ($2, ) + $$(info Error: Prebuilt variable $1 is missing, needed for run-tests-prebuilt) + $$(error Cannot continue.) + else ifeq ($2, OPTIONAL) + ifneq ($$(findstring $$(LOG), info debug trace), ) + $$(info Prebuilt variable $1 is not provided) + endif + else + ifneq ($$(findstring $$(LOG), info debug trace), ) + $$(info Prebuilt variable $1=$2 (default value)) + endif + $1:=$2 + endif + else + ifneq ($$(findstring $$(LOG), info debug trace), ) + $$(info Prebuilt variable $1=$$($1)) + endif + endif + # If $1 has a value (is not optional), and $3 is not set (to NO_CHECK), + # and if wildcard is empty, then complain that the file is missing. + ifeq ($$(strip $$(if $$($1), , OPTIONAL) $$(wildcard $$($1)) $3), ) + $$(info Error: Prebuilt variable $1 points to missing file/directory:) + $$(info '$$($1)') + $$(error Cannot continue.) + endif +endef + +# Create an ephemeral spec file +# +# $1: The output file name +# $2..$N: The lines to output to the file +define CreateNewSpec + $(if $(strip $(26)), \ + $(error Internal makefile error: \ + Too many arguments to macro, please update CreateNewSpec in RunTestsPrebuilt.gmk) \ + ) \ + $(shell $(RM) $1) \ + $(foreach i, $(call sequence, 2, 25), \ + $(if $(strip $($i)), \ + $(call AppendFile, $(strip $($i)), $1) \ + ) \ + ) +endef + +################################################################################ +# Check input and setup basic buildsystem support +################################################################################ + +# Verify that user has given correct additional input. + +# These variables are absolutely necessary +$(eval $(call SetupVariable,OUTPUTDIR)) +$(eval $(call SetupVariable,BOOT_JDK)) +$(eval $(call SetupVariable,JT_HOME)) + +# These can have default values based on the ones above +$(eval $(call SetupVariable,JDK_IMAGE_DIR,$(OUTPUTDIR)/images/jdk)) +$(eval $(call SetupVariable,TEST_IMAGE_DIR,$(OUTPUTDIR)/images/test)) + +# Provide default values for tools that we need +$(eval $(call SetupVariable,MAKE,make,NO_CHECK)) +$(eval $(call SetupVariable,BASH,bash,NO_CHECK)) + +# Check optional variables +$(eval $(call SetupVariable,JIB_JAR,OPTIONAL)) + +# Now that we have verified that we have the required variables available, we +# can include the prebuilt spec file ourselves, without an ephemeral spec +# wrapper. This is required so we can include MakeBase which is needed for +# CreateNewSpec. +HAS_SPEC := +include $(TOPDIR)/make/InitSupport.gmk + +$(eval $(call CheckDeprecatedEnvironment)) +$(eval $(call CheckInvalidMakeFlags)) +$(eval $(call ParseLogLevel)) + +SPEC := $(TOPDIR)/make/RunTestsPrebuiltSpec.gmk +include $(SPEC) +include $(TOPDIR)/make/common/MakeBase.gmk + +################################################################################ +# Determine what platform we're running on +################################################################################ +UNAME := uname + +# Get OS name from uname (Cygwin inexplicably adds _NT-x.x) +UNAME_OS := $(shell $(UNAME) -s | $(CUT) -f1 -d_) + +ifeq ($(UNAME_OS), CYGWIN) + OPENJDK_TARGET_OS := windows + OPENJDK_TARGET_OS_TYPE := windows + OPENJDK_TARGET_OS_ENV := windows.cygwin +else + OPENJDK_TARGET_OS_TYPE:=unix + ifeq ($(UNAME_OS), Linux) + OPENJDK_TARGET_OS := linux + else ifeq ($(UNAME_OS), Darwin) + OPENJDK_TARGET_OS := macosx + else ifeq ($(UNAME_OS), SunOS) + OPENJDK_TARGET_OS := solaris + else + OPENJDK_TARGET_OS := $(UNAME_OS) + endif + OPENJDK_TARGET_OS_ENV := $(OPENJDK_TARGET_OS) +endif + +# Assume little endian unless otherwise specified +OPENJDK_TARGET_CPU_ENDIAN := little + +ifeq ($(OPENJDK_TARGET_OS), solaris) + # On solaris, use uname -p + UNAME_CPU := $(shell $(UNAME) -p) + # Assume 64-bit platform + OPENJDK_TARGET_CPU_BITS := 64 + ifeq ($(UNAME_CPU), i386) + OPENJDK_TARGET_CPU := x86_64 + else ifeq ($(UNAME_CPU), sparc) + OPENJDK_TARGET_CPU := sparcv9 + OPENJDK_TARGET_CPU_ENDIAN := big + else + OPENJDK_TARGET_CPU := $(UNAME_CPU) + endif +else + # ... all others use uname -m + UNAME_CPU := $(shell $(UNAME) -m) + ifeq ($(UNAME_CPU), i686) + OPENJDK_TARGET_CPU := x86 + OPENJDK_TARGET_CPU_BITS := 32 + else + # Assume all others are 64-bit. We use the same CPU name as uname for + # at least x86_64 and aarch64. + OPENJDK_TARGET_CPU := $(UNAME_CPU) + OPENJDK_TARGET_CPU_BITS := 64 + endif +endif + +OPENJDK_TARGET_CPU_ARCH := $(OPENJDK_TARGET_CPU) +ifeq ($(OPENJDK_TARGET_CPU), x86_64) + OPENJDK_TARGET_CPU_ARCH := x86 +else ifeq ($(OPENJDK_TARGET_CPU), sparcv9) + OPENJDK_TARGET_CPU_ARCH := sparc +endif + +ifeq ($(OPENJDK_TARGET_OS), windows) + ifeq ($(wildcard $(TEST_IMAGE_DIR)/bin/fixpath.exe), ) + $$(info Error: fixpath is missing from test image '$(TEST_IMAGE_DIR)') + $$(error Cannot continue.) + endif + FIXPATH := $(TEST_IMAGE_DIR)/bin/fixpath.exe -c + PATH_SEP:=; +else + FIXPATH := + PATH_SEP:=: +endif + +# Check number of cores +ifeq ($(OPENJDK_TARGET_OS), linux) + NUM_CORES := $(shell $(CAT) /proc/cpuinfo | $(GREP) -c processor) +else ifeq ($(OPENJDK_TARGET_OS), macosx) + NUM_CORES := $(shell /usr/sbin/sysctl -n hw.ncpu) +else ifeq ($(OPENJDK_TARGET_OS), solaris) + NUM_CORES := $(shell LC_MESSAGES=C /usr/sbin/psrinfo -v | $(GREP) -c on-line) +else ifeq ($(OPENJDK_TARGET_OS), windows) + NUM_CORES := $(NUMBER_OF_PROCESSORS) +else + NUM_CORES := 1 +endif + +################################################################################ +# Generate the ephemeral spec file +################################################################################ + +# Now we can include additional custom support. +# This might define CUSTOM_NEW_SPEC_LINE +ifneq ($(CUSTOM_MAKE_DIR), ) + include $(CUSTOM_MAKE_DIR)/RunTestsPrebuilt.gmk +endif + +NEW_SPEC := $(OUTPUTDIR)/run-test-spec.gmk + +$(call CreateNewSpec, $(NEW_SPEC), \ + # Generated file -- do not edit!, \ + SPEC := $(NEW_SPEC), \ + TOPDIR := $(TOPDIR), \ + OUTPUTDIR := $(OUTPUTDIR), \ + BOOT_JDK := $(BOOT_JDK), \ + JT_HOME := $(JT_HOME), \ + JDK_IMAGE_DIR := $(JDK_IMAGE_DIR), \ + TEST_IMAGE_DIR := $(TEST_IMAGE_DIR), \ + MAKE := $(MAKE), \ + BASH := $(BASH), \ + JIB_JAR := $(JIB_JAR), \ + FIXPATH := $(FIXPATH), \ + PATH_SEP := $(PATH_SEP), \ + OPENJDK_TARGET_OS := $(OPENJDK_TARGET_OS), \ + OPENJDK_TARGET_OS_TYPE := $(OPENJDK_TARGET_OS_TYPE), \ + OPENJDK_TARGET_OS_ENV := $(OPENJDK_TARGET_OS_ENV), \ + OPENJDK_TARGET_CPU := $(OPENJDK_TARGET_CPU), \ + OPENJDK_TARGET_CPU_ARCH := $(OPENJDK_TARGET_CPU_ARCH), \ + OPENJDK_TARGET_CPU_BITS := $(OPENJDK_TARGET_CPU_BITS), \ + OPENJDK_TARGET_CPU_ENDIAN := $(OPENJDK_TARGET_CPU_ENDIAN), \ + NUM_CORES := $(NUM_CORES), \ + include $(TOPDIR)/make/RunTestsPrebuiltSpec.gmk, \ + $(CUSTOM_NEW_SPEC_LINE), \ +) + +################################################################################ +# The run-test-prebuilt target +################################################################################ + +SPEC := $(NEW_SPEC) + +default: all + +run-test-prebuilt: + @$(RM) -f $(MAKESUPPORT_OUTPUTDIR)/exit-with-error + @cd $(TOPDIR) && $(MAKE) $(MAKE_ARGS) -f make/RunTests.gmk run-test \ + TEST="$(TEST)" + @if test -f $(MAKESUPPORT_OUTPUTDIR)/exit-with-error ; then \ + exit 1 ; \ + fi + +all: run-test-prebuilt + +.PHONY: default all diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk new file mode 100644 index 00000000000..5194099fd62 --- /dev/null +++ b/make/RunTestsPrebuiltSpec.gmk @@ -0,0 +1,175 @@ +# +# Copyright (c) 2017, 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. 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. +# + +################################################################################ +# Fake minimalistic spec file for RunTestsPrebuilt.gmk. +################################################################################ + +define VerifyVariable + ifeq ($$($1), ) + $$(info Error: Variable $1 is missing, needed by RunTestPrebuiltSpec.gmk) + $$(error Cannot continue.) + else + ifneq ($$(findstring $$(LOG_LEVEL), debug trace), ) + $$(info Prebuilt variable $1=$$($1)) + endif + endif +endef + +# It is the responsibility of the file including us to have set these up. +# Verify that this is correct. +$(eval $(call VerifyVariable,SPEC)) +$(eval $(call VerifyVariable,TOPDIR)) +$(eval $(call VerifyVariable,OUTPUTDIR)) +$(eval $(call VerifyVariable,BOOT_JDK)) +$(eval $(call VerifyVariable,JT_HOME)) +$(eval $(call VerifyVariable,JDK_IMAGE_DIR)) +$(eval $(call VerifyVariable,TEST_IMAGE_DIR)) +$(eval $(call VerifyVariable,MAKE)) +$(eval $(call VerifyVariable,BASH)) + +################################################################################ +# The "human readable" name of this configuration +CONF_NAME := run-test-prebuilt + +# Number of parallel jobs to use for compilation +JOBS ?= $(NUM_CORES) +TEST_JOBS ?= 0 + +# Use hard-coded values for java flags (one size, fits all!) +JAVA_FLAGS := -Duser.language=en -Duser.country=US +JAVA_FLAGS_BIG:= -Xms64M -Xmx1600M -XX:ThreadStackSize=1536 +JAVA_FLAGS_SMALL:= -XX:+UseSerialGC -Xms32M -Xmx512M -XX:TieredStopAtLevel=1 +BUILD_JAVA_FLAGS := $(JAVA_FLAGS_BIG) + +################################################################################ +# Hard-coded values copied from spec.gmk.in. +X:= +SPACE:=$(X) $(X) +COMMA:=, +MAKE_ARGS = $(MAKE_LOG_FLAGS) -r -R -I $(TOPDIR)/make/common SPEC=$(SPEC) \ + MAKE_LOG_FLAGS="$(MAKE_LOG_FLAGS)" LOG_LEVEL=$(LOG_LEVEL) +BASH_ARGS := -o pipefail -e +SHELL := $(BASH) $(BASH_ARGS) + +################################################################################ +# Set some reasonable defaults for features +DEBUG_LEVEL := release +HOTSPOT_DEBUG_LEVEL := release +BUILD_GTEST := true +BUILD_FAILURE_HANDLER := true + +################################################################################ +# Alias some paths (that should not really be used) to our JDK image under test. +SUPPORT_OUTPUTDIR := $(OUTPUTDIR)/support +BUILDTOOLS_OUTPUTDIR := $(OUTPUTDIR)/buildtools +HOTSPOT_OUTPUTDIR := $(OUTPUTDIR)/hotspot +JDK_OUTPUTDIR := $(OUTPUTDIR)/jdk +IMAGES_OUTPUTDIR := $(OUTPUTDIR)/images +BUNDLES_OUTPUTDIR := $(OUTPUTDIR)/bundles +TESTMAKE_OUTPUTDIR := $(OUTPUTDIR)/test-make +MAKESUPPORT_OUTPUTDIR := $(OUTPUTDIR)/make-support +BUILDJDK_OUTPUTDIR := $(OUTPUTDIR)/buildjdk + +JRE_IMAGE_DIR := $(JDK_IMAGE_DIR) + +################################################################################ +# Assume build platform is same as target platform +OPENJDK_BUILD_OS := $(OPENJDK_TARGET_OS) +OPENJDK_BUILD_OS_TYPE := $(OPENJDK_TARGET_OS_TYPE) +OPENJDK_BUILD_OS_ENV := $(OPENJDK_TARGET_OS_ENV) + +OPENJDK_BUILD_CPU := $(OPENJDK_TARGET_CPU) +OPENJDK_BUILD_CPU_ARCH := $(OPENJDK_TARGET_CPU_ARCH) +OPENJDK_BUILD_CPU_BITS := $(OPENJDK_TARGET_CPU_BITS) +OPENJDK_BUILD_CPU_ENDIAN := $(OPENJDK_TARGET_CPU_ENDIAN) + +################################################################################ +# Java executable definitions +JAVA_CMD := $(BOOT_JDK)/bin/java +JAVAC_CMD := $(BOOT_JDK)/bin/javac +JAVAH_CMD := $(BOOT_JDK)/bin/javah +JAR_CMD := $(BOOT_JDK)/bin/jar +JLINK_CMD := $(JDK_OUTPUTDIR)/bin/jlink +JMOD_CMD := $(JDK_OUTPUTDIR)/bin/jmod +JARSIGNER_CMD := $(BOOT_JDK)/bin/jarsigner + +JAVA := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_BIG) $(JAVA_FLAGS) +JAVA_SMALL := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_SMALL) $(JAVA_FLAGS) +JAVA_JAVAC := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_SMALL) $(JAVA_FLAGS) +JAVAC := $(FIXPATH) $(JAVAC_CMD) +JAVAH := $(FIXPATH) $(JAVAH_CMD) +JAR := $(FIXPATH) $(JAR_CMD) +JLINK := $(FIXPATH) $(JLINK_CMD) +JMOD := $(FIXPATH) $(JMOD_CMD) +JARSIGNER := $(FIXPATH) $(JARSIGNER_CMD) + +BUILD_JAVA := $(JAVA) +################################################################################ +# Some common tools. Assume most common name and no path. +AWK := awk +BASENAME := basename +CAT := cat +CD := cd +CHMOD := chmod +CP := cp +CUT := cut +DATE := date +DIFF := diff +DIRNAME := dirname +FIND := find +FIND_DELETE := -delete +ECHO := echo +EGREP := grep -E +FGREP := grep -F +GREP := grep +GZIP := gzip +HEAD := head +LS := ls +LN := ln +MKDIR := mkdir +MV := mv +NAWK := nawk +NICE := nice +PATCH := patch +PRINTF := printf +RM := rm -f +RMDIR := rmdir +SED := sed +SH := sh +SORT := sort +TAR := tar +TAIL := tail +TEE := tee +TR := tr +TOUCH := touch +UNIQ := uniq +WC := wc +XARGS := xargs +ZIPEXE := zip +UNZIP := unzip +EXPR := expr +FILE := file +HG := hg diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index 69dc9fbffe1..61a27155a84 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -912,6 +912,17 @@ else $(shell $(PRINTF) "%s" $(call ShellQuote, $1) > $2) endif +# Param 1 - Text to write +# Param 2 - File to write to +ifeq ($(HAS_FILE_FUNCTION), true) + AppendFile = \ + $(file >>$2,$(strip $1)) +else + # Use printf to get consistent behavior on all platforms. + AppendFile = \ + $(shell $(PRINTF) "%s" $(call ShellQuote, $1) >> $2) +endif + ################################################################################ # DependOnVariable # From a923e57c7b042e4e11975d14503810090ed41de6 Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie <ihse@openjdk.org> Date: Wed, 6 Dec 2017 13:47:35 +0100 Subject: [PATCH 063/165] 8193060: Set MAKE env variable in jib profile for gnumake Reviewed-by: tbell, erikj --- make/conf/jib-profiles.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 9bf032e0e8e..840966c742f 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -779,6 +779,10 @@ var getJibProfilesDependencies = function (input, common) { macosx_x64: "2.7.1-Xcode6.3-MacOSX10.9+1.0" }[input.target_platform]; + var makeBinDir = (input.build_os == "windows" + ? input.get("gnumake", "install_path") + "/cygwin/bin" + : input.get("gnumake", "install_path") + "/bin"); + var dependencies = { boot_jdk: { @@ -831,13 +835,13 @@ var getJibProfilesDependencies = function (input, common) { ? "gnumake-" + input.build_osenv_platform : "gnumake-" + input.build_platform), - configure_args: (input.build_os == "windows" - ? "MAKE=" + input.get("gnumake", "install_path") + "/cygwin/bin/make" - : "MAKE=" + input.get("gnumake", "install_path") + "/bin/make"), + configure_args: "MAKE=" + makeBinDir + "/make", - environment_path: (input.build_os == "windows" - ? input.get("gnumake", "install_path") + "/cygwin/bin" - : input.get("gnumake", "install_path") + "/bin") + environment: { + "MAKE": makeBinDir + "/make" + }, + + environment_path: makeBinDir }, freetype: { From 0c7be1d9e75285adc9e26bd4f69b71c8b91e8ee5 Mon Sep 17 00:00:00 2001 From: Sean Coffey <coffeys@openjdk.org> Date: Wed, 6 Dec 2017 14:33:33 +0000 Subject: [PATCH 064/165] 8185855: Debug exception stacks should be clearer Reviewed-by: mullan, ascarpino --- .../share/classes/java/security/IdentityScope.java | 4 ++-- .../share/classes/java/security/Signature.java | 2 +- .../share/classes/sun/security/jca/ProviderList.java | 2 +- .../share/classes/sun/security/pkcs/PKCS8Key.java | 1 - .../sun/security/provider/AuthPolicyFile.java | 3 ++- .../share/classes/sun/security/provider/DSA.java | 12 ------------ .../classes/sun/security/provider/PolicyFile.java | 4 +++- .../sun/security/util/AnchorCertificates.java | 2 +- .../sun/security/util/SignatureFileVerifier.java | 5 +++-- .../sun/security/x509/CertificateExtensions.java | 3 ++- .../share/classes/sun/security/x509/X509Key.java | 1 - 11 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/java.base/share/classes/java/security/IdentityScope.java b/src/java.base/share/classes/java/security/IdentityScope.java index 49c7c180322..2bd517cd422 100644 --- a/src/java.base/share/classes/java/security/IdentityScope.java +++ b/src/java.base/share/classes/java/security/IdentityScope.java @@ -90,8 +90,8 @@ class IdentityScope extends Identity { try { Class.forName(classname); } catch (ClassNotFoundException e) { - //Security.error("unable to establish a system scope from " + - // classname); + System.err.println("unable to establish a system scope from " + + classname); e.printStackTrace(); } } diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java index 3f0a9d96771..83a154ed216 100644 --- a/src/java.base/share/classes/java/security/Signature.java +++ b/src/java.base/share/classes/java/security/Signature.java @@ -1074,7 +1074,7 @@ public abstract class Signature extends SignatureSpi { debug.println("Further warnings of this type will " + "be suppressed"); } - new Exception("Call trace").printStackTrace(); + new Exception("Debug call trace").printStackTrace(); } } Exception lastException = null; diff --git a/src/java.base/share/classes/sun/security/jca/ProviderList.java b/src/java.base/share/classes/sun/security/jca/ProviderList.java index 81f8a6b8ef2..68597c891b2 100644 --- a/src/java.base/share/classes/sun/security/jca/ProviderList.java +++ b/src/java.base/share/classes/sun/security/jca/ProviderList.java @@ -308,7 +308,7 @@ public final class ProviderList { } if (debug != null) { debug.println("Loading all providers"); - new Exception("Call trace").printStackTrace(); + new Exception("Debug Info. Call trace:").printStackTrace(); } int n = 0; for (int i = 0; i < configs.length; i++) { diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java index ee2c6e31959..3301b72f400 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java @@ -346,7 +346,6 @@ public class PKCS8Key implements PrivateKey { } } catch (IOException e) { - // e.printStackTrace (); throw new InvalidKeyException("IOException : " + e.getMessage()); } diff --git a/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java b/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java index 46cdcd9b092..1626f48ccb7 100644 --- a/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java +++ b/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java @@ -187,6 +187,7 @@ public class AuthPolicyFile extends javax.security.auth.Policy { } catch (Exception e) { // ignore, treat it like we have no keystore if (debug != null) { + debug.println("Debug info only. No keystore."); e.printStackTrace(); } return null; @@ -261,7 +262,7 @@ public class AuthPolicyFile extends javax.security.auth.Policy { loaded_one = true; } catch (Exception e) { if (debug != null) { - debug.println("error reading policy " + e); + debug.println("Debug info only. Error reading policy " + e); e.printStackTrace(); } // ignore that policy diff --git a/src/java.base/share/classes/sun/security/provider/DSA.java b/src/java.base/share/classes/sun/security/provider/DSA.java index 9fc47851baf..705974ac6ad 100644 --- a/src/java.base/share/classes/sun/security/provider/DSA.java +++ b/src/java.base/share/classes/sun/security/provider/DSA.java @@ -490,18 +490,6 @@ abstract class DSA extends SignatureSpi { return printable; } - private static void debug(Exception e) { - if (debug) { - e.printStackTrace(); - } - } - - private static void debug(String s) { - if (debug) { - System.err.println(s); - } - } - /** * Standard SHA224withDSA implementation as defined in FIPS186-3. */ diff --git a/src/java.base/share/classes/sun/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java index 3319f91a7c0..ee761d754f5 100644 --- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java +++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java @@ -512,7 +512,8 @@ public class PolicyFile extends java.security.Policy { } } catch (Exception e) { if (debug != null) { - debug.println("error reading policy "+e); + debug.println( + "Debug info only. Error reading policy " +e); e.printStackTrace(); } // ignore that policy @@ -559,6 +560,7 @@ public class PolicyFile extends java.security.Policy { } catch (Exception e) { // ignore, treat it like we have no keystore if (debug != null) { + debug.println("Debug info only. Ignoring exception."); e.printStackTrace(); } } diff --git a/src/java.base/share/classes/sun/security/util/AnchorCertificates.java b/src/java.base/share/classes/sun/security/util/AnchorCertificates.java index e260dd44ac0..af77f8fda57 100644 --- a/src/java.base/share/classes/sun/security/util/AnchorCertificates.java +++ b/src/java.base/share/classes/sun/security/util/AnchorCertificates.java @@ -75,8 +75,8 @@ public class AnchorCertificates { } catch (Exception e) { if (debug != null) { debug.println("Error parsing cacerts"); + e.printStackTrace(); } - e.printStackTrace(); } return null; } diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index 5a459267130..35b06aa554c 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -386,8 +386,9 @@ public class SignatureFileVerifier { if (e.getMessage() != null) { debug.println(key + ": " + e.getMessage()); } else { - debug.println(key + ": " + algorithm + - " was disabled, no exception msg given."); + debug.println("Debug info only. " + key + ": " + + algorithm + + " was disabled, no exception msg given."); e.printStackTrace(); } } diff --git a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java index f26244e7c82..e76a8b331f3 100644 --- a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java +++ b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java @@ -124,7 +124,8 @@ public class CertificateExtensions implements CertAttrSet<Extension> { unparseableExtensions.put(ext.getExtensionId().toString(), new UnparseableExtension(ext, e)); if (debug != null) { - debug.println("Error parsing extension: " + ext); + debug.println("Debug info only." + + " Error parsing extension: " + ext); e.printStackTrace(); HexDumpEncoder h = new HexDumpEncoder(); System.err.println(h.encodeBuffer(ext.getExtensionValue())); diff --git a/src/java.base/share/classes/sun/security/x509/X509Key.java b/src/java.base/share/classes/sun/security/x509/X509Key.java index a11fa27dff8..6a0008a68c1 100644 --- a/src/java.base/share/classes/sun/security/x509/X509Key.java +++ b/src/java.base/share/classes/sun/security/x509/X509Key.java @@ -392,7 +392,6 @@ public class X509Key implements PublicKey { throw new InvalidKeyException ("excess key data"); } catch (IOException e) { - // e.printStackTrace (); throw new InvalidKeyException("IOException: " + e.getMessage()); } From 2c9ed8e178afd71f7112b2c0b0358796e3d4f109 Mon Sep 17 00:00:00 2001 From: Paul Sandoz <psandoz@openjdk.org> Date: Fri, 3 Nov 2017 10:01:08 -0700 Subject: [PATCH 065/165] 8188870: Bump classfile version number to 54 Reviewed-by: alanb, dholmes, coleenp, mcimadamore --- .../share/classfile/classFileParser.cpp | 4 +- .../com/sun/java/util/jar/pack/Constants.java | 8 +++- .../org/objectweb/asm/ClassReader.java | 2 +- .../internal/org/objectweb/asm/Opcodes.java | 1 + .../native/include/classfile_constants.h | 2 +- src/java.base/share/native/libjava/System.c | 4 +- .../com/sun/tools/javac/jvm/ClassFile.java | 5 ++- .../com/sun/tools/javac/jvm/ClassReader.java | 2 +- .../com/sun/tools/javac/jvm/ClassWriter.java | 13 ++---- .../com/sun/tools/javac/jvm/Target.java | 4 +- .../replacements/classfile/Classfile.java | 8 ++-- .../sun/tools/java/RuntimeConstants.java | 4 +- .../runtime/classFileParserBug/Class54.jasm | 43 +++++++++++++++++++ .../tools/javac/6330997/T6330997.java | 8 ++-- .../javac/classfiles/ClassVersionChecker.java | 15 ++++--- .../tools/javac/versions/Versions.java | 21 +++++---- 16 files changed, 96 insertions(+), 48 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 5074ba4ca83..6326ca1e76a 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -86,7 +86,7 @@ #define JAVA_CLASSFILE_MAGIC 0xCAFEBABE #define JAVA_MIN_SUPPORTED_VERSION 45 -#define JAVA_MAX_SUPPORTED_VERSION 53 +#define JAVA_MAX_SUPPORTED_VERSION 54 #define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 // Used for two backward compatibility reasons: @@ -108,6 +108,8 @@ #define JAVA_9_VERSION 53 +#define JAVA_10_VERSION 54 + void ClassFileParser::set_class_bad_constant_seen(short bad_constant) { assert((bad_constant == 19 || bad_constant == 20) && _major_version >= JAVA_9_VERSION, "Unexpected bad constant pool entry"); diff --git a/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java b/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java index 50dc8020b25..5eb79adb6ae 100644 --- a/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java +++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2017, 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 @@ -47,6 +47,7 @@ class Constants { 1.7 to 1.7.X 51,0 1.8 to 1.8.X 52,0 1.9 to 1.9.X 53,0 + 1.10 to 1.10.X 54,0 */ public static final Package.Version JAVA_MIN_CLASS_VERSION = @@ -67,6 +68,9 @@ class Constants { public static final Package.Version JAVA9_MAX_CLASS_VERSION = Package.Version.of(53, 00); + public static final Package.Version JAVA10_MAX_CLASS_VERSION = + Package.Version.of(54, 00); + public static final int JAVA_PACKAGE_MAGIC = 0xCAFED00D; public static final Package.Version JAVA5_PACKAGE_VERSION = @@ -83,7 +87,7 @@ class Constants { // upper limit, should point to the latest class version public static final Package.Version JAVA_MAX_CLASS_VERSION = - JAVA9_MAX_CLASS_VERSION; + JAVA10_MAX_CLASS_VERSION; // upper limit should point to the latest package version, for version info!. public static final Package.Version MAX_PACKAGE_VERSION = diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java index 3854a3eddb8..47e1f6d4b15 100644 --- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java +++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java @@ -185,7 +185,7 @@ public class ClassReader { public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version - if (readShort(off + 6) > Opcodes.V9) { + if (readShort(off + 6) > Opcodes.V10) { throw new IllegalArgumentException(); } // parses the constant pool diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java index 4dc3ff19c5c..7ce9d9210c8 100644 --- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java +++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java @@ -89,6 +89,7 @@ public interface Opcodes { int V1_7 = 0 << 16 | 51; int V1_8 = 0 << 16 | 52; int V9 = 0 << 16 | 53; + int V10 = 0 << 16 | 54; // access flags diff --git a/src/java.base/share/native/include/classfile_constants.h b/src/java.base/share/native/include/classfile_constants.h index e6490a74cda..3b63f31c6e8 100644 --- a/src/java.base/share/native/include/classfile_constants.h +++ b/src/java.base/share/native/include/classfile_constants.h @@ -31,7 +31,7 @@ extern "C" { #endif /* Classfile version number for this information */ -#define JVM_CLASSFILE_MAJOR_VERSION 53 +#define JVM_CLASSFILE_MAJOR_VERSION 54 #define JVM_CLASSFILE_MINOR_VERSION 0 /* Flags */ diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c index 80ab07191de..07e638862db 100644 --- a/src/java.base/share/native/libjava/System.c +++ b/src/java.base/share/native/libjava/System.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, 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 @@ -114,7 +114,7 @@ Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x) #define VENDOR_URL_BUG "http://bugreport.java.com/bugreport/" #endif -#define JAVA_MAX_SUPPORTED_VERSION 53 +#define JAVA_MAX_SUPPORTED_VERSION 54 #define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 #ifdef JAVA_SPECIFICATION_VENDOR /* Third party may NOT overwrite this. */ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java index 6429cf8f16f..6e80995e509 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, 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 @@ -110,7 +110,8 @@ public class ClassFile { V50(50, 0), // JDK 1.6: stackmaps V51(51, 0), // JDK 1.7 V52(52, 0), // JDK 1.8: lambda, type annos, param names - V53(53, 0); // JDK 1.9: modules, indy string concat + V53(53, 0), // JDK 1.9: modules, indy string concat + V54(54, 0); // JDK 10 Version(int major, int minor) { this.major = major; this.minor = minor; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index a0495a969bb..ec321fad4e5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2684,7 +2684,7 @@ public class ClassReader { minorVersion = nextChar(); majorVersion = nextChar(); - int maxMajor = 53; // Version.MAX().major; //******* TEMPORARY ******* + int maxMajor = Version.MAX().major; int maxMinor = Version.MAX().minor; if (majorVersion > maxMajor || majorVersion * 1000 + minorVersion < diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index f23385c7c21..a0a1453c1c2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, 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 @@ -1815,15 +1815,8 @@ public class ClassWriter extends ClassFile { acount += writeExtraClassAttributes(c); poolbuf.appendInt(JAVA_MAGIC); - - if (c.owner.kind == MDL) { - // temporarily overide to force use of v53 for module-info.class - poolbuf.appendChar(0); - poolbuf.appendChar(53); - } else { - poolbuf.appendChar(target.minorVersion); - poolbuf.appendChar(target.majorVersion); - } + poolbuf.appendChar(target.minorVersion); + poolbuf.appendChar(target.majorVersion); writePool(c.pool); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java index 1d1fdf1e11d..b0413afb743 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java @@ -63,8 +63,8 @@ public enum Target { /** JDK 9. */ JDK1_9("1.9", 53, 0), - /** JDK 10, initially an alias for 9 */ - JDK1_10("1.10", 53, 0); + /** JDK 10. */ + JDK1_10("1.10", 54, 0); private static final Context.Key<Target> targetKey = new Context.Key<>(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java index d627cc4f68b..c983a1b466c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -46,8 +46,8 @@ public class Classfile { private final ResolvedJavaType type; private final List<ClassfileBytecode> codeAttributes; - private static final int MAJOR_VERSION_JAVA7 = 51; - private static final int MAJOR_VERSION_JAVA9 = 53; + private static final int MAJOR_VERSION_JAVA_MIN = 51; + private static final int MAJOR_VERSION_JAVA_MAX = 54; private static final int MAGIC = 0xCAFEBABE; /** @@ -65,7 +65,7 @@ public class Classfile { int minor = stream.readUnsignedShort(); int major = stream.readUnsignedShort(); - if (major < MAJOR_VERSION_JAVA7 || major > MAJOR_VERSION_JAVA9) { + if (major < MAJOR_VERSION_JAVA_MIN || major > MAJOR_VERSION_JAVA_MAX) { throw new UnsupportedClassVersionError("Unsupported class file version: " + major + "." + minor); } diff --git a/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java b/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java index 1c7ada16f06..85b903d670a 100644 --- a/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java +++ b/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -67,7 +67,7 @@ public interface RuntimeConstants { /* Class File Constants */ int JAVA_MAGIC = 0xcafebabe; int JAVA_MIN_SUPPORTED_VERSION = 45; - int JAVA_MAX_SUPPORTED_VERSION = 53; + int JAVA_MAX_SUPPORTED_VERSION = 54; int JAVA_MAX_SUPPORTED_MINOR_VERSION = 0; /* Generate class file version for 1.1 by default */ diff --git a/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm b/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm new file mode 100644 index 00000000000..3150d5dddf4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 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 8188870 + * @summary Check that the JVM accepts class files with version 54 + * @run main Class54 + */ + +super public class Class54 version 54:0 { + + public Method "<init>":"()V" stack 1 locals 1 { + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; + } + + public static Method main:"([Ljava/lang/String;)V" stack 0 locals 1 { + return; + } + +} // end Class Class54 diff --git a/test/langtools/tools/javac/6330997/T6330997.java b/test/langtools/tools/javac/6330997/T6330997.java index 3ffdd78638d..3e4d8e535ed 100644 --- a/test/langtools/tools/javac/6330997/T6330997.java +++ b/test/langtools/tools/javac/6330997/T6330997.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2017, 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 @@ -23,7 +23,7 @@ /** * @test - * @bug 6330997 7025789 8000961 + * @bug 6330997 7025789 8000961 8188870 * @summary javac should accept class files with major version of the next release * @author Wei Tao * @modules jdk.compiler/com.sun.tools.javac.api @@ -32,8 +32,8 @@ * jdk.compiler/com.sun.tools.javac.main * jdk.compiler/com.sun.tools.javac.util * @clean T1 T2 - * @compile -source 9 -target 9 T1.java - * @compile -source 9 -target 9 T2.java + * @compile -source 10 -target 10 T1.java + * @compile -source 10 -target 10 T2.java * @run main/othervm T6330997 */ diff --git a/test/langtools/tools/javac/classfiles/ClassVersionChecker.java b/test/langtools/tools/javac/classfiles/ClassVersionChecker.java index 419ec1313da..4a6cf188c89 100644 --- a/test/langtools/tools/javac/classfiles/ClassVersionChecker.java +++ b/test/langtools/tools/javac/classfiles/ClassVersionChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 7157626 8001112 + * @bug 7157626 8001112 8188870 * @summary Test major version for all legal combinations for -source and -target * @author sgoel * @@ -38,7 +38,7 @@ import java.util.regex.*; public class ClassVersionChecker { int errors; - String[] jdk = {"", "1.6", "1.7", "1.8", "1.9"}; + String[] jdk = {"", "1.6", "1.7", "1.8", "1.9", "1.10"}; File javaFile = null; public static void main(String[] args) throws Throwable { @@ -58,10 +58,11 @@ public class ClassVersionChecker { * -1 => invalid combinations */ int[][] ver = - {{53, -1, -1, -1, -1}, - {53, 50, 51, 52, 53}, - {53, -1, 51, 52, 53}, - {53, -1, -1, 52, 53}}; + {{54, -1, -1, -1, -1, -1}, + {54, 50, 51, 52, 53, 54}, + {54, -1, 51, 52, 53, 54}, + {54, -1, -1, 52, 53, 54}, + {54, -1, -1, -1, 53, 54}}; // Loop to run all possible combinations of source/target values for (int i = 0; i< ver.length; i++) { diff --git a/test/langtools/tools/javac/versions/Versions.java b/test/langtools/tools/javac/versions/Versions.java index 9d6440038a0..52dfb9c5bc2 100644 --- a/test/langtools/tools/javac/versions/Versions.java +++ b/test/langtools/tools/javac/versions/Versions.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 8000961 8030610 8028546 + * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 8000961 8030610 8028546 8188870 * @summary Check interpretation of -target and -source options * @modules java.compiler * jdk.compiler @@ -64,12 +64,12 @@ public class Versions { String TC = ""; System.out.println("Version.java: Starting"); - check("53.0"); - check("53.0", "-source 1.6"); - check("53.0", "-source 1.7"); - check("53.0", "-source 1.8"); - check("53.0", "-source 1.9"); - check("53.0", "-source 1.10"); + check("54.0"); + check("54.0", "-source 1.6"); + check("54.0", "-source 1.7"); + check("54.0", "-source 1.8"); + check("54.0", "-source 1.9"); + check("54.0", "-source 1.10"); check_source_target("50.0", "6", "6"); check_source_target("51.0", "6", "7"); @@ -81,7 +81,11 @@ public class Versions { check_source_target("53.0", "7", "9"); check_source_target("53.0", "8", "9"); check_source_target("53.0", "9", "9"); - check_source_target("53.0", "10", "10"); + check_source_target("54.0", "6", "10"); + check_source_target("54.0", "7", "10"); + check_source_target("54.0", "8", "10"); + check_source_target("54.0", "9", "10"); + check_source_target("54.0", "10", "10"); checksrc16("-source 1.6"); checksrc16("-source 6"); @@ -99,7 +103,6 @@ public class Versions { checksrc19("-source 9"); checksrc19("-source 1.9", "-target 1.9"); checksrc19("-source 9", "-target 9"); - checksrc110(); checksrc110("-source 1.10"); checksrc110("-source 10"); From 205351451fcec0e8a182388f01a5e70ec65241c2 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu <zgu@openjdk.org> Date: Mon, 27 Nov 2017 17:19:53 -0500 Subject: [PATCH 066/165] 8190729: Adjustment to anonymous metadata space chunk allocation algorithm Adjusted anonymous metadata space chunk allocation algorithm to reduce waste Reviewed-by: stuefe, coleenp --- src/hotspot/share/memory/metaspace.cpp | 31 +++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 5671e12c2eb..18de6beaef9 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -785,7 +785,10 @@ class SpaceManager : public CHeapObj<mtClass> { Mutex* const _lock; // Type of metadata allocated. - Metaspace::MetadataType _mdtype; + const Metaspace::MetadataType _mdtype; + + // Type of metaspace + const Metaspace::MetaspaceType _space_type; // List of chunks in use by this SpaceManager. Allocations // are done from the current chunk. The list is used for deallocating @@ -796,6 +799,10 @@ class SpaceManager : public CHeapObj<mtClass> { // Maximum number of small chunks to allocate to a SpaceManager static uint const _small_chunk_limit; + // Maximum number of specialize chunks to allocate for anonymous + // metadata space to a SpaceManager + static uint const _anon_metadata_specialize_chunk_limit; + // Sum of all space in allocated chunks size_t _allocated_blocks_words; @@ -846,6 +853,7 @@ class SpaceManager : public CHeapObj<mtClass> { public: SpaceManager(Metaspace::MetadataType mdtype, + Metaspace::MetaspaceType space_type, Mutex* lock); ~SpaceManager(); @@ -963,6 +971,7 @@ class SpaceManager : public CHeapObj<mtClass> { }; uint const SpaceManager::_small_chunk_limit = 4; +uint const SpaceManager::_anon_metadata_specialize_chunk_limit = 4; const char* SpaceManager::_expand_lock_name = "SpaceManager chunk allocation lock"; @@ -2400,6 +2409,20 @@ size_t SpaceManager::calc_chunk_size(size_t word_size) { // _small_chunk_limit small chunks can be allocated. // After that a medium chunk is preferred. size_t chunk_word_size; + + // Special case for anonymous metadata space. + // Anonymous metadata space is usually small, with majority within 1K - 2K range and + // rarely about 4K (64-bits JVM). + // Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation + // from SpecializeChunk up to _anon_metadata_specialize_chunk_limit (4) reduces space waste + // from 60+% to around 30%. + if (_space_type == Metaspace::AnonymousMetaspaceType && + _mdtype == Metaspace::NonClassType && + sum_count_in_chunks_in_use(SpecializedIndex) < _anon_metadata_specialize_chunk_limit && + word_size + Metachunk::overhead() <= SpecializedChunk) { + return SpecializedChunk; + } + if (chunks_in_use(MediumIndex) == NULL && sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit) { chunk_word_size = (size_t) small_chunk_size(); @@ -2504,8 +2527,10 @@ void SpaceManager::print_on(outputStream* st) const { } SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, + Metaspace::MetaspaceType space_type, Mutex* lock) : _mdtype(mdtype), + _space_type(space_type), _allocated_blocks_words(0), _allocated_chunks_words(0), _allocated_chunks_count(0), @@ -3781,11 +3806,11 @@ void Metaspace::initialize(Mutex* lock, MetaspaceType type) { verify_global_initialization(); // Allocate SpaceManager for metadata objects. - _vsm = new SpaceManager(NonClassType, lock); + _vsm = new SpaceManager(NonClassType, type, lock); if (using_class_space()) { // Allocate SpaceManager for classes. - _class_vsm = new SpaceManager(ClassType, lock); + _class_vsm = new SpaceManager(ClassType, type, lock); } MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); From 570a828dfc3052446a0dae9fe120f6c75c62f660 Mon Sep 17 00:00:00 2001 From: Jiangli Zhou <jiangli@openjdk.org> Date: Mon, 27 Nov 2017 20:35:56 -0500 Subject: [PATCH 067/165] 8191504: [TESTBUG] CDSTestUtils.isUnableToMap() should check OptionalData region mapping failure Check all regions for mapping failure in CDSTestUtils.isUnableToMap(). Reviewed-by: iklam, ccheung, mseledtsov --- test/lib/jdk/test/lib/cds/CDSTestUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 8d0f9ef4bcb..5b1dd1846f8 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -166,9 +166,9 @@ public class CDSTestUtils { outStr.contains("Unable to map ReadWrite shared space at required address") || outStr.contains("Unable to map MiscData shared space at required address") || outStr.contains("Unable to map MiscCode shared space at required address") || - outStr.contains("Unable to map shared string space at required address") || + outStr.contains("Unable to map OptionalData shared space at required address") || outStr.contains("Could not allocate metaspace at a compatible address") || - outStr.contains("Unable to allocate shared string space: range is not within java heap") )) + outStr.contains("UseSharedSpaces: Unable to allocate region, range is not within java heap") )) { return true; } From 61e736cbae980433d5b4e547a7d2f159063bced0 Mon Sep 17 00:00:00 2001 From: Jiangli Zhou <jiangli.zhou@oracle.com> Date: Mon, 27 Nov 2017 20:21:34 -0800 Subject: [PATCH 068/165] 8188791: Move AppCDS from closed repo to open repo Co-authored-by: Mikhailo Seledtsov <mikhailo.seledtsov@oracle.com> Co-authored-by: Calvin Cheung <calvin.cheung@oracle.com> Reviewed-by: dsamersoff, simonis, minqi --- .../share/classfile/classListParser.cpp | 408 +- .../share/classfile/classListParser.hpp | 106 +- .../share/classfile/classLoaderExt.cpp | 327 +- .../share/classfile/classLoaderExt.hpp | 147 +- .../share/classfile/sharedClassUtil.cpp | 248 + .../share/classfile/sharedClassUtil.hpp | 111 +- .../share/classfile/systemDictionary.cpp | 2 +- .../classfile/systemDictionaryShared.cpp | 1086 ++ .../classfile/systemDictionaryShared.hpp | 379 +- .../share/classfile/systemDictionary_ext.hpp | 13 +- src/hotspot/share/classfile/vmSymbols.hpp | 9 +- src/hotspot/share/prims/cdsoffsets.cpp | 69 + src/hotspot/share/prims/cdsoffsets.hpp | 48 + src/hotspot/share/prims/whitebox.cpp | 18 + src/hotspot/share/runtime/arguments.cpp | 8 + src/hotspot/share/runtime/arguments_ext.hpp | 1 - src/hotspot/share/runtime/globals.hpp | 7 + test/hotspot/jtreg/TEST.groups | 17 +- .../jtreg/runtime/appcds/AppCDSOptions.java | 45 + .../jtreg/runtime/appcds/AppendClasspath.java | 87 + .../runtime/appcds/BootClassPathMismatch.java | 108 + .../appcds/CaseSensitiveClassPath.java | 92 + .../jtreg/runtime/appcds/ClassLoaderTest.java | 93 + .../jtreg/runtime/appcds/ClassPathAttr.java | 106 + .../runtime/appcds/CommandLineFlagCombo.java | 128 + .../appcds/CommandLineFlagComboNegative.java | 101 + .../jtreg/runtime/appcds/CompilerUtils.java | 80 + .../jtreg/runtime/appcds/DumpClassList.java | 103 + .../runtime/appcds/ExtraSymbols.invalid_1.txt | 11 + .../runtime/appcds/ExtraSymbols.invalid_2.txt | 5 + .../runtime/appcds/ExtraSymbols.invalid_3.txt | 13 + .../jtreg/runtime/appcds/ExtraSymbols.java | 89 + .../runtime/appcds/ExtraSymbols.symbols.txt | 10826 ++++++++++++++++ .../runtime/appcds/FieldAnnotationsTest.java | 67 + .../runtime/appcds/FreeUnusedMetadata.java | 118 + .../jtreg/runtime/appcds/HelloExtTest.java | 72 + .../jtreg/runtime/appcds/HelloTest.java | 44 + .../runtime/appcds/IgnoreEmptyClassPaths.java | 63 + .../jtreg/runtime/appcds/JarBuilder.java | 235 + .../jtreg/runtime/appcds/JvmtiAddPath.java | 107 + .../runtime/appcds/MismatchedUseAppCDS.java | 81 + .../runtime/appcds/MissingSuperTest.java | 52 + .../runtime/appcds/MultiProcessSharing.java | 144 + .../runtime/appcds/MultiReleaseJars.java | 238 + .../jtreg/runtime/appcds/OldClassTest.java | 167 + .../jtreg/runtime/appcds/PackageSealing.java | 60 + .../jtreg/runtime/appcds/ParallelLoad2.java | 64 + .../runtime/appcds/ParallelLoadTest.java | 64 + .../appcds/PrintSharedArchiveAndExit.java | 148 + .../runtime/appcds/ProhibitedPackage.java | 101 + .../runtime/appcds/ProtectionDomain.java | 73 + .../runtime/appcds/RewriteBytecodesTest.java | 65 + .../appcds/SharedArchiveConsistency.java | 386 + .../runtime/appcds/SharedArchiveFile.java | 83 + .../runtime/appcds/SharedBaseAddress.java | 65 + .../jtreg/runtime/appcds/SharedPackages.java | 79 + .../jtreg/runtime/appcds/SignedJar.java | 69 + .../runtime/appcds/SpecifySysLoaderProp.java | 107 + .../jtreg/runtime/appcds/TestCommon.java | 339 + .../runtime/appcds/TraceLongClasspath.java | 105 + .../jtreg/runtime/appcds/UseAppCDS.java | 228 + .../jtreg/runtime/appcds/UseAppCDS_Test.java | 30 + .../jtreg/runtime/appcds/VerifierTest.java | 343 + .../jtreg/runtime/appcds/VerifierTest_0.java | 38 + .../jtreg/runtime/appcds/VerifierTest_1A.java | 38 + .../jtreg/runtime/appcds/VerifierTest_1B.java | 38 + .../jtreg/runtime/appcds/VerifierTest_2.java | 38 + .../jtreg/runtime/appcds/WideIloadTest.java | 50 + .../jtreg/runtime/appcds/WrongClasspath.java | 56 + .../appcds/XShareAutoWithChangedJar.java | 55 + .../CheckCachedResolvedReferences.java | 69 + .../CheckCachedResolvedReferencesApp.java | 77 + .../DumpTimeVerifyFailure.config.txt | 3 + .../cacheObject/DumpTimeVerifyFailure.java | 63 + .../appcds/cacheObject/GCStress.config.txt | 3 + .../appcds/cacheObject/GCStressApp.java | 93 + .../appcds/cacheObject/GCStressTest.java | 61 + .../cacheObject/InstrumentationAgent.mf | 5 + .../appcds/cacheObject/MyException.java | 15 +- .../runtime/appcds/cacheObject/MyOuter.java | 43 + .../appcds/cacheObject/OpenArchiveRegion.java | 63 + .../cacheObject/RangeNotWithinHeap.java | 72 + .../appcds/cacheObject/RedefineClassApp.java | 149 + .../appcds/cacheObject/RedefineClassTest.java | 105 + .../appcds/customLoader/ClassListFormatA.java | 138 + .../appcds/customLoader/ClassListFormatB.java | 74 + .../customLoader/ClassListFormatBase.java | 82 + .../appcds/customLoader/ClassListFormatC.java | 76 + .../appcds/customLoader/ClassListFormatD.java | 85 + .../appcds/customLoader/ClassListFormatE.java | 111 + .../appcds/customLoader/CustomLoaderApp.java | 110 + .../appcds/customLoader/HelloCustom.java | 75 + .../customLoader/LoaderSegregationTest.java | 125 + .../appcds/customLoader/ParallelTestBase.java | 99 + .../customLoader/ParallelTestMultiFP.java | 44 + .../customLoader/ParallelTestSingleFP.java | 44 + .../ProhibitedPackageNamesTest.java | 60 + .../appcds/customLoader/ProtectionDomain.java | 61 + .../SameNameInTwoLoadersTest.java | 94 + .../customLoader/UnintendedLoadersTest.java | 76 + .../UnloadUnregisteredLoaderTest.java | 82 + .../customLoader/UnsupportedPlatforms.java | 67 + .../test-classes/CustomInterface2_ia.java | 25 + .../test-classes/CustomInterface2_ib.java | 25 + .../test-classes/CustomLoadee.java | 29 + .../test-classes/CustomLoadee2.java | 29 + .../test-classes/CustomLoadee3.java | 29 + .../test-classes/CustomLoadee3Child.java | 29 + .../customLoader/test-classes/Hello.java | 56 + .../test-classes/InProhibitedPkg.java | 33 + .../customLoader/test-classes/LoaderAPI.mf | 12 + .../test-classes/LoaderSegregation.java | 99 + .../test-classes/OnlyBuiltin.java | 28 + .../test-classes/OnlyUnregistered.java | 28 + .../customLoader/test-classes/ProtDomain.java | 55 + .../SameNameUnrelatedLoaders.java | 101 + .../test-classes/SimpleHello.java | 29 + .../test-classes/UnintendedLoaders.java | 54 + .../UnloadUnregisteredLoader.java | 69 + .../runtime/appcds/javaldr/ArrayTest.java | 78 + .../appcds/javaldr/ArrayTestHelper.java | 46 + .../appcds/javaldr/CheckAnonymousClass.java | 75 + .../runtime/appcds/javaldr/GCDuringDump.java | 80 + .../javaldr/GCDuringDumpTransformer.java | 72 + .../appcds/javaldr/GCDuringDumpTransformer.mf | 5 + .../javaldr/GCSharedStringsDuringDump.java | 131 + .../javaldr/GCSharedStringsDuringDumpWb.java | 45 + .../CheckUnsupportedDumpingOptions.java | 102 + .../appcds/jigsaw/JigsawOptionsCombo.java | 216 + .../jigsaw/PatchModule/AppClassInCP.java | 104 + .../jigsaw/PatchModule/CustomPackage.java | 83 + .../PatchModule/MismatchedPatchModule.java | 132 + .../appcds/jigsaw/PatchModule/PatchDir.java | 73 + .../jigsaw/PatchModule/PatchJavaBase.java | 73 + .../appcds/jigsaw/PatchModule/PatchMain.java | 33 + .../appcds/jigsaw/PatchModule/Simple.java | 81 + .../PatchModule/SubClassOfPatchedClass.java | 105 + .../appcds/jigsaw/PatchModule/TwoJars.java | 100 + .../classpathtests/BootAppendTests.java | 256 + .../jigsaw/classpathtests/ClassPathTests.java | 240 + .../DummyClassesInBootClassPath.java | 88 + .../EmptyClassInBootClassPath.java | 106 + .../src/com/sun/tools/javac/Main.jasm | 46 + .../src/com/sun/tools/javac/Main2.jasm | 46 + .../UnsupportedDataTypeException2.jasm | 46 + .../classpathtests/src/jdk/test/Main.java | 122 + .../src/sun/nio/cs/ext/MyClass.java | 31 + .../src/sun/nio/cs/ext1/MyClass.java | 31 + .../jigsaw/limitmods/LimitModsHelper.java | 93 + .../jigsaw/limitmods/LimitModsTests.java | 164 + .../jigsaw/overridetests/OverrideTests.java | 238 + .../UnsupportedDataTypeException.java | 36 + .../src/java.activation/module-info.java | 28 + .../com/sun/tools/javac/Main.java | 31 + .../src/jdk.compiler/module-info.java | 28 + .../overridetests/src/test/jdk/test/Main.java | 101 + .../overridetests/src/test/module-info.java | 28 + .../appcds/jvmti/ClassFileLoadHook.java | 93 + .../appcds/jvmti/ClassFileLoadHookTest.java | 100 + .../appcds/jvmti/InstrumentationAgent.mf | 5 + .../appcds/jvmti/InstrumentationApp.java | 220 + .../InstrumentationClassFileTransformer.java | 57 + ...mentationRegisterClassFileTransformer.java | 45 + .../appcds/jvmti/InstrumentationTest.java | 278 + .../ParallelClassesTransform.java | 70 + .../ParallelLoadAndTransformTest.java | 88 + .../TransformInterfaceImplementorAppCDS.java | 43 + .../TransformRelatedClassesAppCDS.java | 204 + .../TransformSuperSubAppCDS.java | 43 + .../appcds/redefineClass/RedefineBasic.java | 105 + .../redefineClass/RedefineBasicTest.java | 76 + .../RedefineRunningMethods_Shared.java | 82 + .../RedefineRunningMethods_SharedHelper.java | 49 + .../appcds/sharedStrings/ExerciseGC.java | 49 + .../appcds/sharedStrings/ExtraSharedInput.txt | 7 + .../appcds/sharedStrings/FlagCombo.java | 58 + .../appcds/sharedStrings/HelloString.java | 32 + .../appcds/sharedStrings/HelloStringGC.java | 69 + .../appcds/sharedStrings/HelloStringPlus.java | 76 + .../sharedStrings/IncompatibleOptions.java | 145 + .../sharedStrings/InternSharedString.java | 56 + .../sharedStrings/InternStringTest.java | 79 + .../sharedStrings/InvalidFileFormat.java | 73 + .../appcds/sharedStrings/LargePages.java | 53 + .../sharedStrings/LockSharedStrings.java | 57 + .../appcds/sharedStrings/LockStringTest.java | 68 + .../sharedStrings/LockStringValueTest.java | 61 + .../sharedStrings/SharedStringsBasic.java | 78 + .../sharedStrings/SharedStringsBasic.txt | 60 + .../sharedStrings/SharedStringsBasicPlus.java | 50 + .../sharedStrings/SharedStringsStress.java | 71 + .../sharedStrings/SharedStringsUtils.java | 143 + .../appcds/sharedStrings/SharedStringsWb.java | 40 + .../sharedStrings/SharedStringsWbTest.java | 53 + .../appcds/sharedStrings/SysDictCrash.java | 63 + .../invalidFormat/CorruptDataLine.txt | 60 + .../invalidFormat/InvalidDataType.txt | 60 + .../invalidFormat/InvalidHeader.txt | 60 + .../invalidFormat/InvalidString.txt | 6 + .../invalidFormat/InvalidStringFormat.txt | 60 + .../invalidFormat/InvalidSymbol.txt | 12 + .../invalidFormat/InvalidSymbolFormat.txt | 11 + .../invalidFormat/InvalidVersion.txt | 60 + .../invalidFormat/OverflowPrefix.txt | 11 + .../invalidFormat/TruncatedString.txt | 10 + .../invalidFormat/UnrecognizedPrefix.txt | 11 + .../appcds/test-classes/ArrayListTest.java | 41 + .../BootClassPathAppendHelper.java | 42 + .../jtreg/runtime/appcds/test-classes/C1.java | 28 + .../jtreg/runtime/appcds/test-classes/C2.java | 28 + .../appcds/test-classes/CheckIfShared.java | 40 + .../runtime/appcds/test-classes/Child.java | 25 + .../runtime/appcds/test-classes/CpAttr1.java | 38 + .../runtime/appcds/test-classes/CpAttr2.java | 25 + .../runtime/appcds/test-classes/CpAttr3.java | 26 + .../runtime/appcds/test-classes/CpAttr4.java | 28 + .../runtime/appcds/test-classes/CpAttr5.java | 25 + .../appcds/test-classes/DummyClassHelper.java | 58 + .../appcds/test-classes/EmptyClassHelper.java | 59 + .../test-classes/FieldAnnotationsApp.java | 58 + .../appcds/test-classes/ForNameTest.java | 45 + .../runtime/appcds/test-classes/Greet.java | 30 + .../runtime/appcds/test-classes/Hello.java | 29 + .../runtime/appcds/test-classes/HelloExt.java | 59 + .../appcds/test-classes/HelloExtApp.java | 29 + .../appcds/test-classes/HelloExtExt.java | 27 + .../appcds/test-classes/HelloMore.java | 30 + .../runtime/appcds/test-classes/HelloWB.java | 37 + .../jtreg/runtime/appcds/test-classes/Hi.java | 35 + .../runtime/appcds/test-classes/Iloadw.jasm | 37 + .../appcds/test-classes/IloadwMain.java | 35 + .../test-classes/JimageClassPackage.java | 95 + .../test-classes/JimageClassProtDomain.java | 74 + .../runtime/appcds/test-classes/JvmtiApp.java | 105 + .../appcds/test-classes/MethodNoReturn.jasm | 93 + .../appcds/test-classes/MissingSuper.java | 49 + .../appcds/test-classes/MultiProcClass.java | 66 + .../appcds/test-classes/MyAnnotation.java | 36 + .../test-classes/PackageSealingTest.java | 52 + .../appcds/test-classes/PackageTest.java | 56 + .../appcds/test-classes/ParallelClasses.java | 64 + .../appcds/test-classes/ParallelLoad.java | 220 + .../appcds/test-classes/Prohibited.jasm | 30 + .../appcds/test-classes/ProhibitedHelper.java | 57 + .../appcds/test-classes/ProtDomain.java | 53 + .../appcds/test-classes/ProtDomainB.java | 53 + .../appcds/test-classes/ReportMyLoader.java | 31 + .../appcds/test-classes/RewriteBytecodes.java | 55 + .../runtime/appcds/test-classes/Super.java | 29 + .../appcds/test-classes/TestClassLoader.java | 42 + .../test-classes/TrySwitchMyLoader.java | 36 + .../runtime/appcds/test-classes/Util.java | 156 + .../appcds/test-classes/VerifierTest0.java | 75 + .../com/sun/tools/javac/Main.jasm | 30 + .../runtime/appcds/test-classes/cpattr1.mf | 3 + .../appcds/test-classes/cpattr1_long.mf | 18 + .../runtime/appcds/test-classes/cpattr2.mf | 4 + .../runtime/appcds/test-classes/cpattr3.mf | 2 + .../runtime/appcds/test-classes/cpattr4.mf | 2 + .../appcds/test-classes/cpattr5_extra_long.mf | 3 + .../test-classes/java/net/HttpCookie.jasm | 37 + .../javax/activation/MimeType.jasm | 37 + .../InvalidTransactionException.jasm | 37 + .../jdk/dynalink/DynamicLinker.jasm | 37 + .../appcds/test-classes/package_seal.mf | 6 + 265 files changed, 31331 insertions(+), 134 deletions(-) create mode 100644 src/hotspot/share/classfile/sharedClassUtil.cpp create mode 100644 src/hotspot/share/classfile/systemDictionaryShared.cpp create mode 100644 src/hotspot/share/prims/cdsoffsets.cpp create mode 100644 src/hotspot/share/prims/cdsoffsets.hpp create mode 100644 test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java create mode 100644 test/hotspot/jtreg/runtime/appcds/AppendClasspath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java create mode 100644 test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java create mode 100644 test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java create mode 100644 test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java create mode 100644 test/hotspot/jtreg/runtime/appcds/CompilerUtils.java create mode 100644 test/hotspot/jtreg/runtime/appcds/DumpClassList.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java create mode 100644 test/hotspot/jtreg/runtime/appcds/HelloExtTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/HelloTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java create mode 100644 test/hotspot/jtreg/runtime/appcds/JarBuilder.java create mode 100644 test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java create mode 100644 test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java create mode 100644 test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java create mode 100644 test/hotspot/jtreg/runtime/appcds/OldClassTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/PackageSealing.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java create mode 100644 test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SharedPackages.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SignedJar.java create mode 100644 test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/TestCommon.java create mode 100644 test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/UseAppCDS.java create mode 100644 test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java create mode 100644 test/hotspot/jtreg/runtime/appcds/VerifierTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java create mode 100644 test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java create mode 100644 test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java create mode 100644 test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java create mode 100644 test/hotspot/jtreg/runtime/appcds/WideIloadTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/WrongClasspath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf rename src/hotspot/share/classfile/vmSymbols_ext.hpp => test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java (79%) create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java create mode 100644 test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java create mode 100644 test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java create mode 100644 test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java create mode 100644 test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java create mode 100644 test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java create mode 100644 test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/C1.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/C2.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Child.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Super.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/Util.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm create mode 100644 test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf diff --git a/src/hotspot/share/classfile/classListParser.cpp b/src/hotspot/share/classfile/classListParser.cpp index 8f7be316b50..ed7fa514dcc 100644 --- a/src/hotspot/share/classfile/classListParser.cpp +++ b/src/hotspot/share/classfile/classListParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -23,13 +23,32 @@ */ #include "precompiled.hpp" +#include "jvm.h" +#include "jimage.hpp" #include "classfile/classListParser.hpp" -#include "runtime/os.hpp" -#include "runtime/java.hpp" +#include "classfile/classLoaderExt.hpp" +#include "classfile/sharedClassUtil.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "memory/metaspaceShared.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/fieldType.hpp" +#include "runtime/javaCalls.hpp" +#include "utilities/defaultStream.hpp" +#include "utilities/hashtable.inline.hpp" +#include "utilities/macros.hpp" + +ClassListParser* ClassListParser::_instance = NULL; ClassListParser::ClassListParser(const char* file) { + assert(_instance == NULL, "must be singleton"); + _instance = this; _classlist_file = file; _file = fopen(file, "r"); + _line_no = 0; + _interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, true); + if (_file == NULL) { char errmsg[JVM_MAXPATHLEN]; os::lasterror(errmsg, JVM_MAXPATHLEN); @@ -41,6 +60,7 @@ ClassListParser::~ClassListParser() { if (_file) { fclose(_file); } + _instance = NULL; } bool ClassListParser::parse_one_line() { @@ -48,10 +68,10 @@ bool ClassListParser::parse_one_line() { if (fgets(_line, sizeof(_line), _file) == NULL) { return false; } - int line_len = (int)strlen(_line); - if (line_len > _max_allowed_line_len) { - tty->print_cr("input line too long (must be no longer than %d chars)", _max_allowed_line_len); - vm_exit_during_initialization("Loading classlist failed"); + ++ _line_no; + _line_len = (int)strlen(_line); + if (_line_len > _max_allowed_line_len) { + error("input line too long (must be no longer than %d chars)", _max_allowed_line_len); } if (*_line == '#') { // comment continue; @@ -59,8 +79,378 @@ bool ClassListParser::parse_one_line() { break; } - // Remove trailing \r\n - _line[strcspn(_line, "\r\n")] = 0; + _id = _unspecified; + _super = _unspecified; + _interfaces->clear(); + _source = NULL; + _interfaces_specified = false; + + { + int len = (int)strlen(_line); + int i; + // Replace \t\r\n with ' ' + for (i=0; i<len; i++) { + if (_line[i] == '\t' || _line[i] == '\r' || _line[i] == '\n') { + _line[i] = ' '; + } + } + + // Remove trailing newline/space + while (len > 0) { + if (_line[len-1] == ' ') { + _line[len-1] = '\0'; + len --; + } else { + break; + } + } + _line_len = len; + _class_name = _line; + } + + if ((_token = strchr(_line, ' ')) == NULL) { + // No optional arguments are specified. + return true; + } + + // Mark the end of the name, and go to the next input char + *_token++ = '\0'; + + while (*_token) { + skip_whitespaces(); + + if (parse_int_option("id:", &_id)) { + continue; + } else if (parse_int_option("super:", &_super)) { + check_already_loaded("Super class", _super); + continue; + } else if (skip_token("interfaces:")) { + int i; + while (try_parse_int(&i)) { + check_already_loaded("Interface", i); + _interfaces->append(i); + } + } else if (skip_token("source:")) { + skip_whitespaces(); + _source = _token; + char* s = strchr(_token, ' '); + if (s == NULL) { + break; // end of input line + } else { + *s = '\0'; // mark the end of _source + _token = s+1; + } + } else { + error("Unknown input"); + } + } + + // if src is specified + // id super interfaces must all be specified + // loader may be specified + // else + // # the class is loaded from classpath + // id may be specified + // super, interfaces, loader must not be specified return true; } +void ClassListParser::skip_whitespaces() { + while (*_token == ' ' || *_token == '\t') { + _token ++; + } +} + +void ClassListParser::skip_non_whitespaces() { + while (*_token && *_token != ' ' && *_token != '\t') { + _token ++; + } +} + +void ClassListParser::parse_int(int* value) { + skip_whitespaces(); + if (sscanf(_token, "%i", value) == 1) { + skip_non_whitespaces(); + if (*value < 0) { + error("Error: negative integers not allowed (%d)", *value); + } + } else { + error("Error: expected integer"); + } +} + +bool ClassListParser::try_parse_int(int* value) { + skip_whitespaces(); + if (sscanf(_token, "%i", value) == 1) { + skip_non_whitespaces(); + return true; + } + return false; +} + +bool ClassListParser::skip_token(const char* option_name) { + size_t len = strlen(option_name); + if (strncmp(_token, option_name, len) == 0) { + _token += len; + return true; + } else { + return false; + } +} + +bool ClassListParser::parse_int_option(const char* option_name, int* value) { + if (skip_token(option_name)) { + if (*value != _unspecified) { + error("%s specified twice", option_name); + } else { + parse_int(value); + return true; + } + } + return false; +} + +void ClassListParser::print_specified_interfaces() { + const int n = _interfaces->length(); + jio_fprintf(defaultStream::error_stream(), "Currently specified interfaces[%d] = {\n", n); + for (int i=0; i<n; i++) { + InstanceKlass* k = lookup_class_by_id(_interfaces->at(i)); + jio_fprintf(defaultStream::error_stream(), " %4d = %s\n", _interfaces->at(i), k->name()->as_klass_external_name()); + } + jio_fprintf(defaultStream::error_stream(), "}\n"); +} + +void ClassListParser::print_actual_interfaces(InstanceKlass *ik) { + int n = ik->local_interfaces()->length(); + jio_fprintf(defaultStream::error_stream(), "Actual interfaces[%d] = {\n", n); + for (int i = 0; i < n; i++) { + InstanceKlass* e = InstanceKlass::cast(ik->local_interfaces()->at(i)); + jio_fprintf(defaultStream::error_stream(), " %s\n", e->name()->as_klass_external_name()); + } + jio_fprintf(defaultStream::error_stream(), "}\n"); +} + +void ClassListParser::error(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + int error_index = _token - _line; + if (error_index >= _line_len) { + error_index = _line_len - 1; + } + if (error_index < 0) { + error_index = 0; + } + + jio_fprintf(defaultStream::error_stream(), + "An error has occurred while processing class list file %s %d:%d.\n", + _classlist_file, _line_no, (error_index + 1)); + jio_vfprintf(defaultStream::error_stream(), msg, ap); + + if (_line_len <= 0) { + jio_fprintf(defaultStream::error_stream(), "\n"); + } else { + jio_fprintf(defaultStream::error_stream(), ":\n"); + for (int i=0; i<_line_len; i++) { + char c = _line[i]; + if (c == '\0') { + jio_fprintf(defaultStream::error_stream(), "%s", " "); + } else { + jio_fprintf(defaultStream::error_stream(), "%c", c); + } + } + jio_fprintf(defaultStream::error_stream(), "\n"); + for (int i=0; i<error_index; i++) { + jio_fprintf(defaultStream::error_stream(), "%s", " "); + } + jio_fprintf(defaultStream::error_stream(), "^\n"); + } + + vm_exit_during_initialization("class list format error.", NULL); + va_end(ap); +} + +// This function is used for loading classes for customized class loaders +// during archive dumping. +InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) { +#if !((defined(LINUX) && defined(X86) && defined(_LP64)) || \ + (defined(SOLARIS) && defined(_LP64))) + // The only supported platforms are: (1) Linux/AMD64; (2) Solaris/64-bit + error("AppCDS custom class loaders not supported on this platform"); +#endif + + assert(UseAppCDS, "must be"); + if (!is_super_specified()) { + error("If source location is specified, super class must be also specified"); + } + if (!is_id_specified()) { + error("If source location is specified, id must be also specified"); + } + InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, THREAD); + + if (strncmp(_class_name, "java/", 5) == 0) { + log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s", + _class_name, _source); + return NULL; + } + + if (k != NULL) { + if (k->local_interfaces()->length() != _interfaces->length()) { + print_specified_interfaces(); + print_actual_interfaces(k); + error("The number of interfaces (%d) specified in class list does not match the class file (%d)", + _interfaces->length(), k->local_interfaces()->length()); + } + + if (!SystemDictionaryShared::add_non_builtin_klass(class_name, ClassLoaderData::the_null_class_loader_data(), + k, THREAD)) { + error("Duplicated class %s", _class_name); + } + + // This tells JVM_FindLoadedClass to not find this class. + k->set_shared_classpath_index(UNREGISTERED_INDEX); + } + + return k; +} + +InstanceKlass* ClassListParser::load_current_class(TRAPS) { + TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); + + InstanceKlass *klass = NULL; + if (!is_loading_from_source()) { + if (is_super_specified()) { + error("If source location is not specified, super class must not be specified"); + } + if (are_interfaces_specified()) { + error("If source location is not specified, interface(s) must not be specified"); + } + + bool non_array = !FieldType::is_array(class_name_symbol); + + Handle s = java_lang_String::create_from_symbol(class_name_symbol, CHECK_0); + // Translate to external class name format, i.e., convert '/' chars to '.' + Handle string = java_lang_String::externalize_classname(s, CHECK_0); + JavaValue result(T_OBJECT); + InstanceKlass* spec_klass = non_array ? + SystemDictionary::ClassLoader_klass() : SystemDictionary::Class_klass(); + Symbol* method_name = non_array ? + vmSymbols::loadClass_name() : vmSymbols::forName_name(); + Handle loader = Handle(THREAD, SystemDictionary::java_system_loader()); + + if (non_array) { + JavaCalls::call_virtual(&result, + loader, //SystemDictionary::java_system_loader(), + spec_klass, + method_name, //vmSymbols::loadClass_name(), + vmSymbols::string_class_signature(), + string, + THREAD); + } else { + JavaCalls::call_static(&result, + spec_klass, + method_name, + vmSymbols::string_class_signature(), + string, + CHECK_NULL); + } + assert(result.get_type() == T_OBJECT, "just checking"); + oop obj = (oop) result.get_jobject(); + if (!HAS_PENDING_EXCEPTION && (obj != NULL)) { + if (non_array) { + klass = InstanceKlass::cast(java_lang_Class::as_Klass(obj)); + } else { + klass = static_cast<InstanceKlass*>(java_lang_Class::array_klass_acquire(obj)); + } + } else { // load classes in bootclasspath/a + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + } + + if (non_array) { + Klass* k = SystemDictionary::resolve_or_null(class_name_symbol, CHECK_NULL); + if (k != NULL) { + klass = InstanceKlass::cast(k); + } else { + if (!HAS_PENDING_EXCEPTION) { + THROW_NULL(vmSymbols::java_lang_ClassNotFoundException()); + } + } + } + } + } else { + // If "source:" tag is specified, all super class and super interfaces must be specified in the + // class list file. + if (UseAppCDS) { + klass = load_class_from_source(class_name_symbol, CHECK_NULL); + } + } + + if (klass != NULL && is_id_specified()) { + int id = this->id(); + SystemDictionaryShared::update_shared_entry(klass, id); + InstanceKlass* old = table()->lookup(id); + if (old != NULL && old != klass) { + error("Duplicated ID %d for class %s", id, _class_name); + } + table()->add(id, klass); + } + + return klass; +} + +bool ClassListParser::is_loading_from_source() { + return (_source != NULL); +} + +InstanceKlass* ClassListParser::lookup_class_by_id(int id) { + InstanceKlass* klass = table()->lookup(id); + if (klass == NULL) { + error("Class ID %d has not been defined", id); + } + return klass; +} + + +InstanceKlass* ClassListParser::lookup_super_for_current_class(Symbol* super_name) { + if (!is_loading_from_source()) { + return NULL; + } + + InstanceKlass* k = lookup_class_by_id(super()); + if (super_name != k->name()) { + error("The specified super class %s (id %d) does not match actual super class %s", + k->name()->as_klass_external_name(), super(), + super_name->as_klass_external_name()); + } + return k; +} + +InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* interface_name) { + if (!is_loading_from_source()) { + return NULL; + } + + const int n = _interfaces->length(); + if (n == 0) { + error("Class %s implements the interface %s, but no interface has been specified in the input line", + _class_name, interface_name->as_klass_external_name()); + ShouldNotReachHere(); + } + + int i; + for (i=0; i<n; i++) { + InstanceKlass* k = lookup_class_by_id(_interfaces->at(i)); + if (interface_name == k->name()) { + return k; + } + } + + // interface_name is not specified by the "interfaces:" keyword. + print_specified_interfaces(); + error("The interface %s implemented by class %s does not match any of the specified interface IDs", + interface_name->as_klass_external_name(), _class_name); + ShouldNotReachHere(); + return NULL; +} + diff --git a/src/hotspot/share/classfile/classListParser.hpp b/src/hotspot/share/classfile/classListParser.hpp index 912ae3175a3..e6d48f41c8d 100644 --- a/src/hotspot/share/classfile/classListParser.hpp +++ b/src/hotspot/share/classfile/classListParser.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -27,30 +27,122 @@ #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashtable.hpp" + +class CDSClassInfo; + +// Look up from ID -> InstanceKlass* +class ID2KlassTable : public Hashtable<InstanceKlass*, mtClass> { +public: + ID2KlassTable() : Hashtable<InstanceKlass*, mtClass>(1987, sizeof(HashtableEntry<InstanceKlass*, mtClass>)) { } + void add(int id, InstanceKlass* klass) { + unsigned int hash = (unsigned int)id; + HashtableEntry<InstanceKlass*, mtClass>* entry = new_entry(hash, klass); + add_entry(hash_to_index(hash), entry); + } + + InstanceKlass* lookup(int id) { + unsigned int hash = (unsigned int)id; + int index = hash_to_index(id); + for (HashtableEntry<InstanceKlass*, mtClass>* e = bucket(index); e != NULL; e = e->next()) { + if (e->hash() == hash) { + return e->literal(); + } + } + return NULL; + } +}; class ClassListParser : public StackObj { enum { + _unspecified = -999, + // Max number of bytes allowed per line in the classlist. - // Theoretically Java class names could be 65535 bytes in length. In reality, + // Theoretically Java class names could be 65535 bytes in length. Also, an input line + // could have a very long path name up to JVM_MAXPATHLEN bytes in length. In reality, // 4K bytes is more than enough. _max_allowed_line_len = 4096, _line_buf_extra = 10, // for detecting input too long _line_buf_size = _max_allowed_line_len + _line_buf_extra }; + static ClassListParser* _instance; // the singleton. const char* _classlist_file; FILE* _file; - char _line[_line_buf_size]; // The buffer that holds the current line. + ID2KlassTable _id2klass_table; + + // The following field contains information from the *current* line being + // parsed. + char _line[_line_buf_size]; // The buffer that holds the current line. Some characters in + // the buffer may be overwritten by '\0' during parsing. + int _line_len; // Original length of the input line. + int _line_no; // Line number for current line being parsed + const char* _class_name; + int _id; + int _super; + GrowableArray<int>* _interfaces; + bool _interfaces_specified; + const char* _source; + + bool parse_int_option(const char* option_name, int* value); + InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS); + ID2KlassTable *table() { + return &_id2klass_table; + } + InstanceKlass* lookup_class_by_id(int id); + void print_specified_interfaces(); + void print_actual_interfaces(InstanceKlass *ik); public: ClassListParser(const char* file); ~ClassListParser(); + + static ClassListParser* instance() { + return _instance; + } bool parse_one_line(); + char* _token; + void error(const char* msg, ...); + void parse_int(int* value); + bool try_parse_int(int* value); + bool skip_token(const char* option_name); + void skip_whitespaces(); + void skip_non_whitespaces(); + + bool is_id_specified() { + return _id != _unspecified; + } + bool is_super_specified() { + return _super != _unspecified; + } + bool are_interfaces_specified() { + return _interfaces->length() > 0; + } + int id() { + assert(is_id_specified(), "do not query unspecified id"); + return _id; + } + int super() { + assert(is_super_specified(), "do not query unspecified super"); + return _super; + } + void check_already_loaded(const char* which, int id) { + if (_id2klass_table.lookup(id) == NULL) { + error("%s id %d is not yet loaded", which, id); + } + } const char* current_class_name() { - return _line; + return _class_name; } + + InstanceKlass* load_current_class(TRAPS); + + bool is_loading_from_source(); + + // Look up the super or interface of the current class being loaded + // (in this->load_current_class()). + InstanceKlass* lookup_super_for_current_class(Symbol* super_name); + InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name); }; - - -#endif // SHARE_VM_MEMORY_CLASSLISTPARSER_HPP +#endif diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index 44efabec083..a7256bb8e72 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -23,14 +23,329 @@ */ #include "precompiled.hpp" +#include "classfile/classFileParser.hpp" +#include "classfile/classFileStream.hpp" #include "classfile/classListParser.hpp" +#include "classfile/classLoader.hpp" #include "classfile/classLoaderExt.hpp" -#include "classfile/symbolTable.hpp" -#include "classfile/systemDictionary.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/klassFactory.hpp" +#include "classfile/sharedClassUtil.hpp" +#include "classfile/sharedPathsMiscInfo.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmSymbols.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/filemap.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "runtime/arguments.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/os.hpp" +#include "services/threadService.hpp" +#include "utilities/stringUtils.hpp" +jshort ClassLoaderExt::_app_paths_start_index = ClassLoaderExt::max_classpath_index; +bool ClassLoaderExt::_has_app_classes = false; +bool ClassLoaderExt::_has_platform_classes = false; + +void ClassLoaderExt::setup_app_search_path() { + assert(DumpSharedSpaces, "this function is only used with -Xshare:dump and -XX:+UseAppCDS"); + _app_paths_start_index = ClassLoader::num_boot_classpath_entries(); + char* app_class_path = os::strdup(Arguments::get_appclasspath()); + + if (strcmp(app_class_path, ".") == 0) { + // This doesn't make any sense, even for AppCDS, so let's skip it. We + // don't want to throw an error here because -cp "." is usually assigned + // by the launcher when classpath is not specified. + trace_class_path("app loader class path (skipped)=", app_class_path); + } else { + trace_class_path("app loader class path=", app_class_path); + shared_paths_misc_info()->add_app_classpath(app_class_path); + ClassLoader::setup_app_search_path(app_class_path); + } +} + +char* ClassLoaderExt::read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS) { + const char* name = "META-INF/MANIFEST.MF"; + char* manifest; + jint size; + + assert(entry->is_jar_file(), "must be"); + manifest = (char*) ((ClassPathZipEntry*)entry )->open_entry(name, &size, true, CHECK_NULL); + + if (manifest == NULL) { // No Manifest + *manifest_size = 0; + return NULL; + } + + + if (clean_text) { + // See http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest + // (1): replace all CR/LF and CR with LF + StringUtils::replace_no_expand(manifest, "\r\n", "\n"); + + // (2) remove all new-line continuation (remove all "\n " substrings) + StringUtils::replace_no_expand(manifest, "\n ", ""); + } + + *manifest_size = (jint)strlen(manifest); + return manifest; +} + +char* ClassLoaderExt::get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size) { + const char* tag = "Class-Path: "; + const int tag_len = (int)strlen(tag); + char* found = NULL; + char* line_start = manifest; + char* end = manifest + manifest_size; + + assert(*end == 0, "must be nul-terminated"); + + while (line_start < end) { + char* line_end = strchr(line_start, '\n'); + if (line_end == NULL) { + // JAR spec require the manifest file to be terminated by a new line. + break; + } + if (strncmp(tag, line_start, tag_len) == 0) { + if (found != NULL) { + // Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java + // If duplicated entries are found, the last one is used. + tty->print_cr("Warning: Duplicate name in Manifest: %s.\n" + "Ensure that the manifest does not have duplicate entries, and\n" + "that blank lines separate individual sections in both your\n" + "manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n", tag, jar_path); + } + found = line_start + tag_len; + assert(found <= line_end, "sanity"); + *line_end = '\0'; + } + line_start = line_end + 1; + } + return found; +} + +void ClassLoaderExt::process_jar_manifest(ClassPathEntry* entry, + bool check_for_duplicates) { + Thread* THREAD = Thread::current(); + ResourceMark rm(THREAD); + jint manifest_size; + char* manifest = read_manifest(entry, &manifest_size, CHECK); + + if (manifest == NULL) { + return; + } + + if (strstr(manifest, "Extension-List:") != NULL) { + tty->print_cr("-Xshare:dump does not support Extension-List in JAR manifest: %s", entry->name()); + vm_exit(1); + } + + char* cp_attr = get_class_path_attr(entry->name(), manifest, manifest_size); + + if (cp_attr != NULL && strlen(cp_attr) > 0) { + trace_class_path("found Class-Path: ", cp_attr); + + char sep = os::file_separator()[0]; + const char* dir_name = entry->name(); + const char* dir_tail = strrchr(dir_name, sep); + int dir_len; + if (dir_tail == NULL) { + dir_len = 0; + } else { + dir_len = dir_tail - dir_name + 1; + } + + // Split the cp_attr by spaces, and add each file + char* file_start = cp_attr; + char* end = file_start + strlen(file_start); + + while (file_start < end) { + char* file_end = strchr(file_start, ' '); + if (file_end != NULL) { + *file_end = 0; + file_end += 1; + } else { + file_end = end; + } + + int name_len = (int)strlen(file_start); + if (name_len > 0) { + ResourceMark rm(THREAD); + char* libname = NEW_RESOURCE_ARRAY(char, dir_len + name_len + 1); + *libname = 0; + strncat(libname, dir_name, dir_len); + strncat(libname, file_start, name_len); + trace_class_path("library = ", libname); + ClassLoader::update_class_path_entry_list(libname, true, false); + } + + file_start = file_end; + } + } +} + +void ClassLoaderExt::setup_search_paths() { + if (UseAppCDS) { + shared_paths_misc_info()->record_app_offset(); + ClassLoaderExt::setup_app_search_path(); + } +} + +Thread* ClassLoaderExt::Context::_dump_thread = NULL; + +bool ClassLoaderExt::check(ClassLoaderExt::Context *context, + const ClassFileStream* stream, + const int classpath_index) { + if (stream != NULL) { + // Ignore any App classes from signed JAR file during CDS archiving + // dumping + if (DumpSharedSpaces && + SharedClassUtil::is_classpath_entry_signed(classpath_index) && + classpath_index >= _app_paths_start_index) { + tty->print_cr("Preload Warning: Skipping %s from signed JAR", + context->class_name()); + return false; + } + if (classpath_index >= _app_paths_start_index) { + _has_app_classes = true; + _has_platform_classes = true; + } + } + + return true; +} + +void ClassLoaderExt::record_result(ClassLoaderExt::Context *context, + Symbol* class_name, + const s2 classpath_index, + InstanceKlass* result, + TRAPS) { + assert(DumpSharedSpaces, "Sanity"); + + // We need to remember where the class comes from during dumping. + oop loader = result->class_loader(); + s2 classloader_type = ClassLoader::BOOT_LOADER; + if (SystemDictionary::is_system_class_loader(loader)) { + classloader_type = ClassLoader::APP_LOADER; + ClassLoaderExt::set_has_app_classes(); + } else if (SystemDictionary::is_platform_class_loader(loader)) { + classloader_type = ClassLoader::PLATFORM_LOADER; + ClassLoaderExt::set_has_platform_classes(); + } + result->set_shared_classpath_index(classpath_index); + result->set_class_loader_type(classloader_type); +} + +void ClassLoaderExt::finalize_shared_paths_misc_info() { + if (UseAppCDS) { + if (!_has_app_classes) { + shared_paths_misc_info()->pop_app(); + } + } +} + +// Load the class of the given name from the location given by path. The path is specified by +// the "source:" in the class list file (see classListParser.cpp), and can be a directory or +// a JAR file. +InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS) { + + assert(name != NULL, "invariant"); + assert(DumpSharedSpaces && UseAppCDS, "this function is only used with -Xshare:dump and -XX:+UseAppCDS"); + ResourceMark rm(THREAD); + const char* class_name = name->as_C_string(); + + const char* file_name = file_name_for_class_name(class_name, + name->utf8_length()); + assert(file_name != NULL, "invariant"); + + // Lookup stream for parsing .class file + ClassFileStream* stream = NULL; + ClassPathEntry* e = find_classpath_entry_from_cache(path, CHECK_NULL); + if (e == NULL) { + return NULL; + } + { + PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(), + ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(), + PerfClassTraceTime::CLASS_LOAD); + stream = e->open_stream(file_name, CHECK_NULL); + } + + if (NULL == stream) { + tty->print_cr("Preload Warning: Cannot find %s", class_name); + return NULL; + } + + assert(stream != NULL, "invariant"); + stream->set_verify(true); + + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + Handle protection_domain; + + InstanceKlass* result = KlassFactory::create_from_stream(stream, + name, + loader_data, + protection_domain, + NULL, // host_klass + NULL, // cp_patches + THREAD); + + if (HAS_PENDING_EXCEPTION) { + tty->print_cr("Preload Error: Failed to load %s", class_name); + return NULL; + } + result->set_shared_classpath_index(UNREGISTERED_INDEX); + SystemDictionaryShared::set_shared_class_misc_info(result, stream); + return result; +} + +struct CachedClassPathEntry { + const char* _path; + ClassPathEntry* _entry; +}; + +static GrowableArray<CachedClassPathEntry>* cached_path_entries = NULL; + +ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path, TRAPS) { + // This is called from dump time so it's single threaded and there's no need for a lock. + assert(DumpSharedSpaces && UseAppCDS, "this function is only used with -Xshare:dump and -XX:+UseAppCDS"); + if (cached_path_entries == NULL) { + cached_path_entries = new (ResourceObj::C_HEAP, mtClass) GrowableArray<CachedClassPathEntry>(20, /*c heap*/ true); + } + CachedClassPathEntry ccpe; + for (int i=0; i<cached_path_entries->length(); i++) { + ccpe = cached_path_entries->at(i); + if (strcmp(ccpe._path, path) == 0) { + if (i != 0) { + // Put recent entries at the beginning to speed up searches. + cached_path_entries->remove_at(i); + cached_path_entries->insert_before(0, ccpe); + } + return ccpe._entry; + } + } + + struct stat st; + if (os::stat(path, &st) != 0) { + // File or directory not found + return NULL; + } + ClassPathEntry* new_entry = NULL; + + new_entry = create_class_path_entry(path, &st, false, false, CHECK_NULL); + if (new_entry == NULL) { + return NULL; + } + ccpe._path = strdup(path); + ccpe._entry = new_entry; + cached_path_entries->insert_before(0, ccpe); + return new_entry; +} Klass* ClassLoaderExt::load_one_class(ClassListParser* parser, TRAPS) { - TempNewSymbol class_name_symbol = SymbolTable::new_symbol(parser->current_class_name(), THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); - return SystemDictionary::resolve_or_null(class_name_symbol, THREAD); + return parser->load_current_class(THREAD); } diff --git a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp index 09cb592b0d6..27e9ce25ef5 100644 --- a/src/hotspot/share/classfile/classLoaderExt.hpp +++ b/src/hotspot/share/classfile/classLoaderExt.hpp @@ -26,65 +26,152 @@ #define SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP #include "classfile/classLoader.hpp" -#include "classfile/systemDictionary.hpp" -#include "oops/instanceKlass.hpp" -#include "runtime/handles.hpp" +#include "utilities/macros.hpp" -class ClassListParser; +CDS_ONLY(class SharedPathsMiscInfoExt;) +CDS_ONLY(class ClassListParser;) class ClassLoaderExt: public ClassLoader { // AllStatic public: - + enum SomeConstants { + max_classpath_index = 0x7fff + }; + // ClassLoaderExt::Context -- + // + // This is used by DumpSharedSpaces only - it enforces the same classloader + // delegation model as would be in run-time. I.e., + // + classes defined by the NULL class loader cannot load classes in the PLATFORM or APP paths. + // + classes defined by the PLATFORM class loader cannot load classes in the APP paths. class Context { + static Thread* _dump_thread; + const char* _class_name; const char* _file_name; public: + const char* class_name() { + return _class_name; + } + const char* file_name() { + return _file_name; + } + Context(const char* class_name, const char* file_name, TRAPS) { + _class_name = class_name; _file_name = file_name; +#if INCLUDE_CDS + if (!DumpSharedSpaces && !UseSharedSpaces) { + // Must not modify _app_paths_start_index if we're not using CDS. + assert(_app_paths_start_index == ClassLoaderExt::max_classpath_index, "must be"); + } +#endif } bool check(const ClassFileStream* stream, const int classpath_index) { - return true; + CDS_ONLY(return ClassLoaderExt::check(this, stream, classpath_index);) + NOT_CDS(return true;) } bool should_verify(int classpath_index) { - return false; + CDS_ONLY(return (classpath_index >= _app_paths_start_index);) + NOT_CDS(return false;) } void record_result(Symbol* class_name, const s2 classpath_index, - InstanceKlass* result, TRAPS) { + InstanceKlass* result, + TRAPS) { #if INCLUDE_CDS - assert(DumpSharedSpaces, "Sanity"); - oop loader = result->class_loader(); - s2 classloader_type = ClassLoader::BOOT_LOADER; - if (SystemDictionary::is_system_class_loader(loader)) { - classloader_type = ClassLoader::APP_LOADER; - ClassLoaderExt::set_has_app_classes(); - } else if (SystemDictionary::is_platform_class_loader(loader)) { - classloader_type = ClassLoader::PLATFORM_LOADER; - ClassLoaderExt::set_has_platform_classes(); - } - result->set_shared_classpath_index(classpath_index); - result->set_class_loader_type(classloader_type); + ClassLoaderExt::record_result(this, class_name, classpath_index, result, THREAD); #endif } - }; + ~Context() { +#if INCLUDE_CDS + if (!DumpSharedSpaces && !UseSharedSpaces) { + // Must not modify app_paths_start_index if we're not using CDS. + assert(_app_paths_start_index == ClassLoaderExt::max_classpath_index, "must be"); + } +#endif + } + }; // end ClassLoaderExt::Context + +private: +#if INCLUDE_CDS + static char* get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size); + static void setup_app_search_path(); // Only when -Xshare:dump + static SharedPathsMiscInfoExt* shared_paths_misc_info() { + return (SharedPathsMiscInfoExt*)_shared_paths_misc_info; + } + static jshort _app_paths_start_index; // index of first app JAR in shared classpath entry table + static bool _has_app_classes; + static bool _has_platform_classes; +#endif + +public: + CDS_ONLY(static void process_jar_manifest(ClassPathEntry* entry, bool check_for_duplicates);) + + // Called by JVMTI code to add boot classpath static void append_boot_classpath(ClassPathEntry* new_entry) { +#if INCLUDE_CDS + if (UseAppCDS) { + warning("UseAppCDS is disabled because bootstrap classpath has been appended"); + UseAppCDS = false; + } +#endif ClassLoader::add_to_boot_append_entries(new_entry); } - static void setup_search_paths() {} - static bool is_boot_classpath(int classpath_index) { - return true; - } - static Klass* load_one_class(ClassListParser* parser, TRAPS); + + static void setup_search_paths() NOT_CDS_RETURN; + #if INCLUDE_CDS - static void set_has_app_classes() {} - static void set_has_platform_classes() {} +private: + static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS); + static ClassPathEntry* find_classpath_entry_from_cache(const char* path, TRAPS); + +public: static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, TRAPS) { - return NULL; + // Remove all the new-line continuations (which wrap long lines at 72 characters, see + // http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest), so + // that the manifest is easier to parse. + return read_manifest(entry, manifest_size, true, THREAD); + } + static char* read_raw_manifest(ClassPathEntry* entry, jint *manifest_size, TRAPS) { + // Do not remove new-line continuations, so we can easily pass it as an argument to + // java.util.jar.Manifest.getManifest() at run-time. + return read_manifest(entry, manifest_size, false, THREAD); + } + + static void finalize_shared_paths_misc_info(); + + static jshort app_paths_start_index() { return _app_paths_start_index; } + + static void init_paths_start_index(jshort app_start) { + _app_paths_start_index = app_start; + } + + static bool is_boot_classpath(int classpath_index) { + return classpath_index < _app_paths_start_index; + } + + static bool has_platform_or_app_classes() { + return _has_app_classes || _has_platform_classes; + } + + static bool check(class ClassLoaderExt::Context *context, + const ClassFileStream* stream, + const int classpath_index); + + static void record_result(class ClassLoaderExt::Context *context, + Symbol* class_name, + const s2 classpath_index, + InstanceKlass* result, 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() { + _has_app_classes = true; + } + static void set_has_platform_classes() { + _has_platform_classes = true; } - static void process_jar_manifest(ClassPathEntry* entry, bool check_for_duplicates) {} #endif }; diff --git a/src/hotspot/share/classfile/sharedClassUtil.cpp b/src/hotspot/share/classfile/sharedClassUtil.cpp new file mode 100644 index 00000000000..4d713cae863 --- /dev/null +++ b/src/hotspot/share/classfile/sharedClassUtil.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/classLoaderExt.hpp" +#include "classfile/dictionary.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/sharedClassUtil.hpp" +#include "classfile/stringTable.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "memory/filemap.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "runtime/arguments.hpp" +#include "runtime/java.hpp" +#include "runtime/os.hpp" + +class ManifestStream: public ResourceObj { + private: + u1* _buffer_start; // Buffer bottom + u1* _buffer_end; // Buffer top (one past last element) + u1* _current; // Current buffer position + + public: + // Constructor + ManifestStream(u1* buffer, int length) : _buffer_start(buffer), + _current(buffer) { + _buffer_end = buffer + length; + } + + static bool is_attr(u1* attr, const char* name) { + return strncmp((const char*)attr, name, strlen(name)) == 0; + } + + static char* copy_attr(u1* value, size_t len) { + char* buf = NEW_RESOURCE_ARRAY(char, len + 1); + strncpy(buf, (char*)value, len); + buf[len] = 0; + return buf; + } + + // The return value indicates if the JAR is signed or not + bool check_is_signed() { + u1* attr = _current; + bool isSigned = false; + while (_current < _buffer_end) { + if (*_current == '\n') { + *_current = '\0'; + u1* value = (u1*)strchr((char*)attr, ':'); + if (value != NULL) { + assert(*(value+1) == ' ', "Unrecognized format" ); + if (strstr((char*)attr, "-Digest") != NULL) { + isSigned = true; + break; + } + } + *_current = '\n'; // restore + attr = _current + 1; + } + _current ++; + } + return isSigned; + } +}; + +void SharedPathsMiscInfoExt::print_path(outputStream* out, int type, const char* path) { + switch(type) { + case APP: + ClassLoader::trace_class_path("Expecting -Djava.class.path=", path); + break; + default: + SharedPathsMiscInfo::print_path(out, type, path); + } +} + +bool SharedPathsMiscInfoExt::check(jint type, const char* path) { + + switch (type) { + case APP: + { + // Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar + size_t len = strlen(path); + const char *appcp = Arguments::get_appclasspath(); + assert(appcp != NULL, "NULL app classpath"); + size_t appcp_len = strlen(appcp); + if (appcp_len < len) { + return fail("Run time APP classpath is shorter than the one at dump time: ", appcp); + } + ResourceMark rm; + char* tmp_path; + if (len == appcp_len) { + tmp_path = (char*)appcp; + } else { + tmp_path = NEW_RESOURCE_ARRAY(char, len + 1); + strncpy(tmp_path, appcp, len); + tmp_path[len] = 0; + } + if (os::file_name_strcmp(path, tmp_path) != 0) { + return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp); + } + if (appcp[len] != '\0' && appcp[len] != os::path_separator()[0]) { + return fail("Dump time APP classpath is not a proper prefix of run time APP classpath: ", appcp); + } + } + break; + default: + return SharedPathsMiscInfo::check(type, path); + } + + return true; +} + +void SharedClassUtil::update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* e, TRAPS) { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*)e; + ResourceMark rm(THREAD); + jint manifest_size; + bool isSigned; + char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK); + if (manifest != NULL) { + ManifestStream* stream = new ManifestStream((u1*)manifest, + manifest_size); + isSigned = stream->check_is_signed(); + if (isSigned) { + ent->_is_signed = true; + } else { + // Copy the manifest into the shared archive + manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK); + Array<u1>* buf = MetadataFactory::new_array<u1>(loader_data, + manifest_size, + THREAD); + char* p = (char*)(buf->data()); + memcpy(p, manifest, manifest_size); + ent->set_manifest(buf); + ent->_is_signed = false; + } + } +} + +void SharedClassUtil::initialize(TRAPS) { + if (UseSharedSpaces) { + int size = FileMapInfo::get_number_of_share_classpaths(); + if (size > 0) { + SystemDictionaryShared::allocate_shared_data_arrays(size, THREAD); + if (!DumpSharedSpaces) { + FileMapHeaderExt* header = (FileMapHeaderExt*)FileMapInfo::current_info()->header(); + ClassLoaderExt::init_paths_start_index(header->_app_paths_start_index); + } + } + } + + if (DumpSharedSpaces) { + if (SharedArchiveConfigFile) { + read_extra_data(SharedArchiveConfigFile, THREAD); + } + } +} + +void SharedClassUtil::read_extra_data(const char* filename, TRAPS) { + HashtableTextDump reader(filename); + reader.check_version("VERSION: 1.0"); + + while (reader.remain() > 0) { + int utf8_length; + int prefix_type = reader.scan_prefix(&utf8_length); + ResourceMark rm(THREAD); + char* utf8_buffer = NEW_RESOURCE_ARRAY(char, utf8_length); + reader.get_utf8(utf8_buffer, utf8_length); + + if (prefix_type == HashtableTextDump::SymbolPrefix) { + SymbolTable::new_symbol(utf8_buffer, utf8_length, THREAD); + } else{ + assert(prefix_type == HashtableTextDump::StringPrefix, "Sanity"); + utf8_buffer[utf8_length] = '\0'; + oop s = StringTable::intern(utf8_buffer, THREAD); + } + } +} + +bool SharedClassUtil::is_classpath_entry_signed(int classpath_index) { + assert(classpath_index >= 0, "Sanity"); + SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*) + FileMapInfo::shared_classpath(classpath_index); + return ent->_is_signed; +} + +void FileMapHeaderExt::populate(FileMapInfo* mapinfo, size_t alignment) { + FileMapInfo::FileMapHeader::populate(mapinfo, alignment); + + ClassLoaderExt::finalize_shared_paths_misc_info(); + _app_paths_start_index = ClassLoaderExt::app_paths_start_index(); + + _verify_local = BytecodeVerificationLocal; + _verify_remote = BytecodeVerificationRemote; + _has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes(); +} + +bool FileMapHeaderExt::validate() { + if (UseAppCDS) { + const char* prop = Arguments::get_property("java.system.class.loader"); + if (prop != NULL) { + warning("UseAppCDS is disabled because the java.system.class.loader property is specified (value = \"%s\"). " + "To enable UseAppCDS, this property must be not be set", prop); + UseAppCDS = false; + } + } + + if (!FileMapInfo::FileMapHeader::validate()) { + return false; + } + + // For backwards compatibility, we don't check the verification setting + // if the archive only contains system classes. + if (_has_platform_or_app_classes && + ((!_verify_local && BytecodeVerificationLocal) || + (!_verify_remote && BytecodeVerificationRemote))) { + FileMapInfo::fail_continue("The shared archive file was created with less restrictive " + "verification setting than the current setting."); + return false; + } + + return true; +} diff --git a/src/hotspot/share/classfile/sharedClassUtil.hpp b/src/hotspot/share/classfile/sharedClassUtil.hpp index 236087f1871..c3b7f603466 100644 --- a/src/hotspot/share/classfile/sharedClassUtil.hpp +++ b/src/hotspot/share/classfile/sharedClassUtil.hpp @@ -27,37 +27,108 @@ #include "classfile/sharedPathsMiscInfo.hpp" #include "memory/filemap.hpp" +#include "classfile/classLoaderExt.hpp" +#include "classfile/dictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "oops/klass.hpp" -class SharedClassUtil : AllStatic { +class FileMapHeaderExt: public FileMapInfo::FileMapHeader { public: + jshort _app_paths_start_index; // Index of first app classpath entry + bool _verify_local; // BytecodeVerificationLocal setting + bool _verify_remote; // BytecodeVerificationRemote setting + bool _has_platform_or_app_classes; // Archive contains app classes - static SharedPathsMiscInfo* allocate_shared_paths_misc_info() { - return new SharedPathsMiscInfo(); + FileMapHeaderExt() { + _has_platform_or_app_classes = true; + } + virtual void populate(FileMapInfo* mapinfo, size_t alignment); + virtual bool validate(); +}; + +// In addition to SharedPathsMiscInfo, the following information is also stored +// +// +// + The value of Arguments::get_appclasspath() used during dumping. +// +class SharedPathsMiscInfoExt : public SharedPathsMiscInfo { +private: + int _app_offset; +public: + enum { + APP = 5 + }; + + virtual const char* type_name(int type) { + switch (type) { + case APP: return "APP"; + default: return SharedPathsMiscInfo::type_name(type); + } } - static SharedPathsMiscInfo* allocate_shared_paths_misc_info(char* buf, int size) { - return new SharedPathsMiscInfo(buf, size); + virtual void print_path(outputStream* out, int type, const char* path); + + SharedPathsMiscInfoExt() : SharedPathsMiscInfo() { + _app_offset = 0; + } + SharedPathsMiscInfoExt(char* buf, int size) : SharedPathsMiscInfo(buf, size) { + _app_offset = 0; } - static FileMapInfo::FileMapHeader* allocate_file_map_header() { - return new FileMapInfo::FileMapHeader(); + virtual bool check(jint type, const char* path); + + void add_app_classpath(const char* path) { + add_path(path, APP); } - static size_t file_map_header_size() { - return sizeof(FileMapInfo::FileMapHeader); + void record_app_offset() { + _app_offset = get_used_bytes(); } - - static size_t shared_class_path_entry_size() { - return sizeof(SharedClassPathEntry); - } - - static void update_shared_classpath(ClassPathEntry *cpe, - SharedClassPathEntry* ent, TRAPS) {} - static void initialize(TRAPS) {} - - inline static bool is_shared_boot_class(Klass* klass) { - return (klass->_shared_class_path_index >= 0); + void pop_app() { + _cur_ptr = _buf_start + _app_offset; + write_jint(0); } }; +class SharedClassPathEntryExt: public SharedClassPathEntry { +public: + //Maniest attributes + bool _is_signed; + void set_manifest(Array<u1>* manifest) { + _manifest = manifest; + } +}; + +class SharedClassUtil : AllStatic { +public: + static SharedPathsMiscInfo* allocate_shared_paths_misc_info() { + return new SharedPathsMiscInfoExt(); + } + + static SharedPathsMiscInfo* allocate_shared_paths_misc_info(char* buf, int size) { + return new SharedPathsMiscInfoExt(buf, size); + } + + static FileMapInfo::FileMapHeader* allocate_file_map_header() { + return new FileMapHeaderExt(); + } + + static size_t file_map_header_size() { + return sizeof(FileMapHeaderExt); + } + + static size_t shared_class_path_entry_size() { + return sizeof(SharedClassPathEntryExt); + } + + static void update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS); + static void initialize(TRAPS); + +private: + static void read_extra_data(const char* filename, TRAPS); + +public: + static bool is_classpath_entry_signed(int classpath_index); +}; + #endif // SHARE_VM_CLASSFILE_SHAREDCLASSUTIL_HPP diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index df4a38f0306..f760c758273 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1087,7 +1087,7 @@ InstanceKlass* SystemDictionary::resolve_from_stream(Symbol* class_name, #if INCLUDE_CDS ResourceMark rm(THREAD); if (DumpSharedSpaces && !class_loader.is_null() && - !ArgumentsExt::using_AppCDS() && strcmp(class_name->as_C_string(), "Unnamed") != 0) { + !UseAppCDS && strcmp(class_name->as_C_string(), "Unnamed") != 0) { // If AppCDS is not enabled, don't define the class at dump time (except for the "Unnamed" // class, which is used by MethodHandles). THROW_MSG_NULL(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string()); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp new file mode 100644 index 00000000000..692ba891823 --- /dev/null +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -0,0 +1,1086 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classFileStream.hpp" +#include "classfile/classListParser.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderExt.hpp" +#include "classfile/compactHashtable.inline.hpp" +#include "classfile/dictionary.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/sharedClassUtil.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" +#include "classfile/verificationType.hpp" +#include "classfile/vmSymbols.hpp" +#include "logging/log.hpp" +#include "memory/allocation.hpp" +#include "memory/filemap.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/metaspaceClosure.hpp" +#include "memory/oopFactory.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.inline.hpp" +#include "oops/objArrayOop.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "utilities/hashtable.inline.hpp" +#include "utilities/stringUtils.hpp" + + +objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL; +objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL; +objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL; + +static Mutex* SharedDictionary_lock = NULL; + +void SystemDictionaryShared::initialize(TRAPS) { + if (_java_system_loader != NULL) { + SharedDictionary_lock = new Mutex(Mutex::leaf, "SharedDictionary_lock", true); + + // These classes need to be initialized before calling get_shared_jar_manifest(), etc. + SystemDictionary::ByteArrayInputStream_klass()->initialize(CHECK); + SystemDictionary::File_klass()->initialize(CHECK); + SystemDictionary::Jar_Manifest_klass()->initialize(CHECK); + SystemDictionary::CodeSource_klass()->initialize(CHECK); + } +} + +oop SystemDictionaryShared::shared_protection_domain(int index) { + return _shared_protection_domains->obj_at(index); +} + +oop SystemDictionaryShared::shared_jar_url(int index) { + return _shared_jar_urls->obj_at(index); +} + +oop SystemDictionaryShared::shared_jar_manifest(int index) { + return _shared_jar_manifests->obj_at(index); +} + + +Handle SystemDictionaryShared::get_shared_jar_manifest(int shared_path_index, TRAPS) { + Handle empty; + Handle manifest ; + if (shared_jar_manifest(shared_path_index) == NULL) { + SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*)FileMapInfo::shared_classpath(shared_path_index); + long size = ent->manifest_size(); + if (size <= 0) { + return empty; // No manifest - return NULL handle + } + + // ByteArrayInputStream bais = new ByteArrayInputStream(buf); + InstanceKlass* bais_klass = SystemDictionary::ByteArrayInputStream_klass(); + Handle bais = bais_klass->allocate_instance_handle(CHECK_(empty)); + { + const char* src = ent->manifest(); + assert(src != NULL, "No Manifest data"); + typeArrayOop buf = oopFactory::new_byteArray(size, CHECK_(empty)); + typeArrayHandle bufhandle(THREAD, buf); + char* dst = (char*)(buf->byte_at_addr(0)); + memcpy(dst, src, (size_t)size); + + JavaValue result(T_VOID); + JavaCalls::call_special(&result, bais, bais_klass, + vmSymbols::object_initializer_name(), + vmSymbols::byte_array_void_signature(), + bufhandle, CHECK_(empty)); + } + + // manifest = new Manifest(bais) + InstanceKlass* manifest_klass = SystemDictionary::Jar_Manifest_klass(); + manifest = manifest_klass->allocate_instance_handle(CHECK_(empty)); + { + JavaValue result(T_VOID); + JavaCalls::call_special(&result, manifest, manifest_klass, + vmSymbols::object_initializer_name(), + vmSymbols::input_stream_void_signature(), + bais, CHECK_(empty)); + } + atomic_set_shared_jar_manifest(shared_path_index, manifest()); + } + + manifest = Handle(THREAD, shared_jar_manifest(shared_path_index)); + assert(manifest.not_null(), "sanity"); + return manifest; +} + +Handle SystemDictionaryShared::get_shared_jar_url(int shared_path_index, TRAPS) { + Handle url_h; + if (shared_jar_url(shared_path_index) == NULL) { + JavaValue result(T_OBJECT); + const char* path = FileMapInfo::shared_classpath_name(shared_path_index); + Handle path_string = java_lang_String::create_from_str(path, CHECK_(url_h)); + Klass* classLoaders_klass = + SystemDictionary::jdk_internal_loader_ClassLoaders_klass(); + JavaCalls::call_static(&result, classLoaders_klass, + vmSymbols::toFileURL_name(), + vmSymbols::toFileURL_signature(), + path_string, CHECK_(url_h)); + + atomic_set_shared_jar_url(shared_path_index, (oop)result.get_jobject()); + } + + url_h = Handle(THREAD, shared_jar_url(shared_path_index)); + assert(url_h.not_null(), "sanity"); + return url_h; +} + +Handle SystemDictionaryShared::get_package_name(Symbol* class_name, TRAPS) { + ResourceMark rm(THREAD); + Handle pkgname_string; + char* pkgname = (char*) ClassLoader::package_from_name((const char*) class_name->as_C_string()); + if (pkgname != NULL) { // Package prefix found + StringUtils::replace_no_expand(pkgname, "/", "."); + pkgname_string = java_lang_String::create_from_str(pkgname, + CHECK_(pkgname_string)); + } + return pkgname_string; +} + +// Define Package for shared app classes from JAR file and also checks for +// package sealing (all done in Java code) +// See http://docs.oracle.com/javase/tutorial/deployment/jar/sealman.html +void SystemDictionaryShared::define_shared_package(Symbol* class_name, + Handle class_loader, + Handle manifest, + Handle url, + TRAPS) { + assert(class_loader == _java_system_loader, "unexpected class loader"); + // get_package_name() returns a NULL handle if the class is in unnamed package + Handle pkgname_string = get_package_name(class_name, CHECK); + if (pkgname_string.not_null()) { + Klass* app_classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass(); + JavaValue result(T_OBJECT); + JavaCallArguments args(3); + args.set_receiver(class_loader); + args.push_oop(pkgname_string); + args.push_oop(manifest); + args.push_oop(url); + JavaCalls::call_virtual(&result, app_classLoader_klass, + vmSymbols::defineOrCheckPackage_name(), + vmSymbols::defineOrCheckPackage_signature(), + &args, + CHECK); + } +} + +// Define Package for shared app/platform classes from named module +void SystemDictionaryShared::define_shared_package(Symbol* class_name, + Handle class_loader, + ModuleEntry* mod_entry, + TRAPS) { + assert(mod_entry != NULL, "module_entry should not be NULL"); + Handle module_handle(THREAD, mod_entry->module()); + + Handle pkg_name = get_package_name(class_name, CHECK); + assert(pkg_name.not_null(), "Package should not be null for class in named module"); + + Klass* classLoader_klass; + if (SystemDictionary::is_system_class_loader(class_loader())) { + classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass(); + } else { + assert(SystemDictionary::is_platform_class_loader(class_loader()), "unexpected classloader"); + classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass(); + } + + JavaValue result(T_OBJECT); + JavaCallArguments args(2); + args.set_receiver(class_loader); + args.push_oop(pkg_name); + args.push_oop(module_handle); + JavaCalls::call_virtual(&result, classLoader_klass, + vmSymbols::definePackage_name(), + vmSymbols::definePackage_signature(), + &args, + CHECK); +} + +// Get the ProtectionDomain associated with the CodeSource from the classloader. +Handle SystemDictionaryShared::get_protection_domain_from_classloader(Handle class_loader, + Handle url, TRAPS) { + // CodeSource cs = new CodeSource(url, null); + InstanceKlass* cs_klass = SystemDictionary::CodeSource_klass(); + Handle cs = cs_klass->allocate_instance_handle(CHECK_NH); + JavaValue void_result(T_VOID); + JavaCalls::call_special(&void_result, cs, cs_klass, + vmSymbols::object_initializer_name(), + vmSymbols::url_code_signer_array_void_signature(), + url, Handle(), CHECK_NH); + + // protection_domain = SecureClassLoader.getProtectionDomain(cs); + Klass* secureClassLoader_klass = SystemDictionary::SecureClassLoader_klass(); + JavaValue obj_result(T_OBJECT); + JavaCalls::call_virtual(&obj_result, class_loader, secureClassLoader_klass, + vmSymbols::getProtectionDomain_name(), + vmSymbols::getProtectionDomain_signature(), + cs, CHECK_NH); + return Handle(THREAD, (oop)obj_result.get_jobject()); +} + +// Returns the ProtectionDomain associated with the JAR file identified by the url. +Handle SystemDictionaryShared::get_shared_protection_domain(Handle class_loader, + int shared_path_index, + Handle url, + TRAPS) { + Handle protection_domain; + if (shared_protection_domain(shared_path_index) == NULL) { + Handle pd = get_protection_domain_from_classloader(class_loader, url, THREAD); + atomic_set_shared_protection_domain(shared_path_index, pd()); + } + + // Acquire from the cache because if another thread beats the current one to + // set the shared protection_domain and the atomic_set fails, the current thread + // needs to get the updated protection_domain from the cache. + protection_domain = Handle(THREAD, shared_protection_domain(shared_path_index)); + assert(protection_domain.not_null(), "sanity"); + return protection_domain; +} + +// Returns the ProtectionDomain associated with the moduleEntry. +Handle SystemDictionaryShared::get_shared_protection_domain(Handle class_loader, + ModuleEntry* mod, TRAPS) { + ClassLoaderData *loader_data = mod->loader_data(); + Handle protection_domain; + if (mod->shared_protection_domain() == NULL) { + Symbol* location = mod->location(); + if (location != NULL) { + Handle url_string = java_lang_String::create_from_symbol( + location, CHECK_(protection_domain)); + JavaValue result(T_OBJECT); + Klass* classLoaders_klass = + SystemDictionary::jdk_internal_loader_ClassLoaders_klass(); + JavaCalls::call_static(&result, classLoaders_klass, vmSymbols::toFileURL_name(), + vmSymbols::toFileURL_signature(), + url_string, CHECK_(protection_domain)); + Handle url = Handle(THREAD, (oop)result.get_jobject()); + + Handle pd = get_protection_domain_from_classloader(class_loader, url, THREAD); + mod->set_shared_protection_domain(loader_data, pd); + } + } + + protection_domain = Handle(THREAD, mod->shared_protection_domain()); + assert(protection_domain.not_null(), "sanity"); + return protection_domain; +} + +// Initializes the java.lang.Package and java.security.ProtectionDomain objects associated with +// the given InstanceKlass. +// Returns the ProtectionDomain for the InstanceKlass. +Handle SystemDictionaryShared::init_security_info(Handle class_loader, InstanceKlass* ik, TRAPS) { + Handle pd; + + if (ik != NULL) { + int index = ik->shared_classpath_index(); + assert(index >= 0, "Sanity"); + SharedClassPathEntryExt* ent = + (SharedClassPathEntryExt*)FileMapInfo::shared_classpath(index); + Symbol* class_name = ik->name(); + + if (ent->is_modules_image()) { + // For shared app/platform classes originated from the run-time image: + // The ProtectionDomains are cached in the corresponding ModuleEntries + // for fast access by the VM. + ResourceMark rm; + ClassLoaderData *loader_data = + ClassLoaderData::class_loader_data(class_loader()); + PackageEntryTable* pkgEntryTable = loader_data->packages(); + TempNewSymbol pkg_name = InstanceKlass::package_from_name(class_name, CHECK_(pd)); + if (pkg_name != NULL) { + PackageEntry* pkg_entry = pkgEntryTable->lookup_only(pkg_name); + if (pkg_entry != NULL) { + ModuleEntry* mod_entry = pkg_entry->module(); + pd = get_shared_protection_domain(class_loader, mod_entry, THREAD); + define_shared_package(class_name, class_loader, mod_entry, CHECK_(pd)); + } + } + } else { + // For shared app/platform classes originated from JAR files on the class path: + // Each of the 3 SystemDictionaryShared::_shared_xxx arrays has the same length + // as the shared classpath table in the shared archive (see + // FileMap::_classpath_entry_table in filemap.hpp for details). + // + // If a shared InstanceKlass k is loaded from the class path, let + // + // index = k->shared_classpath_index(): + // + // FileMap::_classpath_entry_table[index] identifies the JAR file that contains k. + // + // k's protection domain is: + // + // ProtectionDomain pd = _shared_protection_domains[index]; + // + // and k's Package is initialized using + // + // manifest = _shared_jar_manifests[index]; + // url = _shared_jar_urls[index]; + // define_shared_package(class_name, class_loader, manifest, url, CHECK_(pd)); + // + // Note that if an element of these 3 _shared_xxx arrays is NULL, it will be initialized by + // the corresponding SystemDictionaryShared::get_shared_xxx() function. + Handle manifest = get_shared_jar_manifest(index, CHECK_(pd)); + Handle url = get_shared_jar_url(index, CHECK_(pd)); + define_shared_package(class_name, class_loader, manifest, url, CHECK_(pd)); + pd = get_shared_protection_domain(class_loader, index, url, CHECK_(pd)); + } + } + return pd; +} + +// Currently AppCDS only archives classes from the run-time image, the +// -Xbootclasspath/a path, and the class path. The following rules need to be +// revised when AppCDS is changed to archive classes from other code sources +// in the future, for example the module path (specified by -p). +// +// Check if a shared class can be loaded by the specific classloader. Following +// are the "visible" archived classes for different classloaders. +// +// NULL classloader: +// - see SystemDictionary::is_shared_class_visible() +// Platform classloader: +// - Module class from "modules" jimage. ModuleEntry must be defined in the +// classloader. +// App Classloader: +// - Module class from "modules" jimage. ModuleEntry must be defined in the +// classloader. +// - Class from -cp. The class must have no PackageEntry defined in any of the +// boot/platform/app classloader, or must be in the unnamed module defined in the +// AppClassLoader. +bool SystemDictionaryShared::is_shared_class_visible_for_classloader( + InstanceKlass* ik, + Handle class_loader, + const char* pkg_string, + Symbol* pkg_name, + PackageEntry* pkg_entry, + ModuleEntry* mod_entry, + TRAPS) { + assert(class_loader.not_null(), "Class loader should not be NULL"); + assert(Universe::is_module_initialized(), "Module system is not initialized"); + + int path_index = ik->shared_classpath_index(); + SharedClassPathEntry* ent = + (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index); + + if (SystemDictionary::is_platform_class_loader(class_loader())) { + assert(ent != NULL, "shared class for PlatformClassLoader should have valid SharedClassPathEntry"); + // The PlatformClassLoader can only load archived class originated from the + // run-time image. The class' PackageEntry/ModuleEntry must be + // defined by the PlatformClassLoader. + if (mod_entry != NULL) { + // PackageEntry/ModuleEntry is found in the classloader. Check if the + // ModuleEntry's location agrees with the archived class' origination. + if (ent->is_modules_image() && mod_entry->location()->starts_with("jrt:")) { + return true; // Module class from the "modules" jimage + } + } + } else if (SystemDictionary::is_system_class_loader(class_loader())) { + assert(ent != NULL, "shared class for system loader should have valid SharedClassPathEntry"); + if (pkg_string == NULL) { + // The archived class is in the unnamed package. Currently, the boot image + // does not contain any class in the unnamed package. + assert(!ent->is_modules_image(), "Class in the unnamed package must be from the classpath"); + if (path_index >= ClassLoaderExt::app_paths_start_index()) { + return true; + } + } else { + // Check if this is from a PackageEntry/ModuleEntry defined in the AppClassloader. + if (pkg_entry == NULL) { + // It's not guaranteed that the class is from the classpath if the + // PackageEntry cannot be found from the AppClassloader. Need to check + // the boot and platform classloader as well. + if (get_package_entry(pkg_name, ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader())) == NULL && + get_package_entry(pkg_name, ClassLoaderData::the_null_class_loader_data()) == NULL) { + // The PackageEntry is not defined in any of the boot/platform/app classloaders. + // The archived class must from -cp path and not from the run-time image. + if (!ent->is_modules_image() && path_index >= ClassLoaderExt::app_paths_start_index()) { + return true; + } + } + } else if (mod_entry != NULL) { + // The package/module is defined in the AppClassLoader. Currently we only + // support archiving application module class from the run-time image. + // Packages from the -cp path are in the unnamed_module. + if ((ent->is_modules_image() && mod_entry->location()->starts_with("jrt:")) || + (pkg_entry->in_unnamed_module() && path_index >= ClassLoaderExt::app_paths_start_index())) { + DEBUG_ONLY( \ + ClassLoaderData* loader_data = class_loader_data(class_loader); \ + if (pkg_entry->in_unnamed_module()) \ + assert(mod_entry == loader_data->unnamed_module(), "the unnamed module is not defined in the classloader");) + + return true; + } + } + } + } else { + // TEMP: if a shared class can be found by a custom loader, consider it visible now. + // FIXME: is this actually correct? + return true; + } + return false; +} + +// The following stack shows how this code is reached: +// +// [0] SystemDictionaryShared::find_or_load_shared_class() +// [1] JVM_FindLoadedClass +// [2] java.lang.ClassLoader.findLoadedClass0() +// [3] java.lang.ClassLoader.findLoadedClass() +// [4] java.lang.ClassLoader.loadClass() +// [5] jdk.internal.loader.ClassLoaders$AppClassLoader_klass.loadClass() +// +// Because AppCDS supports only the PlatformClassLoader and AppClassLoader, we make the following +// assumptions (based on the JDK 8.0 source code): +// +// [a] these two loaders use the default implementation of +// ClassLoader.loadClass(String name, boolean resolve), which +// [b] calls findLoadedClass(name), immediately followed by parent.loadClass(), +// immediately followed by findClass(name). +// [c] If the requested class is a shared class of the current class loader, parent.loadClass() +// always returns null, and +// [d] if AppCDS is not enabled, the class would be loaded by findClass() by decoding it from a +// JAR file and then parsed. +// +// Given these assumptions, we intercept the findLoadedClass() call to invoke +// SystemDictionaryShared::find_or_load_shared_class() to load the shared class from +// the archive. The reasons are: +// +// + Because AppCDS is a commercial feature, we want to hide the implementation. There +// is currently no easy way to hide Java code, so we did it with native code. +// + Start-up is improved because we avoid decoding the JAR file, and avoid delegating +// to the parent (since we know the parent will not find this class). +// +// NOTE: there's a lot of assumption about the Java code. If any of that change, this +// needs to be redesigned. +// +// An alternative is to modify the Java code of AppClassLoader.loadClass(). +// +InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( + Symbol* name, Handle class_loader, TRAPS) { + if (DumpSharedSpaces) { + return NULL; + } + + InstanceKlass* k = NULL; + if (shared_dictionary() != NULL && + UseAppCDS && (SystemDictionary::is_system_class_loader(class_loader()) || + SystemDictionary::is_platform_class_loader(class_loader()))) { + + // Fix for 4474172; see evaluation for more details + class_loader = Handle( + THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader())); + ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL); + Dictionary* dictionary = loader_data->dictionary(); + + unsigned int d_hash = dictionary->compute_hash(name); + + bool DoObjectLock = true; + if (is_parallelCapable(class_loader)) { + DoObjectLock = false; + } + + // Make sure we are synchronized on the class loader before we proceed + // + // Note: currently, find_or_load_shared_class is called only from + // JVM_FindLoadedClass and used for PlatformClassLoader and AppClassLoader, + // which are parallel-capable loaders, so this lock is NOT taken. + Handle lockObject = compute_loader_lock_object(class_loader, THREAD); + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, DoObjectLock); + + { + MutexLocker mu(SystemDictionary_lock, THREAD); + Klass* check = find_class(d_hash, name, dictionary); + if (check != NULL) { + return InstanceKlass::cast(check); + } + } + + k = load_shared_class_for_builtin_loader(name, class_loader, THREAD); + if (k != NULL) { + define_instance_class(k, CHECK_NULL); + } + } + + return k; +} + +InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader( + Symbol* class_name, Handle class_loader, TRAPS) { + assert(UseAppCDS && shared_dictionary() != NULL, "already checked"); + Klass* k = shared_dictionary()->find_class_for_builtin_loader(class_name); + + if (k != NULL) { + InstanceKlass* ik = InstanceKlass::cast(k); + if ((ik->is_shared_app_class() && + SystemDictionary::is_system_class_loader(class_loader())) || + (ik->is_shared_platform_class() && + SystemDictionary::is_platform_class_loader(class_loader()))) { + Handle protection_domain = + SystemDictionaryShared::init_security_info(class_loader, ik, CHECK_NULL); + return load_shared_class(ik, class_loader, protection_domain, THREAD); + } + } + + return NULL; +} + +void SystemDictionaryShared::oops_do(OopClosure* f) { + f->do_oop((oop*)&_shared_protection_domains); + f->do_oop((oop*)&_shared_jar_urls); + f->do_oop((oop*)&_shared_jar_manifests); +} + +void SystemDictionaryShared::allocate_shared_protection_domain_array(int size, TRAPS) { + if (_shared_protection_domains == NULL) { + _shared_protection_domains = oopFactory::new_objArray( + SystemDictionary::ProtectionDomain_klass(), size, CHECK); + } +} + +void SystemDictionaryShared::allocate_shared_jar_url_array(int size, TRAPS) { + if (_shared_jar_urls == NULL) { + _shared_jar_urls = oopFactory::new_objArray( + SystemDictionary::URL_klass(), size, CHECK); + } +} + +void SystemDictionaryShared::allocate_shared_jar_manifest_array(int size, TRAPS) { + if (_shared_jar_manifests == NULL) { + _shared_jar_manifests = oopFactory::new_objArray( + SystemDictionary::Jar_Manifest_klass(), size, CHECK); + } +} + +void SystemDictionaryShared::allocate_shared_data_arrays(int size, TRAPS) { + allocate_shared_protection_domain_array(size, CHECK); + allocate_shared_jar_url_array(size, CHECK); + allocate_shared_jar_manifest_array(size, CHECK); +} + + +InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_name, + Handle class_loader, + Handle protection_domain, + const ClassFileStream* cfs, + TRAPS) { + if (!UseAppCDS || shared_dictionary() == NULL) { + return NULL; + } + if (class_name == NULL) { // don't do this for anonymous classes + return NULL; + } + if (class_loader.is_null() || + SystemDictionary::is_system_class_loader(class_loader()) || + SystemDictionary::is_platform_class_loader(class_loader())) { + // This function is called for loading only UNREGISTERED classes. + // Do nothing for the BUILTIN loaders. + return NULL; + } + + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); + Klass* k; + + { // UNREGISTERED loader + if (!shared_dictionary()->class_exists_for_unregistered_loader(class_name)) { + // No classes of this name for unregistered loaders. + return NULL; + } + + int clsfile_size = cfs->length(); + int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); + + k = shared_dictionary()->find_class_for_unregistered_loader(class_name, + clsfile_size, clsfile_crc32); + } + + if (k == NULL) { // not archived + return NULL; + } + + return acquire_class_for_current_thread(InstanceKlass::cast(k), class_loader, + protection_domain, THREAD); +} + +InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread( + InstanceKlass *ik, + Handle class_loader, + Handle protection_domain, + TRAPS) { + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); + + { + MutexLocker mu(SharedDictionary_lock, THREAD); + if (ik->class_loader_data() != NULL) { + // ik is already loaded (by this loader or by a different loader) + // or ik is being loaded by a different thread (by this loader or by a different loader) + return NULL; + } + + // No other thread has acquired this yet, so give it to *this thread* + ik->set_class_loader_data(loader_data); + } + + // No longer holding SharedDictionary_lock + // No need to lock, as <ik> can be held only by a single thread. + loader_data->add_class(ik); + + // Load and check super/interfaces, restore unsharable info + InstanceKlass* shared_klass = load_shared_class(ik, class_loader, protection_domain, THREAD); + if (shared_klass == NULL || HAS_PENDING_EXCEPTION) { + // TODO: clean up <ik> so it can be used again + return NULL; + } + + return shared_klass; +} + +bool SystemDictionaryShared::add_non_builtin_klass(Symbol* name, ClassLoaderData* loader_data, + InstanceKlass* k, + TRAPS) { + assert(DumpSharedSpaces, "only when dumping"); + assert(UseAppCDS && boot_loader_dictionary() != NULL, "must be"); + + if (boot_loader_dictionary()->add_non_builtin_klass(name, loader_data, k)) { + MutexLocker mu_r(Compile_lock, THREAD); // not really necessary, but add_to_hierarchy asserts this. + add_to_hierarchy(k, CHECK_0); + return true; + } + return false; +} + +// This function is called to resolve the super/interfaces of shared classes for +// non-built-in loaders. E.g., ChildClass in the below example +// where "super:" (and optionally "interface:") have been specified. +// +// java/lang/Object id: 0 +// Interface id: 2 super: 0 source: cust.jar +// ChildClass id: 4 super: 0 interfaces: 2 source: cust.jar +Klass* SystemDictionaryShared::dump_time_resolve_super_or_fail( + Symbol* child_name, Symbol* class_name, Handle class_loader, + Handle protection_domain, bool is_superclass, TRAPS) { + + assert(DumpSharedSpaces, "only when dumping"); + + ClassListParser* parser = ClassListParser::instance(); + if (parser == NULL) { + // We're still loading the well-known classes, before the ClassListParser is created. + return NULL; + } + if (child_name->equals(parser->current_class_name())) { + // When this function is called, all the numbered super and interface types + // must have already been loaded. Hence this function is never recursively called. + if (is_superclass) { + return parser->lookup_super_for_current_class(class_name); + } else { + return parser->lookup_interface_for_current_class(class_name); + } + } else { + // The VM is not trying to resolve a super type of parser->current_class_name(). + // Instead, it's resolving an error class (because parser->current_class_name() has + // failed parsing or verification). Don't do anything here. + return NULL; + } +} + +struct SharedMiscInfo { + Klass* _klass; + int _clsfile_size; + int _clsfile_crc32; +}; + +static GrowableArray<SharedMiscInfo>* misc_info_array = NULL; + +void SystemDictionaryShared::set_shared_class_misc_info(Klass* k, ClassFileStream* cfs) { + assert(DumpSharedSpaces, "only when dumping"); + int clsfile_size = cfs->length(); + int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); + + if (misc_info_array == NULL) { + misc_info_array = new (ResourceObj::C_HEAP, mtClass) GrowableArray<SharedMiscInfo>(20, /*c heap*/ true); + } + + SharedMiscInfo misc_info; + DEBUG_ONLY({ + 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(Klass* k, DictionaryEntry* ent) { + SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent; + 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) { + for (int i=0; i<misc_info_array->length(); i++) { + SharedMiscInfo misc_info = misc_info_array->at(i); + if (misc_info._klass == k) { + entry->_clsfile_size = misc_info._clsfile_size; + entry->_clsfile_crc32 = misc_info._clsfile_crc32; + misc_info_array->remove_at(i); + return; + } + } + } +} + +bool SystemDictionaryShared::add_verification_constraint(Klass* k, Symbol* name, + Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) { + assert(DumpSharedSpaces, "called at dump time only"); + + // Skip anonymous classes, which are not archived as they are not in + // dictionary (see assert_no_anonymoys_classes_in_dictionaries() in + // VM_PopulateDumpSharedSpace::doit()). + if (k->class_loader_data()->is_anonymous()) { + return true; // 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 && strstr(k->name()->as_C_string(), "Lambda$") != NULL) { + return true; + } + assert(entry != NULL, "class should be in dictionary before being verified"); + entry->add_verification_constraint(name, from_name, from_field_is_protected, + from_is_array, from_is_object); + if (entry->is_builtin()) { + // For builtin class loaders, we can try to complete the verification check at dump time, + // because we can resolve all the constraint classes. + return false; + } else { + // For non-builtin class loaders, we cannot complete the verification check at dump time, + // because at dump time we don't know how to resolve classes for such loaders. + return true; + } +} + +void SystemDictionaryShared::finalize_verification_constraints() { + boot_loader_dictionary()->finalize_verification_constraints(); +} + +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(Klass* 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 SharedDictionary::finalize_verification_constraints() { + int bytes = 0, count = 0; + for (int index = 0; index < table_size(); index++) { + for (SharedDictionaryEntry *probe = bucket(index); + probe != NULL; + probe = probe->next()) { + int n = probe->finalize_verification_constraints(); + if (n > 0) { + bytes += n; + count ++; + } + } + } + if (log_is_enabled(Info, cds, verification)) { + double avg = 0; + if (count > 0) { + avg = double(bytes) / double(count); + } + log_info(cds, verification)("Recorded verification constraints for %d classes = %d bytes (avg = %.2f bytes) ", count, bytes, avg); + } +} + +void SharedDictionaryEntry::add_verification_constraint(Symbol* name, + Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) { + if (_verifier_constraints == NULL) { + _verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<Symbol*>(8, true, mtClass); + } + if (_verifier_constraint_flags == NULL) { + _verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray<char>(4, true, mtClass); + } + GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints; + for (int i=0; i<vc_array->length(); i+= 2) { + if (name == vc_array->at(i) && + from_name == vc_array->at(i+1)) { + return; + } + } + vc_array->append(name); + vc_array->append(from_name); + + GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags; + char c = 0; + c |= from_field_is_protected ? FROM_FIELD_IS_PROTECTED : 0; + c |= from_is_array ? FROM_IS_ARRAY : 0; + c |= from_is_object ? FROM_IS_OBJECT : 0; + vcflags_array->append(c); + + if (log_is_enabled(Trace, cds, verification)) { + ResourceMark rm; + log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s", + instance_klass()->external_name(), from_name->as_klass_external_name(), + name->as_klass_external_name()); + } +} + +int SharedDictionaryEntry::finalize_verification_constraints() { + assert(DumpSharedSpaces, "called at dump time only"); + Thread* THREAD = Thread::current(); + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints; + GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags; + + if (vc_array != NULL) { + if (log_is_enabled(Trace, cds, verification)) { + 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++) { + out->at_put(i, vc_array->at(i)); + } + _verifier_constraints = out; + 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; + } + return 0; +} + +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, + from_name, from_field_is_protected, from_is_array, from_is_object, CHECK); + if (!ok) { + ResourceMark rm(THREAD); + stringStream ss; + + ss.print_cr("Bad type on operand stack"); + ss.print_cr("Exception Details:"); + ss.print_cr(" Location:\n %s", klass->name()->as_C_string()); + ss.print_cr(" Reason:\n Type '%s' is not assignable to '%s'", + from_name->as_quoted_ascii(), name->as_quoted_ascii()); + THROW_MSG(vmSymbols::java_lang_VerifyError(), ss.as_string()); + } + } + } +} + +void SharedDictionaryEntry::metaspace_pointers_do(MetaspaceClosure* it) { + it->push((Array<Symbol*>**)&_verifier_constraints); + it->push((Array<char>**)&_verifier_constraint_flags); +} + +bool SharedDictionary::add_non_builtin_klass(const Symbol* class_name, + ClassLoaderData* loader_data, + InstanceKlass* klass) { + + 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) { + Klass* klass = (Klass*)entry->literal(); + 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"); + SharedDictionaryEntry* entry = (SharedDictionaryEntry*)new_entry(hash, klass); + add_entry(index, entry); + + assert(entry->is_unregistered(), "sanity"); + assert(!entry->is_builtin(), "sanity"); + return true; +} + + +//----------------- +// SharedDictionary +//----------------- + + +Klass* SharedDictionary::find_class_for_builtin_loader(const Symbol* name) const { + SharedDictionaryEntry* entry = get_entry_for_builtin_loader(name); + return entry != NULL ? entry->instance_klass() : (Klass*)NULL; +} + +Klass* 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() : (Klass*)NULL; +} + +void SharedDictionary::update_entry(Klass* 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 { + // We're called from find_class_for_unregistered_loader + if (entry->_clsfile_size && clsfile_crc32 == entry->_clsfile_crc32) { + return entry; + } + } + + // There can be only 1 class with this name for unregistered loaders. + return NULL; + } + } + } + return NULL; +} diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 244e98e5d74..c1b87348a5a 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -25,75 +25,362 @@ #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP -#include "classfile/systemDictionary.hpp" +#include "oops/klass.hpp" #include "classfile/dictionary.hpp" +#include "classfile/systemDictionary.hpp" +#include "memory/filemap.hpp" + + +/*=============================================================================== + + Handling of the classes in the AppCDS archive + + To ensure safety and to simplify the implementation, archived classes are + "segregated" into several types. The following rules describe how they + are stored and looked up. + +[1] Category of archived classes + + There are 3 disjoint groups of classes stored in the AppCDS archive. They are + categorized as by their SharedDictionaryEntry::loader_type() + + BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP + loaders. + + UNREGISTERED: These classes may be defined ONLY by a ClassLoader + instance that's not listed above (using fingerprint matching) + +[2] How classes from different categories are specified in the classlist: + + Starting from JDK9, each class in the classlist may be specified with + these keywords: "id", "super", "interfaces", "loader" and "source". + + + BUILTIN Only the "id" keyword may be (optionally) specified. All other + keywords are forbidden. + + The named class is looked up from the jimage and from + Xbootclasspath/a and CLASSPATH. + + UNREGISTERED: The "id", "super", and "source" keywords must all be + specified. + + The "interfaces" keyword must be specified if the class implements + one or more local interfaces. The "interfaces" keyword must not be + specified if the class does not implement local interfaces. + + The named class is looked up from the location specified in the + "source" keyword. + + Example classlist: + + # BUILTIN + java/lang/Object id: 0 + java/lang/Cloneable id: 1 + java/lang/String + + # UNREGISTERED + Bar id: 3 super: 0 interfaces: 1 source: /foo.jar + + +[3] Identifying the loader_type of archived classes in the shared dictionary + + Each archived Klass* C is associated with a SharedDictionaryEntry* E + + BUILTIN: (C->shared_classpath_index() >= 0) + UNREGISTERED: (C->shared_classpath_index() < 0) + +[4] Lookup of archived classes at run time: + + (a) BUILTIN loaders: + + Search the shared directory for a BUILTIN class with a matching name. + + (b) UNREGISTERED loaders: + + The search originates with SystemDictionaryShared::lookup_from_stream(). + + Search the shared directory for a UNREGISTERED class with a matching + (name, clsfile_len, clsfile_crc32) tuple. + +===============================================================================*/ +#define UNREGISTERED_INDEX -9999 class ClassFileStream; -class SystemDictionaryShared: public SystemDictionary { +// Archived classes need extra information not needed by traditionally loaded classes. +// To keep footprint small, we add these in the dictionary entry instead of the InstanceKlass. +class SharedDictionaryEntry : public DictionaryEntry { + public: - static void initialize(TRAPS) {} - static InstanceKlass* find_or_load_shared_class(Symbol* class_name, - Handle class_loader, - TRAPS) { - return NULL; - } - static void roots_oops_do(OopClosure* blk) {} - static void oops_do(OopClosure* f) {} - static bool is_sharing_possible(ClassLoaderData* loader_data) { - oop class_loader = loader_data->class_loader(); - return (class_loader == NULL); - } - static bool is_shared_class_visible_for_classloader( - InstanceKlass* ik, - Handle class_loader, - const char* pkg_string, - Symbol* pkg_name, - PackageEntry* pkg_entry, - ModuleEntry* mod_entry, - TRAPS) { - return false; + enum LoaderType { + LT_BUILTIN, + LT_UNREGISTERED + }; + + enum { + FROM_FIELD_IS_PROTECTED = 1 << 0, + FROM_IS_ARRAY = 1 << 1, + 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 { + Klass* k = (Klass*)literal(); + + 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(Klass* klass); + void finalize_verification_constraints(); + + bool add_non_builtin_klass(const Symbol* class_name, + ClassLoaderData* loader_data, + InstanceKlass* obj); + + void update_entry(Klass* klass, int id); + + Klass* find_class_for_builtin_loader(const Symbol* name) const; + Klass* 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: + // These _shared_xxxs arrays are used to initialize the java.lang.Package and + // java.security.ProtectionDomain objects associated with each shared class. + // + // See SystemDictionaryShared::init_security_info for more info. + static objArrayOop _shared_protection_domains; + static objArrayOop _shared_jar_urls; + static objArrayOop _shared_jar_manifests; + + static InstanceKlass* load_shared_class_for_builtin_loader( + Symbol* class_name, + Handle class_loader, + TRAPS); + static Handle get_package_name(Symbol* class_name, TRAPS); + + + // Package handling: + // + // 1. For named modules in the runtime image + // BOOT classes: Reuses the existing JVM_GetSystemPackage(s) interfaces + // to get packages in named modules for shared classes. + // Package for non-shared classes in named module is also + // handled using JVM_GetSystemPackage(s). + // + // APP classes: VM calls ClassLoaders.AppClassLoader::definePackage(String, Module) + // to define package for shared app classes from named + // modules. + // + // PLATFORM classes: VM calls ClassLoaders.PlatformClassLoader::definePackage(String, Module) + // to define package for shared platform classes from named + // modules. + // + // 2. For unnamed modules + // BOOT classes: Reuses the existing JVM_GetSystemPackage(s) interfaces to + // get packages for shared boot classes in unnamed modules. + // + // APP classes: VM calls ClassLoaders.AppClassLoader::defineOrCheckPackage() + // with with the manifest and url from archived data. + // + // PLATFORM classes: No package is defined. + // + // The following two define_shared_package() functions are used to define + // package for shared APP and PLATFORM classes. + static void define_shared_package(Symbol* class_name, + Handle class_loader, + Handle manifest, + Handle url, + TRAPS); + static void define_shared_package(Symbol* class_name, + Handle class_loader, + ModuleEntry* mod_entry, + TRAPS); + + static Handle get_shared_jar_manifest(int shared_path_index, TRAPS); + static Handle get_shared_jar_url(int shared_path_index, TRAPS); + static Handle get_protection_domain_from_classloader(Handle class_loader, + Handle url, TRAPS); + static Handle get_shared_protection_domain(Handle class_loader, + int shared_path_index, + Handle url, + TRAPS); + static Handle get_shared_protection_domain(Handle class_loader, + ModuleEntry* mod, TRAPS); + static Handle init_security_info(Handle class_loader, InstanceKlass* ik, TRAPS); + + static void atomic_set_array_index(objArrayOop array, int index, oop o) { + // Benign race condition: array.obj_at(index) may already be filled in. + // The important thing here is that all threads pick up the same result. + // It doesn't matter which racing thread wins, as long as only one + // result is used by all threads, and all future queries. + array->atomic_compare_exchange_oop(index, o, NULL); + } + + static oop shared_protection_domain(int index); + static void atomic_set_shared_protection_domain(int index, oop pd) { + atomic_set_array_index(_shared_protection_domains, index, pd); + } + static void allocate_shared_protection_domain_array(int size, TRAPS); + static oop shared_jar_url(int index); + static void atomic_set_shared_jar_url(int index, oop url) { + atomic_set_array_index(_shared_jar_urls, index, url); + } + static void allocate_shared_jar_url_array(int size, TRAPS); + static oop shared_jar_manifest(int index); + static void atomic_set_shared_jar_manifest(int index, oop man) { + atomic_set_array_index(_shared_jar_manifests, index, man); + } + static void allocate_shared_jar_manifest_array(int size, TRAPS); + static InstanceKlass* acquire_class_for_current_thread( + InstanceKlass *ik, + Handle class_loader, + Handle protection_domain, + TRAPS); + +public: + static void initialize(TRAPS); + + // Called by PLATFORM/APP loader only + static InstanceKlass* find_or_load_shared_class(Symbol* class_name, + Handle class_loader, + TRAPS); + + + static void allocate_shared_data_arrays(int size, TRAPS); + static void oops_do(OopClosure* f); + static void roots_oops_do(OopClosure* f) { + oops_do(f); + } + + // Check if sharing is supported for the class loader. + static bool is_sharing_possible(ClassLoaderData* loader_data) { + oop class_loader = loader_data->class_loader(); + return (class_loader == NULL || + (UseAppCDS && (SystemDictionary::is_system_class_loader(class_loader) || + SystemDictionary::is_platform_class_loader(class_loader))) + ); + } + static bool is_shared_class_visible_for_classloader(InstanceKlass* ik, + Handle class_loader, + const char* pkg_string, + Symbol* pkg_name, + PackageEntry* pkg_entry, + ModuleEntry* mod_entry, + TRAPS); + static PackageEntry* get_package_entry(Symbol* pkg, + ClassLoaderData *loader_data) { + if (loader_data != NULL) { + PackageEntryTable* pkgEntryTable = loader_data->packages(); + return pkgEntryTable->lookup_only(pkg); + } + return NULL; + } + + static bool add_non_builtin_klass(Symbol* class_name, ClassLoaderData* loader_data, + InstanceKlass* k, TRAPS); static Klass* dump_time_resolve_super_or_fail(Symbol* child_name, Symbol* class_name, Handle class_loader, Handle protection_domain, bool is_superclass, - TRAPS) { - return NULL; - } + TRAPS); static size_t dictionary_entry_size() { - return sizeof(DictionaryEntry); + return (DumpSharedSpaces) ? sizeof(SharedDictionaryEntry) : sizeof(DictionaryEntry); + } + static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) NOT_CDS_RETURN; + static bool is_builtin(DictionaryEntry* ent) { + // 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(); } - static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {} - static bool is_builtin(DictionaryEntry* entry) { return true; } + // 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 InstanceKlass* lookup_from_stream(Symbol* class_name, + static SharedDictionary* boot_loader_dictionary() { + return (SharedDictionary*)ClassLoaderData::the_null_class_loader_data()->dictionary(); + } + + static void update_shared_entry(Klass* 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 set_shared_class_misc_info(Klass* k, ClassFileStream* cfs); + + static InstanceKlass* lookup_from_stream(const Symbol* class_name, Handle class_loader, Handle protection_domain, const ClassFileStream* st, - TRAPS) { - return NULL; - } - - // The (non-application) CDS implementation supports only classes in the boot - // class loader, which ensures that the verification constraints are the same - // during archive creation time and runtime. Thus we can do the constraint checks - // entirely during archive creation time. + TRAPS); + // "verification_constraints" are a set of checks performed by + // VerificationType::is_reference_assignable_from when verifying a shared class during + // dump time. + // + // With AppCDS, it is possible to override archived classes by calling + // ClassLoader.defineClass() directly. SystemDictionary::load_shared_class() already + // ensures that you cannot load a shared class if its super type(s) are changed. However, + // we need an additional check to ensure that the verification_constraints did not change + // between dump time and runtime. static bool add_verification_constraint(Klass* k, Symbol* name, Symbol* from_name, bool from_field_is_protected, - bool from_is_array, bool from_is_object) {return false;} - static void finalize_verification_constraints() {} + 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, - TRAPS) {} -}; - -class SharedDictionaryEntry : public DictionaryEntry { -public: - void metaspace_pointers_do(MetaspaceClosure* it) {} + TRAPS) NOT_CDS_RETURN; }; #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff --git a/src/hotspot/share/classfile/systemDictionary_ext.hpp b/src/hotspot/share/classfile/systemDictionary_ext.hpp index 698805b657d..6d257cd09e9 100644 --- a/src/hotspot/share/classfile/systemDictionary_ext.hpp +++ b/src/hotspot/share/classfile/systemDictionary_ext.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017 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,17 @@ #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP +#if INCLUDE_CDS + +#define WK_KLASSES_DO_EXT(do_klass) \ + /* well-known classes */ \ + do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders, Pre ) \ + /*end*/ + +#else + #define WK_KLASSES_DO_EXT(do_klass) +#endif // INCLUDE_CDS + #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 73fb9296772..65246e04bae 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -26,7 +26,6 @@ #define SHARE_VM_CLASSFILE_VMSYMBOLS_HPP #include "classfile/moduleEntry.hpp" -#include "classfile/vmSymbols_ext.hpp" #include "oops/symbol.hpp" #include "memory/iterator.hpp" #include "trace/traceMacros.hpp" @@ -673,8 +672,12 @@ /* trace signatures */ \ TRACE_TEMPLATES(template) \ \ - /* extensions */ \ - VM_SYMBOLS_DO_EXT(template, do_alias) \ + /* cds */ \ + template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \ + template(jdk_vm_cds_SharedClassInfo, "jdk/vm/cds/SharedClassInfo") \ + template(url_void_signature, "(Ljava/net/URL;)V") \ + template(toFileURL_name, "toFileURL") \ + template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \ \ /*end*/ diff --git a/src/hotspot/share/prims/cdsoffsets.cpp b/src/hotspot/share/prims/cdsoffsets.cpp new file mode 100644 index 00000000000..d38b7efbfff --- /dev/null +++ b/src/hotspot/share/prims/cdsoffsets.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "utilities/macros.hpp" +#if INCLUDE_CDS +#include "runtime/os.hpp" +#include "memory/filemap.hpp" +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "prims/cdsoffsets.hpp" + +CDSOffsets* CDSOffsets::_all = NULL; +#define ADD_NEXT(list, name, value) \ + list->add_end(new CDSOffsets(name, value, NULL)) + +#define CREATE_OFFSET_MAPS \ + _all = new CDSOffsets("size_t_size", sizeof(size_t), NULL); \ + ADD_NEXT(_all, "FileMapHeader::_magic", offset_of(FileMapInfo::FileMapHeader, _magic)); \ + ADD_NEXT(_all, "FileMapHeader::_crc", offset_of(FileMapInfo::FileMapHeader, _crc)); \ + ADD_NEXT(_all, "FileMapHeader::_version", offset_of(FileMapInfo::FileMapHeader, _version)); \ + ADD_NEXT(_all, "FileMapHeader::_space[0]", offset_of(FileMapInfo::FileMapHeader, _space)); \ + ADD_NEXT(_all, "space_info::_crc", offset_of(FileMapInfo::FileMapHeader::space_info, _crc)); \ + ADD_NEXT(_all, "space_info::_used", offset_of(FileMapInfo::FileMapHeader::space_info, _used)); \ + ADD_NEXT(_all, "FileMapHeader::_paths_misc_info_size", offset_of(FileMapInfo::FileMapHeader, _paths_misc_info_size)); \ + ADD_NEXT(_all, "file_header_size", sizeof(FileMapInfo::FileMapHeader)); \ + ADD_NEXT(_all, "space_info_size", sizeof(FileMapInfo::FileMapHeader::space_info)); + +int CDSOffsets::find_offset(const char* name) { + if (_all == NULL) { + CREATE_OFFSET_MAPS + } + CDSOffsets* it = _all; + while(it) { + if (!strcmp(name, it->get_name())) { + return it->get_offset(); + } + it = it->next(); + } + return -1; // not found +} + +void CDSOffsets::add_end(CDSOffsets* n) { + CDSOffsets* p = this; + while(p && p->_next) { p = p->_next; } + p->_next = n; +} +#endif // INCLUDE_CDS diff --git a/src/hotspot/share/prims/cdsoffsets.hpp b/src/hotspot/share/prims/cdsoffsets.hpp new file mode 100644 index 00000000000..aa147cc70a0 --- /dev/null +++ b/src/hotspot/share/prims/cdsoffsets.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +#ifndef SHARE_PRIMS_CDSOFFSETS_HPP +#define SHARE_PRIMS_CDSOFFSETS_HPP +class CDSOffsets: public CHeapObj<mtInternal> { + private: + char* _name; + int _offset; + CDSOffsets* _next; + static CDSOffsets* _all; // sole list for cds + public: + CDSOffsets(const char* name, int offset, CDSOffsets* next) { + _name = NEW_C_HEAP_ARRAY(char, strlen(name) + 1, mtInternal); + strcpy(_name, name); + _offset = offset; + _next = next; + } + + char* get_name() const { return _name; } + int get_offset() const { return _offset; } + CDSOffsets* next() const { return _next; } + void add_end(CDSOffsets* n); + + static int find_offset(const char* name); +}; +#endif // SHARE_PRIMS_CDSOFFSETS_HPP diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index dd3e52d2012..28d8851ff08 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -61,6 +61,9 @@ #include "utilities/debug.hpp" #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" +#if INCLUDE_CDS +#include "prims/cdsoffsets.hpp" +#endif // INCLUDE_CDS #if INCLUDE_ALL_GCS #include "gc/g1/concurrentMarkThread.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" @@ -1730,6 +1733,18 @@ WB_ENTRY(jboolean, WB_IsCDSIncludedInVmBuild(JNIEnv* env)) #endif WB_END + +#if INCLUDE_CDS + +WB_ENTRY(jint, WB_GetOffsetForName(JNIEnv* env, jobject o, jstring name)) + ResourceMark rm; + char* c_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name)); + int result = CDSOffsets::find_offset(c_name); + return (jint)result; +WB_END + +#endif // INCLUDE_CDS + WB_ENTRY(jint, WB_HandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_handle, jboolean all_threads)) class TraceSelfClosure : public ThreadClosure { jint _num_threads_completed; @@ -1918,6 +1933,9 @@ static JNINativeMethod methods[] = { {CC"runMemoryUnitTests", CC"()V", (void*)&WB_RunMemoryUnitTests}, {CC"readFromNoaccessArea",CC"()V", (void*)&WB_ReadFromNoaccessArea}, {CC"stressVirtualSpaceResize",CC"(JJJ)I", (void*)&WB_StressVirtualSpaceResize}, +#if INCLUDE_CDS + {CC"getOffsetForName0", CC"(Ljava/lang/String;)I", (void*)&WB_GetOffsetForName}, +#endif #if INCLUDE_ALL_GCS {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, {CC"g1IsHumongous0", CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous }, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 94f0a811112..820f656e9ae 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -3880,6 +3880,14 @@ jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args, vm_exit(0); } #endif + + if (match_option(option, "-XX:+UseAppCDS")) { + Flag* flag = Flag::find_flag("SharedArchiveFile", 17, true, true); + if (flag->is_diagnostic()) { + flag->clear_diagnostic(); + } + continue; + } } return JNI_OK; } diff --git a/src/hotspot/share/runtime/arguments_ext.hpp b/src/hotspot/share/runtime/arguments_ext.hpp index d1c9f183e8e..3ae21e1267f 100644 --- a/src/hotspot/share/runtime/arguments_ext.hpp +++ b/src/hotspot/share/runtime/arguments_ext.hpp @@ -36,7 +36,6 @@ public: // Otherwise returns false. static inline bool process_options(const JavaVMOption *option) { return false; } static inline void report_unsupported_options() { } - static inline bool using_AppCDS() { return false; } }; void ArgumentsExt::set_gc_specific_flags() { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 68f95ad58a3..a6ae28a1683 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -3932,6 +3932,13 @@ public: "Address to allocate shared memory region for class data") \ range(0, SIZE_MAX) \ \ + product(bool, UseAppCDS, false, \ + "Enable Application Class Data Sharing when using shared spaces") \ + writeable(CommandLineOnly) \ + \ + product(ccstr, SharedArchiveConfigFile, NULL, \ + "Data to add to the CDS archive file") \ + \ product(uintx, SharedSymbolTableBucketSize, 4, \ "Average number of symbols per bucket in shared table") \ range(2, 246) \ diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index daf4480f452..d775c50bdb9 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -189,12 +189,27 @@ hotspot_tier1_runtime = \ -runtime/Unsafe/RangeCheck.java \ -runtime/containers/ \ sanity/ \ - testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java + testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java \ + -:hotspot_tier1_runtime_appcds_exclude hotspot_cds = \ runtime/SharedArchiveFile/ \ runtime/CompressedOops/ +# AppCDS +# If modifying AppCDS it is also recommended to run the open hotspot_cds group +hotspot_appcds = \ + runtime/appcds/ + +# A subset of AppCDS tests to be run in JPRT push +hotspot_tier1_runtime_appcds = \ + runtime/appcds/HelloTest.java \ + runtime/appcds/sharedStrings/SharedStringsBasic.java \ + runtime/appcds/ClassLoaderTest.java + +hotspot_tier1_runtime_appcds_exclude = \ + runtime/appcds/ \ + -:hotspot_tier1_runtime_appcds hotspot_tier1_serviceability = \ serviceability/dcmd/compiler \ diff --git a/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java new file mode 100644 index 00000000000..6db82a771ab --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 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. + * + */ +import jdk.test.lib.cds.CDSOptions; + +// This class represents options used for +// during creation of the archive and/or running JVM with archive + +public class AppCDSOptions extends CDSOptions { + public String appJar; + + // Application classes to be archived + public String[] appClasses; + + public AppCDSOptions setAppJar(String appJar) { + this.appJar = appJar; + return this; + } + + public AppCDSOptions setAppClasses(String[] appClasses) { + this.appClasses = appClasses; + return this; + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java b/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java new file mode 100644 index 00000000000..a59d1f3753b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary At run time, it is OK to append new elements to the classpath that was used at dump time. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @compile test-classes/HelloMore.java + * @run main AppendClasspath + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class AppendClasspath { + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore"); + + // Dump an archive with a specified JAR file in -classpath + TestCommon.testDump(appJar, TestCommon.list("Hello")); + + // PASS: 1) runtime with classpath containing the one used in dump time + OutputAnalyzer output = TestCommon.execCommon( + "-cp", appJar + File.pathSeparator + appJar2, + "HelloMore"); + TestCommon.checkExec(output); + + final String errorMessage1 = "Unable to use shared archive"; + final String errorMessage2 = "shared class paths mismatch"; + // FAIL: 2) runtime with classpath different from the one used in dump time + // (runtime has an extra jar file prepended to the class path) + output = TestCommon.execCommon( + "-cp", appJar2 + File.pathSeparator + appJar, + "HelloMore"); + output.shouldContain(errorMessage1); + output.shouldContain(errorMessage2); + output.shouldHaveExitValue(1); + + // FAIL: 3) runtime with classpath part of the one used in dump time + TestCommon.testDump(appJar + File.pathSeparator + appJar2, + TestCommon.list("Hello")); + output = TestCommon.execCommon( + "-cp", appJar2, + "Hello"); + output.shouldContain(errorMessage1); + output.shouldContain(errorMessage2); + output.shouldHaveExitValue(1); + + // FAIL: 4) runtime with same set of jar files in the classpath but + // with different order + output = TestCommon.execCommon( + "-cp", appJar2 + File.pathSeparator + appJar, + "HelloMore"); + output.shouldContain(errorMessage1); + output.shouldContain(errorMessage2); + output.shouldHaveExitValue(1); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java new file mode 100644 index 00000000000..04e6bb2625e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary bootclasspath mismatch test. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main BootClassPathMismatch + */ + +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.StandardCopyOption; +import java.nio.file.Paths; + + +public class BootClassPathMismatch { + private static final String mismatchMessage = "shared class paths mismatch"; + + public static void main(String[] args) throws Exception { + JarBuilder.getOrCreateHelloJar(); + copyHelloToNewDir(); + + BootClassPathMismatch test = new BootClassPathMismatch(); + test.testBootClassPathMismatch(); + test.testBootClassPathMatch(); + } + + /* Error should be detected if: + * dump time: -Xbootclasspath/a:${testdir}/hello.jar + * run-time : -Xbootclasspath/a:${testdir}/newdir/hello.jar + */ + public void testBootClassPathMismatch() throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String appClasses[] = {"Hello"}; + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, appClasses, "-Xbootclasspath/a:" + appJar); + String testDir = TestCommon.getTestDir("newdir"); + String otherJar = testDir + File.separator + "hello.jar"; + OutputAnalyzer execOutput = TestCommon.exec( + appJar, "-verbose:class", "-Xbootclasspath/a:" + otherJar, "Hello"); + try { + TestCommon.checkExec(execOutput, mismatchMessage); + } catch (java.lang.RuntimeException re) { + String cause = re.getMessage(); + if (!mismatchMessage.equals(cause)) { + throw re; + } + } + } + + /* No error if: + * dump time: -Xbootclasspath/a:${testdir}/hello.jar + * run-time : -Xbootclasspath/a:${testdir}/hello.jar + */ + public void testBootClassPathMatch() throws Exception { + String appJar = TestCommon.getTestJar("hello.jar"); + String appClasses[] = {"Hello"}; + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, appClasses, "-Xbootclasspath/a:" + appJar); + OutputAnalyzer execOutput = TestCommon.exec( + appJar, "-verbose:class", + "-Xbootclasspath/a:" + appJar, "Hello"); + TestCommon.checkExec(execOutput, + "[class,load] Hello source: shared objects file"); + } + + private static void copyHelloToNewDir() throws Exception { + String classDir = System.getProperty("test.classes"); + String dstDir = classDir + File.separator + "newdir"; + try { + Files.createDirectory(Paths.get(dstDir)); + } catch (FileAlreadyExistsException e) { } + + Files.copy(Paths.get(classDir, "hello.jar"), + Paths.get(dstDir, "hello.jar"), + StandardCopyOption.REPLACE_EXISTING); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java b/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java new file mode 100644 index 00000000000..56315c16d64 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Test case sensitive aspect of comparing class paths + * between dump time and archive use time + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @requires os.family != "mac" + * @compile test-classes/Hello.java + * @run main CaseSensitiveClassPath + */ + +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + + +// Excluded from running on MAC: a more comprehensive case sensitivity detection +// and fix mechanism is needed, which is planned to be implemented in the future. +public class CaseSensitiveClassPath { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String appJarUpper = appJar.replace("hello", "Hello"); + + OutputAnalyzer out = TestCommon.dump(appJar, TestCommon.list("Hello")); + TestCommon.checkDump(out); + + Path jarPath = Paths.get(appJar); + Path jarPathUpper = null; + + boolean fileExists = false; + try { + jarPathUpper = Files.createFile(Paths.get(appJarUpper)); + } catch (FileAlreadyExistsException faee) { + fileExists = true; + } + + if (!fileExists) { + try { + Files.copy(jarPath, jarPathUpper, StandardCopyOption.REPLACE_EXISTING); + } catch (Exception e) { + throw new java.lang.RuntimeException( + "Failed copying file from " + appJar + " to " + appJarUpper + ".", e); + } + } else { + jarPathUpper = Paths.get(appJarUpper); + } + + out = TestCommon.exec(appJarUpper, "Hello", "-Xlog:class+path=info", + "-Xlog:cds"); + if (TestCommon.isUnableToMap(out)) + return; + + if (Files.isSameFile(jarPath, jarPathUpper)) { + TestCommon.checkExec(out, "Hello World"); + } else { + out.shouldContain("shared class paths mismatch") + .shouldHaveExitValue(1); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java b/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java new file mode 100644 index 00000000000..8012a1de39a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Initiating and defining classloader test. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @compile test-classes/HelloWB.java + * @compile test-classes/ForNameTest.java + * @compile test-classes/BootClassPathAppendHelper.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ClassLoaderTest + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class ClassLoaderTest { + public static void main(String[] args) throws Exception { + JarBuilder.build(true, "ClassLoaderTest-WhiteBox", "sun/hotspot/WhiteBox"); + JarBuilder.getOrCreateHelloJar(); + JarBuilder.build("ClassLoaderTest-HelloWB", "HelloWB"); + JarBuilder.build("ClassLoaderTest-ForName", "ForNameTest"); + ClassLoaderTest test = new ClassLoaderTest(); + test.testBootLoader(); + test.testDefiningLoader(); + } + + public void testBootLoader() throws Exception { + String appJar = TestCommon.getTestJar("ClassLoaderTest-HelloWB.jar"); + String appClasses[] = {"HelloWB"}; + String whiteBoxJar = TestCommon.getTestJar("ClassLoaderTest-WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + appJar + + File.pathSeparator + whiteBoxJar; + + TestCommon.dump(appJar, appClasses, bootClassPath); + + OutputAnalyzer runtimeOutput = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, bootClassPath, "-Xlog:class+load", "HelloWB"); + + if (!TestCommon.isUnableToMap(runtimeOutput)) { + runtimeOutput.shouldNotContain( + "[class,load] HelloWB source: shared objects file by jdk/internal/misc/ClassLoaders$AppClassLoader"); + runtimeOutput.shouldContain("[class,load] HelloWB source: shared objects file"); + } + } + + public void testDefiningLoader() throws Exception { + // The boot loader should be used to load the class when it's + // on the bootclasspath, regardless who is the initiating classloader. + // In this test case, the AppClassLoader is the initiating classloader. + String helloJar = TestCommon.getTestJar("hello.jar"); + String appJar = helloJar + System.getProperty("path.separator") + + TestCommon.getTestJar("ClassLoaderTest-ForName.jar"); + String whiteBoxJar = TestCommon.getTestJar("ClassLoaderTest-WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + helloJar + + File.pathSeparator + whiteBoxJar; + + TestCommon.dump(helloJar, TestCommon.list("Hello"), bootClassPath); + + TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, bootClassPath, "-XX:+TraceClassPaths", "ForNameTest"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java b/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java new file mode 100644 index 00000000000..d32f7b8f2be --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Class-Path: attribute in MANIFEST file + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @run main ClassPathAttr + */ + +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.StandardCopyOption; +import java.nio.file.Paths; + + +public class ClassPathAttr { + + public static void main(String[] args) throws Exception { + buildCpAttr("cpattr1", "cpattr1.mf", "CpAttr1", "CpAttr1"); + buildCpAttr("cpattr1_long", "cpattr1_long.mf", "CpAttr1", "CpAttr1"); + buildCpAttr("cpattr2", "cpattr2.mf", "CpAttr2", "CpAttr2"); + buildCpAttr("cpattr3", "cpattr3.mf", "CpAttr3", "CpAttr2", "CpAttr3"); + buildCpAttr("cpattr4", "cpattr4.mf", "CpAttr4", + "CpAttr2", "CpAttr3", "CpAttr4", "CpAttr5"); + buildCpAttr("cpattr5_123456789_223456789_323456789_423456789_523456789_623456789", "cpattr5_extra_long.mf", "CpAttr5", "CpAttr5"); + + for (int i=1; i<=2; i++) { + String jar1 = TestCommon.getTestJar("cpattr1.jar"); + String jar4 = TestCommon.getTestJar("cpattr4.jar"); + if (i == 2) { + // Test case #2 -- same as #1, except we use cpattr1_long.jar, which has a super-long + // Class-Path: attribute. + jar1 = TestCommon.getTestJar("cpattr1_long.jar"); + } + String cp = jar1 + File.pathSeparator + jar4; + + TestCommon.testDump(cp, TestCommon.list("CpAttr1", + "CpAttr2", + "CpAttr3", + "CpAttr4", + "CpAttr5")); + + OutputAnalyzer output = TestCommon.execCommon( + "-cp", cp, + "CpAttr1"); + TestCommon.checkExec(output); + + // Logging test for class+path. + output = TestCommon.execCommon( + "-Xlog:class+path", + "-cp", cp, + "CpAttr1"); + if (!TestCommon.isUnableToMap(output)){ + output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); + output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + } + // Make sure aliased TraceClassPaths still works + output = TestCommon.execCommon( + "-XX:+TraceClassPaths", + "-cp", cp, + "CpAttr1"); + if (!TestCommon.isUnableToMap(output)){ + output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); + output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + } + } + } + + private static void buildCpAttr(String jarName, String manifest, String enclosingClassName, String ...testClassNames) throws Exception { + String jarClassesDir = System.getProperty("test.classes") + File.separator + jarName + "_classes"; + try { Files.createDirectory(Paths.get(jarClassesDir)); } catch (FileAlreadyExistsException e) { } + + JarBuilder.compile(jarClassesDir, System.getProperty("test.src") + File.separator + + "test-classes" + File.separator + enclosingClassName + ".java"); + JarBuilder.buildWithManifest(jarName, manifest, jarClassesDir, testClassNames); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java new file mode 100644 index 00000000000..8f1fc68b81b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, 2017, 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 CommandLineFlagCombo + * AppCDS does not support uncompressed oops + * @requires (vm.gc=="null") & ((vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)) + * @summary Test command line flag combinations that + * could likely affect the behaviour of AppCDS + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main/timeout=240 CommandLineFlagCombo + */ + +import jdk.test.lib.BuildHelper; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + +public class CommandLineFlagCombo { + + // shared base address test table + private static final String[] testTable = { + "-XX:+UseG1GC", "-XX:+UseSerialGC", "-XX:+UseParallelGC", "-XX:+UseConcMarkSweepGC", + "-XX:+FlightRecorder", + "-XX:+UseLargePages", // may only take effect on machines with large-pages + "-XX:+UseCompressedClassPointers", + "-XX:+UseCompressedOops", + "-XX:ObjectAlignmentInBytes=16", + "-XX:ObjectAlignmentInBytes=32", + "-XX:ObjectAlignmentInBytes=64" + }; + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String classList[] = {"Hello"}; + + for (String testEntry : testTable) { + System.out.println("CommandLineFlagCombo = " + testEntry); + + if (skipTestCase(testEntry)) + continue; + + OutputAnalyzer dumpOutput; + + if (testEntry.equals("-XX:+FlightRecorder")) { + dumpOutput = TestCommon.dump(appJar, classList, "-XX:+UnlockCommercialFeatures", testEntry); + } else { + dumpOutput = TestCommon.dump(appJar, classList, testEntry); + } + + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + + OutputAnalyzer execOutput; + if (testEntry.equals("-XX:+FlightRecorder")) { + execOutput = TestCommon.exec(appJar, "-XX:+UnlockCommercialFeatures", testEntry, "Hello"); + } else { + execOutput = TestCommon.exec(appJar, testEntry, "Hello"); + } + TestCommon.checkExec(execOutput, "Hello World"); + } + + for (int i=0; i<2; i++) { + String g1Flag, serialFlag; + + // Interned strings are supported only with G1GC. However, we should not crash if: + // 0: archive has shared strings, but run time doesn't support shared strings + // 1: archive has no shared strings, but run time supports shared strings + + String dump_g1Flag = "-XX:" + (i == 0 ? "+" : "-") + "UseG1GC"; + String run_g1Flag = "-XX:" + (i != 0 ? "+" : "-") + "UseG1GC"; + String dump_serialFlag = "-XX:" + (i != 0 ? "+" : "-") + "UseSerialGC"; + String run_serialFlag = "-XX:" + (i == 0 ? "+" : "-") + "UseSerialGC"; + + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, classList, dump_g1Flag, dump_serialFlag); + + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + + OutputAnalyzer execOutput = TestCommon.exec(appJar, run_g1Flag, run_serialFlag, "Hello"); + TestCommon.checkExec(execOutput, "Hello World"); + } + } + + private static boolean skipTestCase(String testEntry) throws Exception { + if (Platform.is32bit()) + { + if (testEntry.equals("-XX:+UseCompressedOops") || + testEntry.equals("-XX:+UseCompressedClassPointers") || + testEntry.contains("ObjectAlignmentInBytes") ) + { + System.out.println("Test case not applicable on 32-bit platforms"); + return true; + } + } + + if (!BuildHelper.isCommercialBuild() && testEntry.equals("-XX:+FlightRecorder")) + { + System.out.println("Test case not applicable on non-commercial builds"); + return true; + } + + return false; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java new file mode 100644 index 00000000000..75effb9926c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014, 2017, 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 CommandLineFlagComboNegative + * @summary Test command line flag combinations that differ between + * the dump and execute steps, in such way that they cause errors + * E.g. use compressed oops for creating and archive, but then + * execute w/o compressed oops + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main CommandLineFlagComboNegative + */ + +import java.util.ArrayList; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + +public class CommandLineFlagComboNegative { + + private class TestVector { + public String testOptionForDumpStep; + public String testOptionForExecuteStep; + public String expectedErrorMsg; + public int expectedErrorCode; + + public TestVector(String testOptionForDumpStep, String testOptionForExecuteStep, + String expectedErrorMsg, int expectedErrorCode) { + this.testOptionForDumpStep=testOptionForDumpStep; + this.testOptionForExecuteStep=testOptionForExecuteStep; + this.expectedErrorMsg=expectedErrorMsg; + this.expectedErrorCode=expectedErrorCode; + } + } + + private ArrayList<TestVector> testTable = new ArrayList<TestVector>(); + + private void initTestTable() { + // These options are not applicable on 32-bit platforms + if (Platform.is64bit()) { + testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=8", "-XX:ObjectAlignmentInBytes=16", + "An error has occurred while processing the shared archive file", 1) ); + testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=64", "-XX:ObjectAlignmentInBytes=32", + "An error has occurred while processing the shared archive file", 1) ); + testTable.add( new TestVector("-XX:+UseCompressedOops", "-XX:-UseCompressedOops", + "Class data sharing is inconsistent with other specified options", 1) ); + testTable.add( new TestVector("-XX:+UseCompressedClassPointers", "-XX:-UseCompressedClassPointers", + "Class data sharing is inconsistent with other specified options", 1) ); + } + } + + private void runTests() throws Exception + { + for (TestVector testEntry : testTable) { + System.out.println("CommandLineFlagComboNegative: dump = " + testEntry.testOptionForDumpStep); + System.out.println("CommandLineFlagComboNegative: execute = " + testEntry.testOptionForExecuteStep); + + String appJar = JarBuilder.getOrCreateHelloJar(); + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, new String[] {"Hello"}, testEntry.testOptionForDumpStep); + + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + + OutputAnalyzer execOutput = TestCommon.exec(appJar, testEntry.testOptionForExecuteStep, "Hello"); + execOutput.shouldContain(testEntry.expectedErrorMsg); + execOutput.shouldHaveExitValue(testEntry.expectedErrorCode); + } + } + + public static void main(String[] args) throws Exception { + CommandLineFlagComboNegative thisClass = new CommandLineFlagComboNegative(); + thisClass.initTestTable(); + thisClass.runTests(); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java b/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java new file mode 100644 index 00000000000..cfb5d20ba5c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This class consists exclusively of static utility methods for invoking the + * java compiler. + * + * This class will eventually move to jdk.testlibrary. + */ + +public final class CompilerUtils { + private CompilerUtils() { } + + /** + * Compile all the java sources in {@code <source>/**} to + * {@code <destination>/**}. The destination directory will be created if + * it doesn't exist. + * + * All warnings/errors emitted by the compiler are output to System.out/err. + * + * @return true if the compilation is successful + * + * @throws IOException if there is an I/O error scanning the source tree or + * creating the destination directory + */ + public static boolean compile(Path source, Path destination, String ... options) + throws IOException + { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null); + + List<Path> sources + = Files.find(source, Integer.MAX_VALUE, + (file, attrs) -> (file.toString().endsWith(".java"))) + .collect(Collectors.toList()); + + Files.createDirectories(destination); + jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, + Arrays.asList(destination)); + + List<String> opts = Arrays.asList(options); + JavaCompiler.CompilationTask task + = compiler.getTask(null, jfm, null, opts, null, + jfm.getJavaFileObjectsFromPaths(sources)); + + return task.call(); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/DumpClassList.java b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java new file mode 100644 index 00000000000..c2670a7ef45 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary DumpLoadedClassList should exclude generated classes, classes in bootclasspath/a and + * --patch-module. + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @compile test-classes/ArrayListTest.java + * @run main DumpClassList + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class DumpClassList { + public static void main(String[] args) throws Exception { + // build The app + String[] appClass = new String[] {"ArrayListTest"}; + String classList = "app.list"; + + JarBuilder.build("app", appClass[0]); + String appJar = TestCommon.getTestJar("app.jar"); + + // build patch-module + String source = "package java.lang; " + + "public class NewClass { " + + " static { " + + " System.out.println(\"NewClass\"); "+ + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("java/lang/NewClass", + InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"), + System.getProperty("test.classes")); + + String patchJar = JarBuilder.build("javabase", "java/lang/NewClass"); + + // build bootclasspath/a + String source2 = "package boot.append; " + + "public class Foo { " + + " static { " + + " System.out.println(\"Foo\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("boot/append/Foo", + InMemoryJavaCompiler.compile("boot.append.Foo", source2), + System.getProperty("test.classes")); + + String appendJar = JarBuilder.build("bootappend", "boot/append/Foo"); + + // dump class list + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + "-XX:DumpLoadedClassList=" + classList, + "--patch-module=java.base=" + patchJar, + "-Xbootclasspath/a:" + appendJar, + "-cp", + appJar, + appClass[0]); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dumpClassList"); + TestCommon.checkExecReturn(output, 0, true, + "hello world", + "skip writing class java/lang/NewClass") // skip classes outside of jrt image + .shouldNotContain("skip writing class boot/append/Foo"); // but classes on -Xbootclasspath/a should not be skipped + + output = TestCommon.createArchive(appJar, appClass, + "-Xbootclasspath/a:" + appendJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintSystemDictionaryAtExit", + "-XX:SharedClassListFile=" + classList); + TestCommon.checkDump(output) + .shouldNotContain("Preload Warning: Cannot find java/lang/invoke/LambdaForm") + .shouldNotContain("Preload Warning: Cannot find boot/append/Foo") + .shouldContain("boot.append.Foo, loader <shared, not restored>"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt new file mode 100644 index 00000000000..1f4be1af531 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt @@ -0,0 +1,11 @@ +VERSION: 1.0 +@SECTION: Symbol +0 -1: +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +11 -1 linkMethod +18 -1: type can't be null +20 -1: isAlphaNumericString +43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z +1 -1: \t +15 -1: IntCumulateTask +1 -1: \n diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt new file mode 100644 index 00000000000..f45450da674 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt @@ -0,0 +1,5 @@ +@SECTION: Symbol +20 -1: isAlphaNumericString +43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z +15 -1: IntCumulateTask +1 -1: \n diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt new file mode 100644 index 00000000000..360e94b24fd --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt @@ -0,0 +1,13 @@ +VERSION: 1.0 +@SECTION: Symbol +11 -1: linkMethod +18 -1: isAlphaNumericString +33 -1: java/util/Locale$LocaleNameGetter +23 -1: sun/invoke/util/Wrapper +12 -1: reduceToLong +11 -1: setReadOnly +8 -1: endsWith +55 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;TT;)V +20 -1: createAnnotationData +6 -1: OfLong +17 -1: getClassSignature diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java new file mode 100644 index 00000000000..4fc3bb8757e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Adding extra symbols into CDS archive using -XX:SharedArchiveConfigFile + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main ExtraSymbols + */ + +import java.io.*; +import jdk.test.lib.process.OutputAnalyzer; + +public class ExtraSymbols { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + + // 1. Dump without extra symbols. + OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello")); + checkOutput(output); + int numEntries1 = numOfEntries(output); + + // 2. Dump an archive with extra symbols. All symbols in + // ExtraSymbols.symbols.txt are valid. Dumping should succeed. + output = TestCommon.dump(appJar, TestCommon.list("Hello"), + "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("ExtraSymbols.symbols.txt")); + checkOutput(output); + int numEntries2 = numOfEntries(output); + if (numEntries2 <= numEntries1) { + throw new RuntimeException("No extra symbols added to archive"); + } + output = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(output); + + // 3. Dump with invalid symbol files. Dumping should fail. + String invalid_symbol_files[] = {"ExtraSymbols.invalid_1.txt", + "ExtraSymbols.invalid_2.txt", + "ExtraSymbols.invalid_3.txt"}; + String err_msgs[] = {"Corrupted at line", + "wrong version of hashtable dump file", + "Corrupted at line"}; + for (int i = 0; i < invalid_symbol_files.length; i++) { + output = TestCommon.dump(appJar, TestCommon.list("Hello"), + "-XX:SharedArchiveConfigFile=" + + TestCommon.getSourceFile(invalid_symbol_files[i])); + output.shouldContain("Error occurred during initialization of VM"); + output.shouldContain(err_msgs[i]); + } + } + + static int numOfEntries(OutputAnalyzer output) { + String s = output.firstMatch("Number of entries : .*"); + String subs[] = s.split("[:]"); + int numEntries = Integer.parseInt(subs[1].trim()); + return numEntries; + } + + static void checkOutput(OutputAnalyzer output) throws Exception { + output.shouldContain("Loading classes to share"); + output.shouldContain("Shared symbol table stats -------- base:"); + output.shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt new file mode 100644 index 00000000000..9a75ededb39 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt @@ -0,0 +1,10826 @@ +VERSION: 1.0 +@SECTION: Symbol +69 -1: ------------------------------------------------------------123456789 +68 -1: # The values in this file are only used for testing the operation of +63 -1: # adding extra symbols into the CDS archive. None of the values +70 -1: # are interpreted in any way. So even if they contain names of classes +70 -1: # that have been renamed or removed, or string literals that have been +66 -1: # changed or remove from Java source code, it would not affect the +26 -1: # correctness of the test. +0 -1: +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +11 -1: linkMethod +18 -1: type can't be null +20 -1: isAlphaNumericString +43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z +72 -1: (Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process; +1 -1: \t +15 -1: IntCumulateTask +1 -1: \n +33 -1: java/util/Locale$LocaleNameGetter +23 -1: sun/invoke/util/Wrapper +57 -1: (Ljava/io/InputStream;Ljava/nio/charset/CharsetDecoder;)V +12 -1: reduceToLong +11 -1: setReadOnly +34 -1: (Ljava/lang/reflect/Executable;)[B +54 -1: ([Ljava/net/URL;Ljava/security/AccessControlContext;)V +15 -1: LegacyMergeSort +8 -1: endsWith +55 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;TT;)V +20 -1: createAnnotationData +6 -1: OfLong +90 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)Ljava/util/Map<TK;TV;>; +1 -1: +17 -1: getClassSignature +1 -1: " +1 -1: # +1 -1: ( +21 -1: MethodHandleImpl.java +10 -1: getUTF8At0 +1 -1: ) +1 -1: * +1 -1: + +1 -1: , +1 -1: - +1 -1: . +18 -1: unsignedEntryNames +1 -1: / +1 -1: 0 +19 -1: java/io/InputStream +38 -1: java/util/concurrent/ThreadLocalRandom +1 -1: : +1 -1: ; +1 -1: < +13 -1: getAndAddLong +1 -1: = +1 -1: > +1 -1: ? +20 -1: getMethodAtIfLoaded0 +1 -1: @ +1 -1: A +7 -1: isAlive +1 -1: B +10 -1: checkIndex +1 -1: C +1 -1: D +1 -1: E +1 -1: F +1 -1: I +30 -1: sun/misc/JavaUtilZipFileAccess +11 -1: classloader +1 -1: J +1 -1: L +14 -1: packageEnabled +8 -1: ([BIII)V +24 -1: Ljava/io/BufferedWriter; +1 -1: S +32 -1: (Ljava/util/function/Consumer;)V +11 -1: refKindName +1 -1: U +1 -1: V +3 1: yyy +18 -1: JavaNetAccess.java +1 -1: Z +7 -1: members +1 -1: [ +1 -1: ] +13 -1: ShortLanguage +1 -1: _ +9 -1: invoke__L +28 -1: (D)Ljava/lang/StringBuilder; +15 -1: isInvokeSpecial +1 -1: c +17 -1: subListRangeCheck +1 -1: e +29 -1: Ljava/security/AllPermission; +27 -1: (C)Ljava/lang/StringBuffer; +28 -1: ([Ljava/lang/Comparable;II)V +50 -1: (Ljava/util/zip/ZipFile;Ljava/util/zip/Inflater;)V +9 -1: invoke__V +1 -1: m +101 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetEncoder;)Lsun/nio/cs/StreamEncoder; +13 -1: MAX_SURROGATE +18 -1: Ljava/lang/String; +21 -1: ensureProtectedAccess +18 -1: getIfModifiedSince +1 -1: r +9 -1: setExtra0 +1 -1: s +47 -1: Ljava/lang/Enum<Lsun/launcher/LauncherHelper;>; +1 -1: x +1 -1: { +7 -1: getLast +1 -1: | +1 -1: } +1 -1: ~ +71 -1: (Ljava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$KeySetView; +34 -1: (Ljava/nio/charset/Charset;[BII)[C +10 -1: DST_NSHIFT +25 -1: ForEachTransformedKeyTask +26 -1: Ljava/nio/charset/Charset; +56 -1: (Ljava/lang/reflect/Method;)Lsun/reflect/MethodAccessor; +22 -1: StackTraceElement.java +24 -1: sun.zip.zipFile.openTime +27 -1: JNI_COPY_TO_ARRAY_THRESHOLD +26 -1: java/lang/ClassValue$Entry +19 -1: [Ljava/lang/Thread; +56 -1: (Ljava/lang/ClassLoader$NativeLibrary;)Ljava/lang/Class; +7 -1: message +18 -1: parameterToArgSlot +20 -1: [[Ljava/lang/String; +11 -1: bumpVersion +26 -1: Ljava/lang/reflect/Method; +9 -1: getMethod +6 -1: (I)TE; +49 -1: (Ljava/lang/String;)Ljava/lang/invoke/MemberName; +33 -1: sun/misc/URLClassPath$JarLoader$1 +57 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)V +33 -1: sun/misc/URLClassPath$JarLoader$2 +33 -1: sun/misc/URLClassPath$JarLoader$3 +87 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node; +19 -1: FileDescriptor.java +12 -1: forEachValue +36 -1: (Ljava/util/List;)[Ljava/lang/Class; +53 -1: (Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder; +8 -1: hasArray +4 -1: ROWS +10 -1: linkMethod +9 -1: remaining +23 -1: ARRAY_FLOAT_BASE_OFFSET +35 -1: java/lang/reflect/ReflectPermission +24 -1: ()Ljava/net/InetAddress; +7 -1: ngroups +81 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/Class<*>;>; +10 -1: putTreeVal +4 -1: list +5 -1: trace +7 -1: blocker +21 -1: reset() not supported +8 -1: JAPANESE +11 -1: PRIVATE_USE +53 -1: (Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodHandle; +32 -1: Invalid JavaFX launch parameters +15 -1: SECONDS_PER_DAY +11 -1: UTF_16.java +24 -1: sun/nio/cs/UTF_8$Encoder +102 -1: (Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)V +20 -1: (Lsun/misc/Signal;)V +22 -1: MagicAccessorImpl.java +84 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class; +14 -1: altMetafactory +13 -1: queryOverflow +30 -1: exists, but is not accessible +3 -1: edt +14 -1: MAX_ARRAY_SIZE +20 -1: aliases_UTF_16LE_BOM +34 -1: Ljava/lang/reflect/Constructor<*>; +20 -1: (S)Ljava/lang/Short; +6 -1: STRICT +19 -1: internalCallerClass +27 -1: java/nio/DirectLongBufferRU +13 -1: TIMED_WAITING +15 -1: toGenericString +6 -1: client +10 -1: attachImpl +22 -1: ReflectionFactory.java +8 -1: jsse.jar +37 -1: (IZ)Ljava/lang/AbstractStringBuilder; +41 -1: java/util/LinkedHashMap$LinkedKeyIterator +15 -1: computeIfAbsent +10 -1: GET_TARGET +53 -1: <E:Ljava/lang/Enum<TE;>;>(Ljava/lang/Class<TE;>;)[TE; +35 -1: java/util/Collections$SingletonList +7 -1: addYear +35 -1: Ljava/lang/Class<Ljava/lang/Byte;>; +65 -1: (Ljava/util/LinkedHashMap$Entry;Ljava/util/LinkedHashMap$Entry;)V +14 -1: image/x-bitmap +10 -1: (IIII[JI)V +50 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/net/URL;)V +13 -1: getLineNumber +20 -1: toUpperCaseCharArray +62 -1: (Ljava/util/concurrent/locks/Condition;)Ljava/util/Collection; +11 -1: rotateRight +10 -1: checkPtype +85 -1: (JLjava/util/function/ToLongFunction<-TK;>;JLjava/util/function/LongBinaryOperator;)J +81 -1: (Ljava/lang/Class;Ljava/lang/reflect/Constructor;)Ljava/lang/reflect/Constructor; +15 -1: Illegal style: +28 -1: (Ljava/lang/StringBuilder;)V +41 -1: 1.8.0-internal-iklam_2013_11_27_21_25-b00 +25 -1: Invalid authority field: +55 -1: (Ljava/lang/CharSequence;)Ljava/util/function/Supplier; +12 -1: staticOffset +32 -1: java/util/HashMap$KeySpliterator +13 -1: javaNioAccess +24 -1: (Ljava/util/SortedSet;)V +17 -1: thenComparingLong +2 -1: \n\n +22 -1: registerFieldsToFilter +34 -1: java/lang/invoke/LambdaMetafactory +225 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsTask;Ljava/util/function/BiFunction;Ljava/util/function/BiFunction;)V +26 -1: [[Ljava/lang/CharSequence; +32 -1: java/util/Collections$CheckedMap +147 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSequentialList<TE;>;Ljava/util/List<TE;>;Ljava/util/Deque<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable; +204 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; +27 -1: sun/nio/cs/UTF_16BE$Decoder +12 -1: getZoneInfo0 +77 -1: (Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType; +9 -1: Traverser +35 -1: Ljava/lang/ref/ReferenceQueue<TT;>; +27 -1: lambda$comparing$ea9a8b3a$1 +7 -1: ([CI)[C +6 -1: getenv +9 -1: newMethod +52 -1: <T:Ljava/lang/Object;>Ljava/lang/reflect/Executable; +164 -1: (Ljava/security/ProtectionDomain;Ljava/security/DomainCombiner;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)V +40 -1: (Ljava/lang/String;)Ljava/util/TimeZone; +11 -1: countTokens +202 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;Ljava/util/Map$Entry<TK;TV;>;>;Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;Ljava/io/Serializable; +78 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Collection<TT;>; +34 -1: (Ljava/lang/reflect/Constructor;)I +15 -1: comparingDouble +24 -1: ()Ljava/util/Collection; +14 -1: invokeFinalize +14 -1: encodeISOArray +77 -1: (Ljava/lang/ref/Reference;Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference; +11 -1: bad index: +34 -1: (Ljava/lang/reflect/Constructor;)V +68 -1: (Ljava/util/jar/JarEntry;Lsun/security/util/ManifestEntryVerifier;)V +53 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIIJ)V +27 -1: ([CII)Ljava/nio/CharBuffer; +6 -1: setOut +41 -1: (ILjava/lang/Object;Ljava/lang/Object;I)V +12 -1: MIN_EXPONENT +30 -1: PrivilegedExceptionAction.java +18 -1: key cannot be null +6 -1: CENHDR +73 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>; +23 -1: java/lang/reflect/Array +8 -1: AF_LIMIT +2 -1: \r\n +11 -1: getFileName +10 -1: parseShort +22 -1: java/lang/LinkageError +15 -1: FT_LAST_WRAPPER +32 -1: java/util/ArrayDeque$DeqIterator +24 -1: pc-multilingual-850+euro +3 -1: zfc +14 -1: incrementExact +38 -1: (IIII)Lsun/util/calendar/CalendarDate; +8 -1: (II[BI)V +8 -1: isLocked +13 -1: ZoneInfo.java +36 -1: (Lsun/util/calendar/CalendarDate;J)V +35 -1: java/lang/invoke/MethodHandleImpl$1 +31 -1: (Ljava/util/Comparator<-TE;>;)V +19 -1: CharsetEncoder.java +52 -1: <T:Ljava/lang/Object;>()Ljava/util/Enumeration<TT;>; +44 -1: (Ljava/io/InputStream;)Ljava/io/InputStream; +7 -1: field +5 -1: abort +25 -1: java/lang/SecurityManager +66 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToDoubleTask +1316 -1: \xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe6\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe6\x80\x80\xe4\x80\x8c\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xe2\xa0\x80\x18\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\x18\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xee\xa0\x80\x15\xee\xa0\x80\x16\xe6\xa0\x80\x18\xe2\x80\x80\x19\xe3\xa0\x80\x18\xe2\x80\x80\x14\xe3\xa0\x80\x18\xe3\xa0\x80\x18\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe3\xa0\x80\x18\xe6\xa0\x80\x18\xee\xa0\x80\x19\xe6\xa0\x80\x19\xee\xa0\x80\x19\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xee\xa0\x80\x15\xe6\xa0\x80\x18\xee\xa0\x80\x16\xe6\xa0\x80\x1b\xe6\xa0\x80\xe5\x80\x97\xe6\xa0\x80\x1b\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xee\xa0\x80\x15\xe6\xa0\x80\x19\xee\xa0\x80\x16\xe6\xa0\x80\x19\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\x80\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe3\xa0\x80\x0c\xe6\xa0\x80\x18\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe6\xa0\x80\x1c\xe6\xa0\x80\x18\xe6\xa0\x80\x1b\xe6\xa0\x80\x1c\xc0\x80\xe7\x80\x85\xee\xa0\x80\x1d\xe6\xa0\x80\x19\xe4\xa0\x80\xe1\x80\x90\xe6\xa0\x80\x1c\xe6\xa0\x80\x1b\xe2\xa0\x80\x1c\xe2\xa0\x80\x19\xe1\xa0\x80\xd8\x8b\xe1\xa0\x80\xd8\x8b\xe6\xa0\x80\x1b\xdf\xbd\xe7\x80\x82\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xe6\xa0\x80\x1b\xe1\xa0\x80\xd4\x8b\xc0\x80\xe7\x80\x85\xee\xa0\x80\x1e\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\x18\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xe6\xa0\x80\x19\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xdf\xbd\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xe6\xa0\x80\x19\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xd8\x9d\xe7\x80\x82 +6 -1: (J[I)I +162 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;Ljava/util/Locale$FilteringMode;)Ljava/util/List<Ljava/util/Locale;>; +21 -1: getQualifiedFieldName +46 -1: Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>; +47 -1: (Ljava/util/Collection;Ljava/util/Collection;)Z +10 -1: getRuntime +30 -1: threadLocalRandomSecondarySeed +18 -1: (Ljava/io/File;I)J +10 -1: methodName +34 -1: sun/reflect/generics/tree/TypeTree +35 -1: (Ljava/io/File;)[Ljava/lang/String; +31 -1: java/util/Collections$EmptyList +15 -1: LF_INVINTERFACE +9 -1: notifyAll +18 -1: (Ljava/io/File;I)V +94 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class<*>; +45 -1: (Ljava/lang/String;)Ljava/net/ContentHandler; +3 -1: enc +3 -1: end +18 -1: (Ljava/io/File;I)Z +47 -1: (Ljava/lang/Object;Ljava/lang/reflect/Method;)V +76 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V +19 -1: getURLStreamHandler +46 -1: (Ljava/lang/ClassLoader;Ljava/lang/Class<*>;)V +17 -1: COMPILE_THRESHOLD +15 -1: charset is null +7 -1: ibm-912 +10 -1: basicTypes +7 -1: ibm-914 +78 -1: (Ljava/lang/Class;Ljava/lang/ref/SoftReference;Ljava/lang/ref/SoftReference;)Z +7 -1: ibm-915 +12 -1: JarFileEntry +12 -1: setThreshold +22 -1: (ILjava/lang/Object;)V +55 -1: <T::Lsun/reflect/generics/tree/Tree;>Ljava/lang/Object; +16 -1: Unknown signal: +3 -1: zip +13 -1: CR_UNMAPPABLE +19 -1: getClassAtIfLoaded0 +21 -1: WindowsClientCounters +29 -1: Ljava/lang/invoke/MethodType; +91 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableList<TE;>;Ljava/util/RandomAccess; +23 -1: StackOverflowError.java +13 -1: Launcher.java +9 -1: Signature +7 -1: ibm-920 +153 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;ILjava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V +13 -1: setExtensions +26 -1: [Ljava/lang/ref/Reference; +7 -1: ibm-923 +12 -1: BMH.reinvoke +34 -1: java/lang/IllegalArgumentException +53 -1: (Ljava/lang/String;)Ljava/lang/NumberFormatException; +5 -1: .dirs +13 -1: finishToArray +22 -1: (ZI)Ljava/lang/String; +84 -1: (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/RuntimeException; +15 -1: charsetProvider +24 -1: ()Ljava/lang/Class<TT;>; +10 -1: wordsInUse +26 -1: (Ljava/io/ExpiringCache;)I +53 -1: ()Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>; +25 -1: com/sun/management/GcInfo +26 -1: getCompatibilityExtensions +69 -1: (Ljava/lang/ref/ReferenceQueue;Ljava/util/concurrent/ConcurrentMap;)V +15 -1: getConstantPool +24 -1: [[Ljava/lang/Comparable; +26 -1: (Ljava/io/ExpiringCache;)V +8 -1: getTable +53 -1: sun/reflect/generics/repository/ConstructorRepository +5 -1: range +36 -1: (Ljava/lang/String;)Ljava/lang/Byte; +72 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)V +60 -1: <T:Ljava/lang/Object;>([TT;TT;Ljava/util/Comparator<-TT;>;)I +20 -1: (Ljava/nio/Bits$1;)V +30 -1: ()Ljava/util/Spliterator<TK;>; +6 -1: ([BB)I +53 -1: (Ljava/lang/ref/Finalizer;Lsun/misc/JavaLangAccess;)V +11 -1: memberTypes +45 -1: (ILjava/lang/String;)Ljava/lang/StringBuffer; +12 -1: OTHER_SYMBOL +65 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +43 -1: (Ljava/util/Set;)[Ljava/lang/reflect/Field; +30 -1: (Ljava/lang/ref/Reference$1;)V +18 -1: GREGORIAN_INSTANCE +31 -1: Ljava/lang/FunctionalInterface; +57 -1: (Ljava/lang/Error;Ljava/lang/Exception;)Ljava/lang/Error; +54 -1: ([Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field; +14 -1: not an array: +6 -1: ([BB)V +9 -1: ISO8859_1 +8 -1: addTrans +27 -1: getFunctionalInterfaceClass +29 -1: lambda$comparingInt$7b0bb60$1 +8 -1: TreeNode +138 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Dictionary<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable; +3 -1: era +22 -1: fakeMethodHandleInvoke +9 -1: addToList +39 -1: (Ljava/lang/Class;[Ljava/lang/String;)V +17 -1: launchApplication +21 -1: randomNumberGenerator +51 -1: Ljava/lang/ThreadLocal<Ljava/lang/ThreadLocal<*>;>; +35 -1: java/io/ObjectOutputStream$PutField +42 -1: (ILjava/util/function/IntBinaryOperator;)I +3 -1: err +13 -1: cachedDecoder +32 -1: sun/util/calendar/ZoneInfoFile$1 +23 -1: doIntersectionPrivilege +19 -1: cspc850multilingual +56 -1: Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>; +11 -1: loader_data +27 -1: (Ljava/util/jar/Manifest;)V +5 -1: files +90 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/String;Lsun/util/calendar/CalendarSystem;>; +36 -1: [[Ljava/lang/invoke/LambdaForm$Name; +5 -1: lines +55 -1: (Lsun/misc/URLClassPath$JarLoader;)Lsun/misc/MetaIndex; +9 -1: ansi-1251 +15 -1: refKindIsMethod +29 -1: java/lang/reflect/Constructor +3 -1: est +19 -1: Lsun/misc/Launcher; +109 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;Ljava/security/AccessControlContext;)TT; +10 -1: getOffsets +9 -1: removeAll +23 -1: java/util/regex/Matcher +8 -1: sumCount +7 -1: implies +10 -1: MAIN_CLASS +75 -1: (Ljava/util/List<Lsun/launcher/LauncherHelper$StdArg;>;)[Ljava/lang/String; +11 -1: getISO3Code +4 -1: high +53 -1: (TK;Ljava/util/function/BiFunction<-TK;-TV;+TV;>;)TV; +17 -1: setNormalizedDate +23 -1: AbstractRepository.java +28 -1: java/util/LinkedList$ListItr +8 -1: isFrozen +38 -1: (Ljava/lang/String;Z)Ljava/lang/Class; +16 -1: ReflectUtil.java +30 -1: ()Ljava/util/stream/IntStream; +57 -1: (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; +11 -1: getResource +16 -1: ThreadDeath.java +24 -1: unmodifiableNavigableSet +59 -1: (Ljava/lang/String;)Ljava/util/Enumeration<Ljava/net/URL;>; +24 -1: java.security.auth.debug +58 -1: (Ljava/io/FileInputStream;)Ljava/nio/channels/FileChannel; +25 -1: ()Ljava/util/Enumeration; +11 -1: getInstance +6 -1: MONDAY +15 -1: jdkMinorVersion +16 -1: newThreadWithAcc +6 -1: CENHOW +32 -1: Max. Heap Size (Estimated): +61 -1: (Ljava/lang/invoke/MethodType;Z)Ljava/lang/invoke/LambdaForm; +11 -1: windows-932 +7 -1: Index: +11 -1: composeList +6 -1: utf-16 +6 -1: ibm437 +10 -1: getJarFile +8 -1: , rem = +13 -1: multiNewArray +14 -1: getDefaultPort +39 -1: Ljava/security/cert/CertificateFactory; +10 -1: L_RESERVED +19 -1: getMethodAtIfLoaded +8 -1: needCast +8 -1: IS_FIELD +15 -1: ClassValue.java +31 -1: ()Ljava/util/function/Supplier; +125 -1: (Ljava/lang/Class<*>;)Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>; +34 -1: lambda$comparingByValue$827a17d5$1 +4 -1: NONE +21 -1: java/nio/DoubleBuffer +33 -1: ()Lsun/reflect/LangReflectAccess; +26 -1: invalid compression method +6 -1: (TK;)Z +16 -1: FT_UNCHECKED_REF +14 -1: getGenericType +17 -1: pathSeparatorChar +8 -1: writeUTF +8 -1: NO_PROXY +188 -1: (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;)V +13 -1: finalRefCount +12 -1: NF_checkCast +6 -1: utf-32 +26 -1: (Ljava/util/ArrayDeque;I)Z +19 -1: prefetchWriteStatic +14 -1: computeInvoker +5 -1: cap= +19 -1: generateCertificate +15 -1: methodModifiers +3 -1: exc +27 -1: ()Lsun/misc/JavaLangAccess; +5 -1: State +14 -1: NullComparator +10 -1: getClassAt +15 -1: printProperties +110 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor; +40 -1: (Ljava/util/List<*>;Ljava/util/Random;)V +63 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Method;>; +28 -1: (I)Ljava/lang/StringBuilder; +48 -1: ([DIILjava/util/function/DoubleBinaryOperator;)V +3 -1: exp +11 -1: interpret_L +17 -1: Serializable.java +8 -1: FJDouble +12 -1: HashMap.java +9 -1: sys_paths +17 -1: getMainAttributes +14 -1: asDoubleBuffer +10 -1: buildNames +26 -1: TOPLEVEL_WINDOW_PERMISSION +4 -1: Type +31 -1: (Ljava/util/Collection<+TE;>;)V +64 -1: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class<*>; +100 -1: <E:Ljava/lang/Object;>(Ljava/util/Collection<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Collection<TE;>; +10 -1: checkError +31 -1: (Ljava/util/Collection<+TE;>;)Z +31 -1: java/lang/NoSuchMethodException +6 -1: attach +87 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +9 -1: writeChar +44 -1: java/util/ArraysParallelSortHelpers$FJObject +34 -1: (Ljava/lang/Class;Ljava/io/File;)Z +21 -1: java/util/zip/ZipFile +5 -1: dirty +6 -1: (JIZ)V +12 -1: leftoverChar +39 -1: is being loaded in another classloader +10 -1: writeBytes +6 -1: unlink +41 -1: (TT;Ljava/lang/ref/ReferenceQueue<TT;>;)V +21 -1: getBootstrapResources +95 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/io/Serializable; +10 -1: PathStatus +25 -1: java/io/InputStreamReader +15 -1: ISO_8859-9:1989 +37 -1: java/lang/ExceptionInInitializerError +14 -1: exceptionTypes +19 -1: BufferedReader.java +34 -1: Could not create SecurityManager: +13 -1: definePackage +12 -1: getAndAddInt +50 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; +30 -1: (Ljava/lang/SecurityManager;)V +12 -1: fxLaunchName +22 -1: BINARYSEARCH_THRESHOLD +29 -1: JVMTI_THREAD_STATE_TERMINATED +9 -1: fullFence +60 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration<Ljava/net/URL;>; +29 -1: java/lang/Thread$WeakClassKey +47 -1: (Ljava/nio/charset/Charset;Ljava/lang/String;)V +8 -1: (TV;)TV; +60 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;I)V +47 -1: java/security/cert/CertificateEncodingException +12 -1: BA_DIRECTORY +53 -1: (Ljava/lang/Class;ZLjava/lang/Class;)Ljava/util/List; +9 -1: checkRead +6 -1: <init> +4 -1: args +17 -1: genericMethodType +10 -1: writeFloat +26 -1: Can't handle static method +49 -1: (Ljava/lang/String;)Ljava/lang/invoke/MethodType; +22 -1: MapReduceKeysToIntTask +17 -1: jvm_micro_version +29 -1: (Ljava/util/Map<+TK;+TV;>;Z)V +40 -1: ([Ljava/lang/Object;Ljava/lang/Object;)I +7 -1: vmindex +22 -1: maybeCompileToBytecode +89 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl; +11 -1: getLauncher +17 -1: jvm_major_version +36 -1: ([IIII)Ljava/util/Spliterator$OfInt; +40 -1: ([Ljava/lang/Object;Ljava/lang/Object;)V +33 -1: IllegalMonitorStateException.java +73 -1: ([ILjava/util/function/IntUnaryOperator;)Ljava/util/function/IntConsumer; +39 -1: (Ljava/lang/String;)Ljava/lang/Package; +29 -1: java/lang/CharacterDataLatin1 +52 -1: (Ljava/lang/annotation/Annotation;)Ljava/lang/Class; +49 -1: (Ljava/lang/CharSequence;II)Ljava/nio/CharBuffer; +53 -1: (Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder; +22 -1: Ljava/net/FileNameMap; +16 -1: isAnonymousClass +4 -1: item +7 -1: compute +12 -1: user.country +22 -1: malformed context url: +16 -1: jvm_build_number +69 -1: (Ljava/lang/ThreadLocal;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +17 -1: getDirectionality +4 -1: save +8 -1: UNMARKED +58 -1: (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V +12 -1: searchFields +9 -1: frequency +23 -1: getLocalizedInputStream +2 -1: +126 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>; +39 -1: (Ljava/lang/String;Z)Ljava/lang/String; +7 -1: setZone +2 -1: " +9 -1: checkRef( +11 -1: loadFromXML +49 -1: (Ljava/util/jar/JarFile;Ljava/util/Enumeration;)V +68 -1: (IILsun/util/calendar/CalendarDate;)Lsun/util/calendar/CalendarDate; +2 -1: ( +54 -1: (Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate; +6 -1: rewind +13 -1: getAndSetLong +30 -1: java/lang/invoke/MethodHandles +11 -1: ListPattern +97 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/RandomAccess;Ljava/io/Serializable; +25 -1: (Ljava/io/InputStream;I)V +9 -1: setMethod +10 -1: H_REG_NAME +39 -1: ([Ljava/lang/Object;)Ljava/lang/Object; +16 -1: AbstractMap.java +68 -1: Ljava/util/Hashtable<Ljava/lang/String;Ljava/net/URLStreamHandler;>; +58 -1: (Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process; +23 -1: getFileSystemAttributes +12 -1: toSurrogates +2 -1: !/ +5 -1: empty +24 -1: isUnicodeIdentifierStart +35 -1: sun/nio/cs/StandardCharsets$Classes +27 -1: [Ljava/security/Permission; +13 -1: getDefinition +11 -1: permission= +42 -1: (ILjava/lang/String;)Ljava/nio/ByteBuffer; +69 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType; +3 1: zzz +17 -1: hasLongPrimitives +2 -1: != +13 -1: getInterfaces +2 -1: " +11 -1: noInflation +14 -1: aliases_UTF_16 +2 -1: ") +17 -1: ()Ljava/util/Map; +55 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>; +6 -1: charAt +12 -1: getStringAt0 +10 -1: superClone +28 -1: Ljava/util/AbstractSet<TK;>; +40 -1: java/lang/ref/Reference$ReferenceHandler +22 -1: NaturalOrderComparator +9 -1: markValue +9 -1: getRegion +26 -1: null permissions parameter +17 -1: Ljava/util/Stack; +14 -1: codebase=<URL> +36 -1: (Ljava/util/List;)Ljava/lang/Object; +17 -1: setJavaLangAccess +16 -1: hasQueuedThreads +5 -1: (CC)I +8 -1: toString +5 -1: (CC)J +11 -1: permissions +10 -1: getHeaders +27 -1: java/io/BufferedInputStream +21 -1: unicodelittleunmarked +51 -1: (Ljava/net/URLClassLoader;Ljava/util/Enumeration;)V +14 -1: generateMethod +11 -1: skipForward +55 -1: java/util/concurrent/ConcurrentHashMap$ForEachEntryTask +5 -1: (CC)Z +15 -1: getURLClassPath +84 -1: Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet<Ljava/lang/invoke/MethodType;>; +23 -1: primitiveParameterCount +8 -1: security +14 -1: aliases_UTF_32 +22 -1: ()Ljava/util/Set<TK;>; +9 -1: listFiles +15 -1: insertElementAt +42 -1: Ljava/util/Comparator<Ljava/lang/String;>; +11 -1: getUserInfo +46 -1: ([JIILjava/util/function/LongBinaryOperator;)V +23 -1: (Ljava/util/Iterator;)V +46 -1: ([Ljava/lang/Object;II)Ljava/util/Spliterator; +67 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Object;)Ljava/lang/Object; +14 -1: normalizeMonth +13 -1: getStackTrace +51 -1: java/lang/invoke/MethodType$ConcurrentWeakInternSet +8 -1: makeChar +2 -1: %% +9 -1: getTarget +21 -1: packageDefinitionLock +52 -1: java/util/concurrent/ConcurrentHashMap$ValueIterator +12 -1: OTHER_NUMBER +22 -1: java/util/jar/JarEntry +11 -1: access$1000 +16 -1: NON_SPACING_MARK +13 -1: last-modified +68 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/Void;>; +16 -1: Australia/Darwin +55 -1: (Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;)V +29 -1: Ljava/lang/ref/WeakReference; +19 -1: expungeStaleEntries +41 -1: Ljava/security/PrivilegedActionException; +6 -1: update +23 -1: (Ljava/lang/Object;JB)V +10 -1: newUpdater +39 -1: (Ljava/net/URL;)Ljava/util/jar/JarFile; +25 -1: Ljava/net/ContentHandler; +21 -1: ARRAY_INT_INDEX_SCALE +13 -1: hasSurrogates +27 -1: (Ljava/lang/ThreadGroup;Z)Z +20 -1: createGCNotification +21 -1: negative day of week +11 -1: getInIfOpen +22 -1: java/util/RandomAccess +24 -1: available locales = +21 -1: AccessController.java +44 -1: can not access a protected member of class +4 -1: LONG +15 -1: objectOnlyTypes +75 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetDecoder;)V +14 -1: getFindClasses +10 -1: storeFence +16 -1: asNormalOriginal +45 -1: (Ljava/lang/String;)Ljava/lang/StringBuilder; +6 -1: millis +16 -1: America/St_Johns +38 -1: ()Ljava/lang/IllegalArgumentException; +37 -1: DIRECTIONALITY_POP_DIRECTIONAL_FORMAT +15 -1: implReplaceWith +29 -1: ([C)Ljava/lang/StringBuilder; +15 -1: Appendable.java +41 -1: (Ljava/lang/String;)Ljava/io/InputStream; +26 -1: Illegal Initial Capacity: +9 -1: checkBase +7 -1: setYear +15 -1: DISPLAY_VARIANT +7 -1: getType +31 -1: Ljava/lang/ref/Reference<+TT;>; +15 -1: isFieldOrMethod +35 -1: appendToClassPathForInstrumentation +16 -1: LocaleNameGetter +7 -1: compact +55 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>; +10 -1: dummyQueue +3 -1: ROC +2 -1: (" +15 -1: checkPermission +38 -1: java/util/zip/ZipFile$ZipEntryIterator +8 -1: hexDigit +8 -1: pairs: +2 -1: () +2 -1: )\n +43 -1: handler for url different from this handler +15 -1: isAutoDetecting +37 -1: (Ljava/util/LinkedList$Node<TE;>;)TE; +11 -1: Unsafe.java +12 -1: windows-1250 +39 -1: java/util/Collections$CheckedCollection +12 -1: windows-1251 +15 -1: codePointAtImpl +12 -1: windows-1252 +12 -1: windows-1253 +12 -1: windows-1254 +58 -1: (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/Integer; +5 -1: deref +12 -1: windows-1255 +12 -1: windows-1256 +12 -1: windows-1257 +12 -1: windows-1258 +14 -1: FT_CHECKED_REF +47 -1: (Ljava/util/Hashtable;Ljava/util/Hashtable$1;)V +37 -1: (J)Lsun/util/calendar/Gregorian$Date; +19 -1: checkPropertyAccess +4 -1: file +17 -1: emptyListIterator +26 -1: sun/util/calendar/ZoneInfo +14 -1: file.separator +4 -1: fill +62 -1: (Ljava/util/Spliterator$OfLong;Z)Ljava/util/stream/LongStream; +18 -1: java/util/Iterator +20 -1: reduceValuesToDouble +12 -1: LF_CS_LINKER +26 -1: java/util/Arrays$ArrayList +45 -1: Ljava/util/concurrent/ConcurrentHashMap$Node; +6 -1: skipLF +2 -1: )= +39 -1: (I[Ljava/lang/invoke/LambdaForm$Name;)I +90 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;)V +18 -1: parameterModifiers +31 -1: (Ljava/util/Collection<+TK;>;)Z +21 -1: proxy can not be null +22 -1: java/io/FileDescriptor +6 -1: Loader +21 -1: numberOfTrailingZeros +10 -1: addMapping +39 -1: (I[Ljava/lang/invoke/LambdaForm$Name;)Z +30 -1: java/util/Locale$LanguageRange +20 -1: getReflectionFactory +16 -1: shouldMeterInput +56 -1: ([Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method; +23 -1: sun/net/ProgressMonitor +510 -1: \xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\x01\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\x01\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80 +28 -1: java/util/Spliterator$OfLong +24 -1: SynchronizedNavigableMap +4 -1: find +6 -1: unsafe +31 -1: java/nio/ByteBufferAsIntBufferB +2 -1: ,\n +6 -1: [ call +14 -1: registerFilter +10 -1: ValuesView +9 -1: untreeify +59 -1: ([Ljava/lang/Object;IILjava/util/function/BinaryOperator;)V +13 -1: getSimpleName +41 -1: (Ljava/util/Vector;Ljava/util/Vector$1;)V +31 -1: java/nio/ByteBufferAsIntBufferL +45 -1: (Ljava/lang/reflect/Field;)Ljava/lang/Object; +13 -1: getDefaultRef +18 -1: mapAlternativeName +30 -1: setDefaultAllowUserInteraction +13 -1: cannotCastMsg +4 -1: )=>{ +7 -1: println +2 -1: , +70 -1: (Ljava/nio/Buffer;IILjava/nio/Buffer;II)Ljava/nio/charset/CoderResult; +32 -1: (ILjava/util/Collection<+TE;>;)Z +9 -1: interpret +104 -1: <E:Ljava/lang/Object;>(Ljava/util/NavigableSet<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/NavigableSet<TE;>; +7 -1: advance +86 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/reflect/Field;>; +11 -1: Stack trace +6 -1: raise0 +68 -1: ()Ljava/util/Collections$UnmodifiableNavigableMap$EmptyNavigableMap; +32 -1: sun/util/calendar/Gregorian$Date +33 -1: java/lang/ref/ReferenceQueue$Lock +19 -1: constructorAccessor +6 -1: IBM367 +18 -1: CharacterData.java +8 -1: parseURL +32 -1: java/io/FilePermissionCollection +7 -1: ([JI)[J +9 -1: JIS_X0201 +2 -1: -1 +8 -1: encoding +63 -1: ([Ljava/util/WeakHashMap$Entry;[Ljava/util/WeakHashMap$Entry;)V +9 -1: localhost +66 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal$1;)V +54 -1: (II[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +67 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; +14 -1: containsAllPDs +23 -1: setAllowUserInteraction +30 -1: Ljava/security/DomainCombiner; +26 -1: Ljava/security/Permission; +30 -1: serializePropertiesToByteArray +112 -1: (Ljava/util/Iterator<Ljava/nio/charset/Charset;>;Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;)V +2 -1: .. +6 -1: LOCEXT +2 -1: ./ +26 -1: (Ljava/nio/ByteBuffer;II)V +18 -1: (Ljava/io/File;J)Z +45 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;)V +17 -1: asCollectorChecks +7 -1: actions +5 -1: ([I)I +20 -1: asChange_otherthread +20 -1: forInputStreamReader +69 -1: java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject +5 -1: FINAL +17 -1: staticPermissions +44 -1: (Ljava/lang/Object;)Ljava/lang/StringBuffer; +5 -1: ([I)V +4 -1: swap +10 -1: readOffset +2 -1: /* +112 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU; +12 -1: isAsciiDigit +2 -1: /- +2 -1: /. +2 -1: // +5 -1: zones +37 -1: (ILjava/lang/Object;)Ljava/util/List; +17 -1: SHUFFLE_THRESHOLD +32 -1: java/lang/CharacterDataUndefined +23 -1: sun.reflect.noInflation +11 -1: Can not set +36 -1: (Ljava/util/Properties$LineReader;)V +9 -1: debugName +78 -1: (Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode; +6 -1: (III)J +58 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/lang/String; +19 -1: BufferedWriter.java +148 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor<TT;>; +6 -1: printf +7 -1: signers +2 -1: 0. +4 -1: Date +15 -1: findReplacement +6 -1: (III)V +32 -1: java/nio/ReadOnlyBufferException +92 -1: ([Ljava/lang/String;)Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>; +6 -1: (III)Z +43 -1: (Ljava/lang/ClassValue;Ljava/lang/Object;)V +32 -1: java/nio/ByteBufferAsLongBufferB +16 -1: Constructor.java +10 -1: removeLast +9 -1: ([CII[B)I +40 -1: ()Ljava/util/List<Ljava/lang/Class<*>;>; +2 -1: 1. +7 -1: VARARGS +32 -1: java/nio/ByteBufferAsLongBufferL +18 -1: java/lang/Shutdown +40 -1: not supported, using ISO-8859-1 instead +40 -1: Ljava/util/Collections$EmptyEnumeration; +146 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/SortedMap<TK;TV;>; +20 -1: aliases_UTF_32LE_BOM +23 -1: getEnclosingConstructor +2 -1: 0X +11 -1: NO_TIMEZONE +37 -1: ()Lsun/misc/JavaNioAccess$BufferPool; +18 -1: printXUsageMessage +9 -1: isPrivate +63 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/invoke/MethodHandle;)V +17 -1: maxSkipBufferSize +76 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetEncoder;)V +40 -1: (Ljava/lang/String;II)Ljava/lang/String; +17 -1: selectAlternative +53 -1: [Ljava/util/concurrent/ConcurrentHashMap$CounterCell; +87 -1: <S:Ljava/lang/Object;>(Ljava/util/function/Supplier<+TS;>;)Ljava/lang/ThreadLocal<TS;>; +40 -1: Ljava/util/concurrent/ConcurrentHashMap; +41 -1: (Ljava/lang/Character;)Ljava/lang/String; +19 -1: getMemberRefInfoAt0 +14 -1: reduceToDouble +18 -1: SUPPRESSED_CAPTION +6 -1: CANADA +64 -1: (IZ[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/String; +12 -1: UnicodeBlock +2 -1: 0x +44 -1: (Ljava/lang/CharSequence;II)Ljava/io/Writer; +23 -1: getPermissionCollection +19 -1: threadLocalHashCode +9 -1: createMap +25 -1: checkForSpecialAttributes +12 -1: Mark invalid +36 -1: java/lang/CharSequence$1CharIterator +11 -1: local time +16 -1: Enumeration.java +27 -1: (Ljava/util/zip/ZipEntry;)V +11 -1: MATH_SYMBOL +8 -1: filename +24 -1: (Ljava/util/List<*>;II)V +9 -1: bindCache +18 -1: java/io/FileFilter +12 -1: checkInvoker +18 -1: OSEnvironment.java +8 -1: EmptyMap +11 -1: getIterator +35 -1: java/util/function/IntUnaryOperator +35 -1: java/util/WeakHashMap$EntryIterator +90 -1: <E:Ljava/lang/Object;>(Ljava/util/Queue<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Queue<TE;>; +8 -1: batchFor +16 -1: isValidCodePoint +27 -1: ([Lsun/util/calendar/Era;)V +16 -1: ThreadGroup.java +33 2: sun/net/www/protocol/file/Handler +7 -1: isField +22 -1: sun/misc/OSEnvironment +38 -1: Ljava/lang/Class<Ljava/lang/Boolean;>; +12 -1: ADDRESS_SIZE +8 -1: forDigit +49 -1: (Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry; +13 -1: getCodeSource +41 -1: ([Ljava/lang/Class<*>;)Ljava/lang/String; +39 -1: (Ljava/util/function/Predicate<-TE;>;)Z +13 -1: asFloatBuffer +34 -1: ()Lsun/reflect/generics/tree/Tree; +3 -1: ftp +18 -1: maybeReBoxElements +82 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)[[Ljava/lang/annotation/Annotation; +48 -1: ()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>; +27 -1: (Ljava/util/NavigableMap;)V +18 -1: parameterSlotCount +20 -1: NF_getCallSiteTarget +16 -1: aliases_US_ASCII +13 -1: NF_staticBase +31 -1: sun/reflect/ConstructorAccessor +26 -1: guessContentTypeFromStream +10 -1: Deprecated +35 -1: System initialization has completed +11 -1: initialized +7 -1: compare +15 -1: maxDirectMemory +19 -1: setLastModifiedTime +7 -1: (J[BZ)J +12 -1: readEpochSec +66 -1: (Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/BaseLocale; +69 -1: ()Lsun/misc/JavaSecurityProtectionDomainAccess$ProtectionDomainCache; +9 -1: Constants +11 -1: valueOffset +62 -1: (Ljava/util/Hashtable<Ljava/lang/String;Ljava/lang/Object;>;)V +33 -1: java/lang/CharacterDataPrivateUse +21 -1: Exception in thread " +40 -1: ()Ljava/util/Set<Ljava/lang/Character;>; +12 -1: Asia/Yerevan +40 -1: (Ljava/lang/Throwable;)Ljava/lang/Error; +25 -1: (IS)Ljava/nio/ByteBuffer; +7 -1: (I[CI)I +45 -1: java/nio/charset/UnmappableCharacterException +23 -1: java/util/WeakHashMap$1 +21 -1: setFXLaunchParameters +1250 -1: ADANDAEAREAFAFGAGATGAIAIAALALBAMARMANANTAOAGOAQATAARARGASASMATAUTAUAUSAWABWAXALAAZAZEBABIHBBBRBBDBGDBEBELBFBFABGBGRBHBHRBIBDIBJBENBLBLMBMBMUBNBRNBOBOLBQBESBRBRABSBHSBTBTNBVBVTBWBWABYBLRBZBLZCACANCCCCKCDCODCFCAFCGCOGCHCHECICIVCKCOKCLCHLCMCMRCNCHNCOCOLCRCRICUCUBCVCPVCWCUWCXCXRCYCYPCZCZEDEDEUDJDJIDKDNKDMDMADODOMDZDZAECECUEEESTEGEGYEHESHERERIESESPETETHFIFINFJFJIFKFLKFMFSMFOFROFRFRAGAGABGBGBRGDGRDGEGEOGFGUFGGGGYGHGHAGIGIBGLGRLGMGMBGNGINGPGLPGQGNQGRGRCGSSGSGTGTMGUGUMGWGNBGYGUYHKHKGHMHMDHNHNDHRHRVHTHTIHUHUNIDIDNIEIRLILISRIMIMNININDIOIOTIQIRQIRIRNISISLITITAJEJEYJMJAMJOJORJPJPNKEKENKGKGZKHKHMKIKIRKMCOMKNKNAKPPRKKRKORKWKWTKYCYMKZKAZLALAOLBLBNLCLCALILIELKLKALRLBRLSLSOLTLTULULUXLVLVALYLBYMAMARMCMCOMDMDAMEMNEMFMAFMGMDGMHMHLMKMKDMLMLIMMMMRMNMNGMOMACMPMNPMQMTQMRMRTMSMSRMTMLTMUMUSMVMDVMWMWIMXMEXMYMYSMZMOZNANAMNCNCLNENERNFNFKNGNGANINICNLNLDNONORNPNPLNRNRUNUNIUNZNZLOMOMNPAPANPEPERPFPYFPGPNGPHPHLPKPAKPLPOLPMSPMPNPCNPRPRIPSPSEPTPRTPWPLWPYPRYQAQATREREUROROURSSRBRURUSRWRWASASAUSBSLBSCSYCSDSDNSESWESGSGPSHSHNSISVNSJSJMSKSVKSLSLESMSMRSNSENSOSOMSRSURSSSSDSTSTPSVSLVSXSXMSYSYRSZSWZTCTCATDTCDTFATFTGTGOTHTHATJTJKTKTKLTLTLSTMTKMTNTUNTOTONTRTURTTTTOTVTUVTWTWNTZTZAUAUKRUGUGAUMUMIUSUSAUYURYUZUZBVAVATVCVCTVEVENVGVGBVIVIRVNVNMVUVUTWFWLFWSWSMYEYEMYTMYTZAZAFZMZMBZWZWE +60 -1: Ljava/util/Set<Ljava/lang/Class<+Ljava/lang/ClassLoader;>;>; +24 -1: ()Ljava/security/Policy; +7 -1: initted +44 -1: java/util/Collections$UnmodifiableCollection +12 -1: Pacific/Apia +23 -1: checkProxyPackageAccess +7 -1: (I[CI)V +64 -1: ([Ljava/lang/Object;IILjava/lang/Object;Ljava/util/Comparator;)I +16 -1: getJavaNioAccess +7 -1: reverse +7 -1: nocerts +16 -1: activeGroupCount +34 -1: java/util/jar/JarFile$JarFileEntry +7 -1: loaders +9 -1: toRadians +24 -1: java/util/HashMap$KeySet +37 -1: (Ljava/lang/Class;)Ljava/lang/Object; +6 -1: getRef +6 -1: H_DASH +17 -1: LinkageError.java +66 -1: (Ljava/lang/invoke/MethodTypeForm;)Ljava/lang/invoke/MethodHandle; +41 -1: (Ljava/nio/ByteBuffer;)Ljava/util/BitSet; +10 -1: addMinutes +58 -1: <T:Ljava/lang/Object;>([TT;II)Ljava/util/Spliterator<TT;>; +9 -1: parseJars +13 -1: getUnsignedCS +28 -1: (Ljava/util/AbstractList;I)V +22 -1: threadLocalRandomProbe +19 -1: newDirectByteBuffer +27 -1: Filter already registered: +8 -1: unescape +31 -1: sun/misc/URLClassPath$JarLoader +6 -1: TAIWAN +53 -1: <T:Ljava/lang/Object;>()Ljava/util/ListIterator<TT;>; +17 -1: REVERSE_THRESHOLD +31 -1: Java(TM) SE Runtime Environment +7 -1: SECONDS +70 -1: (Ljava/util/function/ToLongFunction<-TT;>;)Ljava/util/Comparator<TT;>; +7 -1: BLOCKED +6 -1: Caches +63 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)V +2 -1: : +210 -1: (Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;I)V +153 -1: (JLjava/util/function/BiFunction<Ljava/util/Map$Entry<TK;TV;>;Ljava/util/Map$Entry<TK;TV;>;+Ljava/util/Map$Entry<TK;TV;>;>;)Ljava/util/Map$Entry<TK;TV;>; +22 -1: ()Ljava/lang/Class<*>; +28 -1: Ljava/lang/OutOfMemoryError; +19 -1: writeFileDescriptor +39 -1: Ljava/util/LinkedHashMap$Entry<TK;TV;>; +26 -1: (ILjava/util/Collection;)Z +18 -1: getEncodedInternal +16 -1: ForEachValueTask +23 -1: (Ljava/util/List<*>;I)V +19 -1: SharedArchiveLoader +20 -1: probeBackupLocations +24 -1: java/lang/StringCoding$1 +28 -1: lookupContentHandlerClassFor +36 -1: ()Lsun/misc/Launcher$ExtClassLoader; +27 -1: reflectionFactoryAccessPerm +14 -1: ACCESSOR_FORMS +35 -1: ([JII)Ljava/util/stream/LongStream; +34 -1: ISO-8859-1 charset not available: +8 -1: cscesu-8 +2 -1: ;/ +17 -1: typeToPackageName +34 -1: (Ljava/net/URL;)Ljava/lang/String; +5 -1: (IZ)V +25 -1: Prohibited package name: +51 -1: (Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V +108 -1: (JLjava/util/function/ToIntFunction<Ljava/util/Map$Entry<TK;TV;>;>;ILjava/util/function/IntBinaryOperator;)I +12 -1: soleInstance +27 -1: (Ljava/io/BufferedReader;)V +71 -1: (Ljava/nio/charset/CodingErrorAction;)Ljava/nio/charset/CharsetDecoder; +17 -1: Ljava/lang/Class; +38 -1: (Ljava/lang/String;)Ljava/lang/Double; +10 -1: viewAsType +22 -1: (Ljava/io/DataInput;)I +22 -1: (Ljava/io/DataInput;)J +105 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/WrongMethodTypeException; +16 -1: setJavaNetAccess +73 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/util/HashMap$Node<TK;TV;>; +22 -1: (Ljava/io/DataInput;)V +14 -1: getHostAddress +37 -1: sun/reflect/annotation/TypeAnnotation +14 -1: ENCLOSING_MARK +5 -1: FALSE +14 -1: preDefineClass +9 -1: newKeySet +18 -1: getWaitQueueLength +32 -1: ()Lsun/misc/URLClassPath$Loader; +54 -1: (Ljava/nio/CharBuffer;I)Ljava/nio/charset/CoderResult; +3 -1: SEP +45 -1: (ITK;TV;Ljava/util/Hashtable$Entry<TK;TV;>;)V +17 -1: getDeclaredFields +7 -1: getDate +10 -1: getClasses +240 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V +12 -1: WeakClassKey +14 -1: LF_GEN_INVOKER +25 -1: Ljava/lang/StringBuilder; +2 -1: > +9 -1: getJarMap +4 -1: asin +37 -1: (Ljava/net/URLStreamHandlerFactory;)V +30 -1: not a constructor type or name +4 -1: main +22 -1: java/io/FilenameFilter +22 -1: sun.java.launcher.diag +30 -1: ()Ljava/util/Spliterator<TE;>; +57 -1: <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V +86 -1: <T:Ljava/lang/Object;>(Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<TT;>;>;)TT; +18 -1: canBeCalledVirtual +9 -1: Shift_JIS +24 -1: ()Ljava/util/ArrayDeque; +32 -1: (Ljava/lang/Class$MethodArray;)V +22 -1: java/lang/StringCoding +33 -1: sun/util/locale/LocaleObjectCache +20 -1: Sorry, deque too big +38 -1: java/lang/Throwable$WrappedPrintWriter +25 -1: (Ljava/io/InputStream;J)J +44 -1: (Ljava/security/Permission;)Ljava/util/List; +5 -1: thunk +5 -1: props +15 -1: getLastModified +120 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/util/HashMap<Ljava/lang/String;Ljava/util/LinkedList<Ljava/lang/String;>;>;)V +146 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/invoke/MethodHandleImpl$1;)V +27 -1: parseExtensionsDependencies +10 -1: getRawType +39 -1: (Ljava/nio/charset/CodingErrorAction;)V +22 -1: ObjectStreamField.java +6 -1: handle +11 -1: hasPrevious +18 -1: instanceof Float: +52 -1: (ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V +4 -1: make +13 -1: isIdeographic +26 -1: java/util/HashMap$EntrySet +51 -1: (Ljava/util/Hashtable;)[Ljava/util/Hashtable$Entry; +91 -1: (Ljava/lang/CharSequence;Ljava/lang/Iterable<+Ljava/lang/CharSequence;>;)Ljava/lang/String; +13 -1: makeAllocator +10 -1: , headless +20 -1: expungeStaleElements +58 -1: sun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget +41 -1: ([Ljava/lang/Object;Ljava/lang/Class$1;)V +50 -1: <T:Ljava/lang/Object;>(I)Ljava/util/Iterator<TT;>; +7 -1: TreeBin +21 -1: Ljava/io/IOException; +56 -1: (I[Ljava/lang/Class;)[Ljava/lang/invoke/LambdaForm$Name; +6 -1: LOCFLG +6 -1: DIRECT +3 -1: SIG +37 -1: java/security/NoSuchProviderException +39 -1: " with illegal data type conversion to +16 -1: getCodeSourceURL +51 -1: java/util/concurrent/ConcurrentHashMap$EntrySetView +47 -1: (Ljava/util/ArrayList;Ljava/util/ArrayList$1;)V +6 -1: delete +38 -1: sun/reflect/UnsafeFieldAccessorFactory +11 -1: isDestroyed +140 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)Z +9 -1: unaligned +36 -1: ()Lsun/misc/Launcher$AppClassLoader; +57 -1: (Ljava/lang/String;Ljava/util/Locale;)[Ljava/lang/String; +37 -1: sun/reflect/annotation/AnnotationType +49 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/List<TT;>; +24 -1: (S)Ljava/nio/ByteBuffer; +6 -1: ([DD)I +9 -1: setTarget +29 -1: (IF)Ljava/lang/StringBuilder; +12 -1: forBasicType +10 -1: (IIII[BI)V +8 -1: ([DIID)I +9 -1: BASE_YEAR +19 -1: ()Ljava/lang/Error; +42 -1: (Ljava/util/Map<TE;Ljava/lang/Boolean;>;)V +6 -1: ([DD)V +14 -1: Illegal size: +8 -1: ([DIID)V +7 -1: (II[I)I +17 -1: java_profile_name +28 -1: java/util/AbstractCollection +43 -1: (Ljava/net/URL;)[Ljava/security/CodeSource; +62 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/net/URLStreamHandler; +33 -1: [Ljava/util/HashMap$Node<TK;TV;>; +15 -1: (Native Method) +11 -1: fileNameMap +26 -1: ()Ljava/util/ListIterator; +25 -1: java/util/LinkedList$Node +18 -1: SELECT_ALTERNATIVE +35 -1: (Ljava/lang/Object;)Ljava/util/Set; +19 -1: java/io/IOException +16 -1: : already loaded +9 -1: image/gif +6 -1: (TE;)I +25 -1: (Ljava/util/Properties;)V +40 -1: (Ljava/lang/String;)Ljava/nio/file/Path; +19 -1: checkedNavigableMap +9 -1: checkInt( +17 -1: getContentTypeFor +26 -1: ()Ljava/io/FileDescriptor; +69 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V +6 -1: (TE;)V +25 -1: WARNING: Default charset +14 -1: ZipFile closed +2 -1: CA +6 -1: (TE;)Z +64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToLongTask +15 -1: FileSystem.java +75 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +10 -1: FileLoader +44 -1: (Lsun/util/PreHashedMap;)[Ljava/lang/Object; +19 -1: availableProcessors +2 -1: CN +36 -1: ([JII)Ljava/util/Spliterator$OfLong; +11 -1: access$1100 +18 -1: getFieldAtIfLoaded +30 -1: PrivilegedActionException.java +6 -1: EUC-JP +20 -1: (F)Ljava/lang/Float; +22 -1: unable to instantiate +31 -1: java/lang/reflect/ReflectAccess +23 -1: (Ljava/lang/Object;JC)V +3 -1: get +59 -1: <T:Ljava/lang/Object;>([TT;IILjava/util/Comparator<-TT;>;)V +2 -1: DE +13 -1: GMT_ID_LENGTH +7 -1: execute +12 -1: MethodHandle +18 -1: AllPermission.java +54 -1: (Ljava/util/Locale;)Lsun/util/locale/LocaleExtensions; +23 -1: MapReduceKeysToLongTask +12 -1: varargsArray +23 -1: java/util/jar/JarFile$1 +23 -1: java/util/jar/JarFile$2 +23 -1: java/util/jar/JarFile$3 +12 -1: getAndUpdate +13 -1: reserveMemory +17 -1: expungeStaleEntry +6 -1: EUC-KR +120 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/ref/WeakReference<Ljava/lang/Object;>;Ljava/util/Map$Entry<TK;TV;>; +69 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher; +26 -1: ()Ljava/util/NavigableMap; +7 -1: L_PCHAR +23 -1: (Ljava/lang/String$1;)V +4 -1: KEYS +37 -1: (Ljava/util/List;Ljava/lang/Object;)I +31 -1: Ljava/lang/ArithmeticException; +15 -1: [Ljava/net/URL; +37 -1: (Ljava/util/List;Ljava/lang/Object;)V +18 -1: MAX_HIGH_SURROGATE +6 -1: (JCZ)V +4 -1: mark +17 -1: setMethodAccessor +21 -1: java/io/ExpiringCache +21 -1: PrivilegedAction.java +21 -1: MappedByteBuffer.java +2 -1: FR +10 -1: copyMemory +8 -1: L_SERVER +13 -1: assertionLock +12 -1: searchValues +46 -1: (Ljava/util/Collection<*>;Ljava/lang/Object;)I +21 -1: ProtectionDomainCache +32 -1: USE_PREDEFINED_INTERPRET_METHODS +2 -1: GB +16 -1: getFinalRefCount +23 -1: Ljava/lang/ClassLoader; +4 -1: mask +94 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToDoubleFunction<-TT;>;)Ljava/util/Comparator<TT;>; +4 -1: bind +41 -1: (Ljava/lang/Class<*>;I)Ljava/lang/Object; +9 -1: COUNT_GWT +16 -1: DASH_PUNCTUATION +24 -1: UNICODE_LOCALE_EXTENSION +15 -1: checkInvariants +10 -1: stringSize +12 -1: deepHashCode +30 -1: java/security/cert/Certificate +19 -1: America/Los_Angeles +19 -1: unmappableForLength +6 -1: UTF-16 +10 -1: methodType +21 -1: sun/misc/URLClassPath +19 -1: META-INF/INDEX.LIST +10 -1: jniVersion +6 -1: IBM437 +29 -1: sun/reflect/FieldAccessorImpl +21 -1: ()Ljava/lang/Package; +32 -1: java/security/SecurityPermission +57 -1: (Lsun/util/calendar/Era;)Lsun/util/calendar/CalendarDate; +34 -1: [Ljava/util/concurrent/locks/Lock; +11 -1: replacement +20 -1: ()Ljava/lang/String; +6 -1: resize +12 -1: UTF_32BE_BOM +26 -1: (Ljava/util/jar/JarFile;)V +24 -1: DEFAULT_INITIAL_CAPACITY +87 -1: (ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/concurrent/ConcurrentHashMap$TreeNode; +74 -1: (Ljava/util/jar/JarFile;)Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>; +18 -1: parameterSlotDepth +26 -1: (Ljava/util/jar/JarFile;)Z +18 -1: makePlatformString +24 -1: doPrivilegedWithCombiner +48 -1: (Ljava/lang/String;)Lsun/util/calendar/ZoneInfo; +27 -1: ()Ljava/lang/ref/Reference; +40 -1: java/util/ArrayList$ArrayListSpliterator +67 -1: ([Ljava/lang/Object;IILjava/util/Comparator;[Ljava/lang/Object;II)V +2 -1: ID +19 -1: stringPropertyNames +28 -1: (Ljava/util/Collections$1;)V +12 -1: STATE_YELLOW +12 -1: isNormalized +10 -1: fromIndex( +16 -1: getFloatVolatile +37 -1: Lsun/util/calendar/BaseCalendar$Date; +10 -1: properties +17 -1: peakFinalRefCount +102 -1: (Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>; +68 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle; +2 -1: IT +9 -1: ([BI[BI)V +51 -1: scl permissions SecureClassLoader assigns +36 -1: (Ljava/util/Set;Ljava/lang/Object;)V +11 -1: ([SII[SII)V +21 -1: sun.io.useCanonCaches +18 -1: Illegal capacity: +22 -1: (Ljava/lang/Integer;)I +83 -1: ([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set<[Ljava/lang/Object;>;)V +65 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;II)V +17 -1: java/util/Objects +48 -1: (ILjava/util/List;)Ljava/lang/invoke/LambdaForm; +31 -1: java/util/Properties$XmlSupport +10 -1: L_LOWALPHA +13 -1: long overflow +25 -1: NullPointerException.java +32 -1: (I)Ljava/lang/StackTraceElement; +34 -1: ()[Ljava/lang/reflect/Constructor; +2 -1: JP +3 -1: SST +12 -1: ShortCountry +48 -1: (Ljava/util/stream/Collector;)Ljava/lang/Object; +29 -1: Lsun/reflect/CallerSensitive; +10 -1: addElement +12 -1: lastReturned +6 -1: putInt +34 -1: sun.misc.JarIndex.metaInfFilenames +13 -1: getBaseLocale +20 -1: StringTokenizer.java +8 -1: entrySet +11 -1: getTypeName +17 -1: America/Sao_Paulo +5 -1: \t... +35 -1: (Lsun/util/calendar/CalendarDate;)I +28 -1: java/lang/StackOverflowError +35 -1: (Lsun/util/calendar/CalendarDate;)J +10 -1: logicalAnd +18 -1: csISOLatinCyrillic +43 -1: (Ljava/lang/String;II)Ljava/nio/CharBuffer; +73 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;Z)Ljava/lang/invoke/MethodType; +14 -1: aliases_MS1250 +14 -1: aliases_MS1251 +17 -1: getImplMethodKind +17 -1: getLastAccessTime +14 -1: aliases_MS1252 +14 -1: aliases_MS1253 +35 -1: (Lsun/util/calendar/CalendarDate;)V +2 -1: KR +14 -1: aliases_MS1254 +14 -1: getGenericInfo +8 -1: utf_32be +14 -1: aliases_MS1257 +35 -1: (Lsun/util/calendar/CalendarDate;)Z +13 -1: StringEncoder +7 -1: LDT2037 +7 -1: generic +2 -1: L9 +45 -1: ([DLjava/util/function/IntToDoubleFunction;)V +17 -1: isOtherAlphabetic +9 -1: implWrite +24 -1: PC-Multilingual-850+euro +12 -1: valueMatches +78 -1: (Ljava/lang/String;Lsun/util/locale/ParseStatus;)Lsun/util/locale/LanguageTag; +3 -1: gmt +19 -1: (Ljava/io/Reader;)V +25 -1: (JJ)Ljava/nio/ByteBuffer; +7 -1: val$url +26 -1: (Ljava/nio/ByteBuffer;IJ)V +10 -1: isImplicit +19 -1: getDeclaredClasses0 +4 -1: (I)B +4 -1: (I)C +4 -1: (I)D +9 -1: byteValue +4 -1: (I)F +5 -1: ([J)I +6 -1: isLive +5 -1: sleep +4 -1: (I)I +4 -1: (I)J +6 -1: outBuf +77 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Set<TE;>; +4 -1: (I)S +5 -1: ([J)V +4 -1: (I)V +30 -1: (I[C)Ljava/lang/StringBuilder; +14 -1: intBitsToFloat +4 -1: (I)Z +15 -1: MethodType.java +14 -1: resolveSibling +9 -1: Enum.java +111 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V +14 -1: IS_CONSTRUCTOR +4 -1: bits +34 -1: java/security/PermissionCollection +9 -1: autoFlush +21 -1: java/util/Collections +12 -1: bindReceiver +20 -1: DMH.invokeStaticInit +11 -1: charsetName +14 -1: x-utf-32be-bom +5 -1: cause +7 -1: handle0 +43 -1: ([I[C[Ljava/lang/invoke/LambdaForm$Name;I)Z +30 -1: getDefaultAllowUserInteraction +18 -1: ConcurrentMap.java +35 -1: (Ljava/lang/String;Z)Ljava/net/URL; +33 -1: Ljava/nio/charset/CharsetDecoder; +36 -1: java/lang/invoke/MethodHandleStatics +34 -1: java/util/concurrent/ConcurrentMap +16 -1: collectArguments +22 -1: packageAssertionStatus +79 -1: (JLjava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)J +39 -1: Cannot reflectively create enum objects +62 -1: attempt to add a Permission to a readonly PermissionCollection +22 -1: FieldAccessorImpl.java +9 -1: ByteCache +4 -1: TRUE +85 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Character;>; +17 -1: java.library.path +7 -1: encoder +21 -1: default locale = +11 -1: secondOfDay +37 -1: (Lsun/util/calendar/ZoneInfoFile$1;)V +35 -1: sun/reflect/UnsafeFieldAccessorImpl +24 -1: (Ljava/lang/Object;JJJ)V +16 -1: countStackFrames +24 -1: (Ljava/lang/Object;JJJ)Z +17 -1: nonfairTryAcquire +20 -1: ArrayListSpliterator +5 -1: /DMH= +68 -1: (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; +2 -1: PI +8 -1: cspcp852 +112 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;)Ljava/util/Locale; +8 -1: cspcp855 +58 -1: (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z +33 -1: sun/misc/PerfCounter$CoreCounters +6 -1: CESU_8 +7 -1: vmslots +4 -1: Init +7 -1: handler +11 -1: getProperty +10 -1: isVolatile +8 -1: ([J[IJ)I +25 -1: ()Ljava/lang/ClassLoader; +8 -1: asChange +81 -1: (Ljava/net/URLClassLoader;Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class; +12 -1: naturalOrder +8 -1: getState +40 -1: (Ljava/lang/Object;I)[Ljava/lang/String; +36 -1: java/security/AccessControlException +10 -1: linkBefore +48 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;Z)V +26 -1: (Ljava/util/WeakHashMap;)V +23 -1: bad method type alias: +7 -1: putChar +18 -1: basicTypeSignature +33 -1: sun/misc/InvalidJarIndexException +13 -1: getPrivateuse +14 -1: isConstantZero +24 -1: java/io/FilePermission$1 +9 -1: Long.java +19 -1: getLocaleExtensions +10 -1: discovered +17 -1: Invalid file path +12 -1: MAX_MH_ARITY +20 -1: observesDaylightTime +31 -1: Ljava/lang/invoke/MethodHandle; +6 -1: , end +24 -1: java/io/FileDescriptor$1 +12 -1: loadLibrary. +11 -1: Method.java +12 -1: loadLibrary0 +23 -1: java/util/stream/Stream +37 -1: sun.lang.ClassLoader.allowArraySyntax +8 -1: appClass +15 -1: FileReader.java +5 -1: (IF)I +29 -1: [Ljava/lang/ClassValue$Entry; +12 -1: (principals +30 -1: jar jar verification +22 -1: ARRAY_CHAR_BASE_OFFSET +18 -1: newDirectoryStream +4 -1: atan +5 -1: (IF)V +24 -1: (Ljava/lang/Character;)I +2 -1: TH +10 -1: startsWith +9 -1: baseCount +13 -1: canonicalize0 +47 -1: (Ljava/lang/ClassLoader;[Ljava/lang/Class<*>;)V +22 -1: Ljava/io/OutputStream; +2 -1: TW +10 -1: H_RESERVED +19 -1: URLClassLoader.java +16 -1: isAccessibleFrom +59 -1: Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/Object;>; +33 -1: java/lang/invoke/ConstantCallSite +14 -1: Ljava/net/URL; +12 -1: deleteOnExit +20 -1: MAX_SKIP_BUFFER_SIZE +30 -1: [Ljava/lang/ref/WeakReference; +15 -1: contentPathProp +9 -1: initCause +53 -1: (Ljava/util/Queue;Ljava/lang/Class;)Ljava/util/Queue; +20 -1: java/util/TimeZone$1 +2 -1: UK +86 -1: (BLjava/lang/Class;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle; +51 -1: (II[Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +18 -1: Lsun/misc/Cleaner; +31 -1: RuntimeInvisibleTypeAnnotations +2 -1: US +7 -1: makeInt +40 -1: sun/reflect/annotation/AnnotationSupport +28 -1: sun/reflect/misc/ReflectUtil +8 -1: utf_32le +40 -1: (Ljava/lang/Object;JLjava/lang/Object;)V +24 -1: java/nio/file/WatchEvent +66 -1: (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodType; +91 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class; +114 -1: (Ljava/lang/ThreadLocal;ILjava/lang/ThreadLocal$ThreadLocalMap$Entry;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +20 -1: internalWriteEntries +79 -1: <T:Ljava/lang/Object;>(TT;Ljava/util/function/Supplier<Ljava/lang/String;>;)TT; +14 -1: bitIndex < 0: +88 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object; +6 -1: , len +28 -1: java/io/BufferedOutputStream +11 -1: System.java +11 -1: csISOLatin1 +11 -1: csISOLatin2 +28 -1: Ljava/io/OutputStreamWriter; +27 -1: IllegalAccessException.java +11 -1: csISOLatin4 +14 -1: isDaylightTime +11 -1: csISOLatin5 +21 -1: getYearLengthInMonths +13 -1: canonicalizes +93 -1: "'s signer information does not match signer information of other classes in the same package +32 -1: ()Lsun/management/GcInfoBuilder; +37 -1: (IILjava/nio/charset/CoderResult$1;)V +10 -1: stateNames +46 -1: (Ljava/lang/String;)Ljava/nio/charset/Charset; +21 -1: acquireMethodAccessor +2 -1: X- +32 -1: java/security/ProtectionDomain$1 +5 -1: atan2 +32 -1: java/security/ProtectionDomain$2 +32 -1: java/security/ProtectionDomain$3 +41 -1: malformed input: partial character at end +18 -1: dropParameterTypes +44 -1: (Ljava/lang/String;)Ljava/lang/StringBuffer; +18 -1: CalendarUtils.java +5 -1: month +84 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/ClassLoader;>; +33 -1: sun/misc/JavaNioAccess$BufferPool +18 -1: SearchMappingsTask +38 -1: DelegatingConstructorAccessorImpl.java +80 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Ljava/lang/invoke/MemberName; +8 -1: instance +54 -1: Parameter annotations don't match number of parameters +14 -1: createConstant +35 -1: java/lang/invoke/MemberName$Factory +4 -1: scrt +34 -1: Ljava/lang/InstantiationException; +20 -1: allowUserInteraction +15 -1: java/util/Deque +16 -1: forEachRemaining +35 -1: (J)Lsun/util/calendar/CalendarDate; +13 -1: no such field +25 -1: java/nio/file/FileSystems +16 -1: expectedModCount +44 -1: (Ljava/io/File;ILjava/nio/charset/Charset;)V +12 -1: createString +15 -1: useDaylightTime +31 -1: java/util/Properties$LineReader +111 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Class<*>;I[Ljava/lang/invoke/MemberName;)I +16 -1: java/util/Locale +26 -1: URLClassPath.getResource(" +58 -1: (Ljava/util/SortedMap;Ljava/lang/Class;Ljava/lang/Class;)V +10 -1: appendTail +7 -1: cleaner +78 -1: (Lsun/nio/cs/FastCharsetProvider;Ljava/lang/String;)Ljava/nio/charset/Charset; +18 -1: convertOldISOCodes +4 -1: year +38 -1: java/lang/ReflectiveOperationException +28 -1: (Ljava/lang/StringBuffer;B)V +27 -1: (D)Ljava/lang/StringBuffer; +17 -1: emptyNavigableSet +7 -1: indices +10 -1: is sealed +21 -1: SPECIFICATION_VERSION +3 -1: TE; +8 -1: addMonth +24 -1: java/util/HashMap$Values +17 -1: SEARCH_ALL_SUPERS +18 -1: uncaught exception +14 -1: declaredFields +2 -1: [B +2 -1: [C +8 -1: ABSTRACT +2 -1: [D +2 -1: [F +2 -1: [I +2 -1: [J +5 -1: false +36 -1: (Ljava/net/URL;Ljava/lang/String;J)V +12 -1: equalContext +11 -1: VOID_RESULT +2 -1: [S +24 -1: (JLjava/lang/Object;JJ)V +36 -1: Ljava/security/PermissionCollection; +2 -1: [Z +17 -1: floatToRawIntBits +2 -1: [] +21 -1: ()[Ljava/lang/Thread; +20 -1: [Ljava/lang/Integer; +42 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Z)V +19 -1: checkPrintJobAccess +40 -1: (Ljava/lang/Object;)Ljava/lang/Class<*>; +31 -1: (Lsun/reflect/FieldAccessor;Z)V +10 -1: reallyPoll +52 -1: (Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference; +100 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;Ljava/util/function/Consumer<-TU;>;)V +19 -1: CheckedNavigableMap +48 -1: (Ljava/lang/String;)Ljava/lang/RuntimeException; +35 -1: java/util/Hashtable$ValueCollection +89 -1: (JLjava/util/function/ToDoubleFunction<-TK;>;DLjava/util/function/DoubleBinaryOperator;)D +10 -1: Stack.java +57 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;I)V +12 -1: getTimeOfDay +12 -1: reduceValues +22 -1: warnUnsupportedCharset +34 -1: (Ljava/lang/ref/Reference<+TS;>;)Z +31 -1: EEE, dd MMM yyyy HH:mm:ss 'GMT' +19 -1: setCachedLambdaForm +21 -1: (I)Ljava/lang/Object; +85 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;Ljava/util/Locale$1;)V +36 -1: java/security/AccessControlContext$1 +27 -1: Lsun/net/www/MessageHeader; +23 -1: ()[Ljava/lang/Class<*>; +14 -1: refKindIsValid +41 -1: sun/reflect/NativeConstructorAccessorImpl +6 -1: binary +23 -1: ()Ljava/nio/CharBuffer; +8 -1: getSpace +45 -1: bootstrap method failed to produce a CallSite +15 -1: getISO3Language +17 -1: TRANSITION_NSHIFT +163 -1: ([Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;)Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>; +7 -1: os.name +38 -1: java/util/function/IntToDoubleFunction +8 -1: checkInt +21 -1: java/lang/VerifyError +42 -1: sunpkcs11 SunPKCS11 provider debugging +19 -1: URI is not absolute +23 -1: java/io/DataInputStream +53 -1: (Ljava/lang/Object;)Ljava/nio/charset/CharsetEncoder; +2 -1: _# +41 -1: (Ljava/io/FilenameFilter;)[Ljava/io/File; +26 -1: Ljava/util/jar/Attributes; +7 -1: collect +17 -1: Lsun/misc/Unsafe; +97 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; +78 -1: <T:Ljava/lang/Object;>(Ljava/util/Enumeration<TT;>;)Ljava/util/ArrayList<TT;>; +28 -1: (II)Ljava/lang/StringBuffer; +54 -1: (Ljava/lang/reflect/Constructor<*>;)Ljava/lang/String; +28 -1: ()Ljava/util/ResourceBundle; +48 -1: java/util/ArraysParallelSortHelpers$FJInt$Sorter +9 -1: no access +57 -1: <S:Ljava/lang/Object;>Ljava/lang/ref/ReferenceQueue<TS;>; +7 -1: getPerf +11 -1: getClassAt0 +11 -1: rtypeOffset +20 -1: thread can't be null +12 -1: addArguments +12 -1: utf_32le_bom +21 -1: allowThreadSuspension +25 -1: defaultExpectedLineLength +11 -1: removeFirst +13 -1: reduceEntries +20 -1: Read-ahead limit < 0 +57 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<-TT;>;[TT;)Z +39 -1: ()Ljava/lang/invoke/MemberName$Factory; +13 -1: ZipCoder.java +16 -1: java/lang/Thread +27 -1: java/lang/NoSuchMethodError +66 -1: (Ljava/util/jar/JarFile;Ljava/net/URL;)[Ljava/security/CodeSource; +22 -1: java/lang/StringBuffer +3 -1: TK; +34 -1: (I)Ljava/lang/invoke/MethodHandle; +30 -1: (Ljava/lang/reflect/Method;Z)V +13 -1: file.encoding +14 -1: removeTreeNode +27 -1: sun/misc/JavaSecurityAccess +58 -1: ([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object; +19 -1: equalLimitedContext +22 -1: appendVmSynonymMessage +10 -1: applyAsInt +23 -1: privateGetPublicMethods +11 -1: dumpThreads +25 -1: RuntimeVisibleAnnotations +37 -1: (IF)Ljava/lang/AbstractStringBuilder; +18 -1: getBooleanVolatile +27 -1: (Ljava/util/zip/ZipFile;J)V +25 -1: INITIAL_QUOTE_PUNCTUATION +51 -1: (Ljava/util/Map;)[Ljava/lang/annotation/Annotation; +54 -1: (ILjava/lang/Object;)Ljava/lang/AbstractStringBuilder; +20 -1: reflectionDataOffset +2 1: aa +51 -1: (Ljava/util/jar/Attributes$Name;)Ljava/lang/String; +13 -1: transferLinks +18 -1: java/util/Vector$1 +10 -1: ensureOpen +22 -1: getDeclaredConstructor +2 -1: am +19 -1: NF_allocateInstance +66 -1: (Ljava/util/Spliterator$OfDouble;Z)Ljava/util/stream/DoubleStream; +6 -1: ([SS)I +17 -1: newReflectionData +19 -1: subclassAuditsQueue +11 -1: access$1200 +16 -1: ValueSpliterator +18 -1: preparedLambdaForm +38 -1: (Ljava/net/URL;)Ljava/net/InetAddress; +14 -1: getMaxPriority +32 -1: ()[Ljava/lang/StackTraceElement; +67 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToDoubleTask +62 -1: (Ljava/util/Hashtable<Ljava/lang/String;Ljava/lang/String;>;)V +6 -1: EXTHDR +14 -1: java/util/Date +2 -1: az +6 -1: ([SS)V +15 -1: arrayBaseOffset +9 -1: isTrusted +23 -1: (Ljava/lang/Object;JD)V +2 -1: bb +10 -1: ([BII[BI)V +7 -1: toLower +14 -1: invoke_LLLLL_L +7 -1: jarfile +42 -1: (Ljava/lang/String;)Lsun/misc/PerfCounter; +28 -1: java/lang/ref/Reference$Lock +16 -1: java/util/BitSet +22 -1: jarfile parsing error! +18 -1: jdk_update_version +14 -1: invoke_LLLLL_V +20 -1: java/util/LinkedList +22 -1: erasedInvokerWithDrops +25 -1: timeout value is negative +21 -1: getCalendarProperties +25 -1: not a method descriptor: +2 -1: cb +31 -1: (Lsun/misc/JavaUtilJarAccess;)V +2 -1: cd +43 -1: Lsun/util/PreHashedMap<Ljava/lang/String;>; +12 -1: getEntrySize +2 -1: ce +24 -1: guessContentTypeFromName +2 -1: ch +14 -1: JulianCalendar +22 -1: [Ljava/lang/Cloneable; +122 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Deque<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable; +34 -1: Lsun/util/locale/BaseLocale$Cache; +14 -1: LinkedEntrySet +72 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/io/Serializable; +39 -1: ()Ljava/io/ObjectOutputStream$PutField; +2 -1: cs +8 -1: indexFor +25 -1: (Ljava/util/List<+TE;>;)V +7 -1: subpath +11 -1: invokeExact +14 -1: setFileNameMap +22 -1: LangReflectAccess.java +8 -1: referent +34 -1: java/util/MissingResourceException +77 -1: (Ljava/lang/String;Ljava/util/jar/Manifest;Ljava/net/URL;)Ljava/lang/Package; +11 -1: ([FII[FII)V +17 -1: getParameterCount +18 -1: isMemberAccessible +10 -1: getExtDirs +69 -1: <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;Ljava/util/List<+TT;>;)V +9 -1: getNextPC +13 -1: setProperties +2 -1: de +16 -1: generateCertPath +6 -1: decode +16 -1: getDefaultParent +50 -1: (Ljava/net/URL;[Ljava/security/cert/Certificate;)V +24 -1: Certificate factory for +35 -1: ()Ljava/lang/reflect/AnnotatedType; +2 -1: ee +12 -1: asLongBuffer +7 -1: PRIVATE +16 -1: aliases_UTF_32BE +2 -1: en +2 -1: eq +7 -1: indexOf +136 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<+Ljava/lang/Throwable;>;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +24 -1: (Ljava/lang/Class<*>;I)V +72 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/IllegalAccessException; +35 -1: java/util/jar/JavaUtilJarAccessImpl +24 -1: (Ljava/lang/Class<*>;I)Z +2 -1: ex +24 -1: getProtectionDomainCache +101 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;JLjava/security/AccessControlContext;)V +3 -1: hit +8 -1: UTF_16BE +2 -1: fd +16 -1: EmptyEnumeration +4 -1: attr +16 -1: fromIndex < -1: +7 -1: getIntB +22 -1: java/io/FilePermission +2 -1: fr +2 -1: fs +7 -1: lazySet +7 -1: getIntL +37 -1: configfile JAAS ConfigFile loading +24 -1: sun/nio/cs/StreamEncoder +40 -1: (Ljava/time/ZoneId;)Ljava/util/TimeZone; +132 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiConsumer;)V +2 -1: gc +18 -1: Ljava/lang/Thread; +10 -1: cacheArray +48 -1: (Ljava/util/jar/JarFile;)Ljava/util/jar/JarFile; +61 -1: (Ljava/util/NavigableMap;Ljava/lang/Class;Ljava/lang/Class;)V +8 -1: readByte +7 -1: ([S[S)Z +24 -1: findBootstrapClassOrNull +2 -1: hb +7 -1: REPLACE +45 -1: ()Ljava/util/Enumeration<Ljava/lang/String;>; +10 -1: isOverflow +2 -1: he +13 -1: getCachedJan1 +12 -1: getClassPath +8 -1: leapYear +31 -1: java/lang/invoke/MethodTypeForm +10 -1: ANNOTATION +20 -1: getGregorianCalendar +11 -1: ISO_8859_13 +11 -1: ISO_8859_15 +6 -1: invoke +2 -1: ht +7 -1: ListItr +15 -1: synchronizedMap +7 -1: cleanup +94 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)Lsun/reflect/annotation/AnnotationType; +3 -1: TT; +69 -1: (JLsun/util/calendar/CalendarDate;)Lsun/util/calendar/Gregorian$Date; +81 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/HashMap$TreeNode<TK;TV;>;)Z +20 -1: Min. Heap Size: +33 -1: (IZ)Ljava/lang/invoke/MethodType; +2 -1: id +7 -1: ([DI)[D +37 -1: (Ljava/lang/reflect/Constructor<*>;)I +42 -1: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V +13 -1: addSuppressed +28 -1: internalMemberNameEnsureInit +2 -1: in +17 -1: getCompressedSize +34 -1: sun/misc/Launcher$AppClassLoader$1 +88 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class<*>;)Ljava/security/AccessControlContext; +37 -1: (Ljava/lang/reflect/Constructor<*>;)V +2 -1: is +2 -1: it +6 -1: ENDOFF +4 -1: void +2 -1: iw +14 -1: emptySortedSet +2 -1: ix +17 -1: unicode-1-1-utf-8 +64 -1: (Ljava/lang/Class;Ljava/util/List;)Ljava/lang/invoke/MethodType; +46 -1: (Lsun/misc/URLClassPath$Loader;)Ljava/net/URL; +2 -1: ja +13 -1: synchronized +76 -1: ([DLjava/util/function/IntToDoubleFunction;)Ljava/util/function/IntConsumer; +11 -1: wrapperType +51 -1: <T:Ljava/lang/Object;>()Ljava/util/Comparator<TT;>; +106 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; +17 -1: removeAllElements +2 -1: ji +24 -1: java/util/Vector$ListItr +14 -1: getNestedTypes +6 -1: L_MARK +27 -1: Source does not fit in dest +2 -1: l1 +2 -1: jp +2 -1: l2 +4 -1: (J)B +57 -1: (Ljava/lang/String;Ljava/util/Locale;I)Ljava/lang/String; +4 -1: (J)C +27 -1: ForEachTransformedValueTask +2 -1: l4 +26 -1: java/util/Hashtable$KeySet +4 -1: (J)D +2 -1: l5 +39 -1: java/security/BasicPermissionCollection +4 -1: (J)F +2 -1: jv +29 -1: MapReduceMappingsToDoubleTask +18 -1: copyFromShortArray +2 -1: l9 +3 -1: TV; +4 -1: (J)I +4 -1: (J)J +23 -1: Lsun/util/calendar/Era; +8 -1: x-EUC-TW +15 -1: checkSetFactory +7 -1: Special +4 -1: (J)S +4 -1: (J)V +7 -1: invoke_ +25 -1: (IC)Ljava/nio/ByteBuffer; +68 -1: (JLjava/util/concurrent/TimeUnit;)Ljava/nio/file/attribute/FileTime; +36 -1: ()Ljava/net/URLStreamHandlerFactory; +4 -1: (J)Z +181 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;)Z +9 -1: getOffset +46 -1: (ILjava/lang/String;)Ljava/lang/StringBuilder; +7 -1: element +15 -1: createByteArray +17 -1: uncaughtException +2 -1: ko +29 -1: java/nio/file/DirectoryStream +15 -1: getISOCountries +246 -1: <NoSuchMemberException:Ljava/lang/ReflectiveOperationException;>(BLjava/lang/invoke/MemberName;Ljava/lang/Class<*>;Ljava/lang/Class<TNoSuchMemberException;>;)Ljava/lang/invoke/MemberName;^Ljava/lang/IllegalAccessException;^TNoSuchMemberException; +7 -1: invoker +17 -1: langReflectAccess +10 -1: bindSingle +23 -1: java/lang/reflect/Proxy +2 -1: lb +2 -1: lc +29 -1: CREATE_CLASSLOADER_PERMISSION +5 -1: isSet +18 -1: ([Ljava/io/File;)V +15 -1: urlNoFragString +21 -1: DirectByteBuffer.java +15 -1: java.class.path +15 -1: createDirectory +4 -1: GMT +37 -1: sun/reflect/annotation/ExceptionProxy +28 -1: (Ljava/util/Map<+TK;+TV;>;)V +17 -1: getContentHandler +26 -1: GenericDeclRepository.java +56 -1: (Ljava/lang/String;)Ljava/lang/IllegalArgumentException; +15 -1: getJavaIOAccess +39 -1: java/util/Collections$EmptyListIterator +34 -1: java/lang/ConditionalSpecialCasing +82 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean; +58 -1: (Ljava/util/function/ToIntFunction;)Ljava/util/Comparator; +21 -1: ()Lsun/misc/Launcher; +2 -1: lt +92 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIILjava/util/concurrent/ConcurrentHashMap;)V +8 -1: disjoint +62 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/net/URLStreamHandler;)V +24 -1: ([Ljava/lang/Object;)TT; +4 -1: lmap +8 -1: SATURDAY +12 -1: toStringUTF8 +91 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BinaryOperator;)Ljava/lang/Object; +46 -1: pkcs11 PKCS11 session manager debugging +27 -1: (Z)Ljava/lang/StringBuffer; +7 -1: forEach +17 -1: (Ljava/io/File;)I +17 -1: (Ljava/io/File;)J +5 -1: RESET +6 -1: isFile +14 -1: Exception.java +8 -1: isPublic +27 -1: computeInitialPreparedForms +50 -1: (BZLjava/lang/Class;)Ljava/lang/invoke/LambdaForm; +19 -1: getAvailableLocales +17 -1: (Ljava/io/File;)V +28 -1: ()Ljava/nio/charset/Charset; +10 -1: appendNull +17 -1: (Ljava/io/File;)Z +23 -1: java/lang/InternalError +2 -1: ne +6 -1: radix +8 -1: checksum +66 -1: (Lsun/net/www/MessageHeader;Ljava/lang/String;Ljava/lang/Object;)V +45 -1: (Ljava/io/FilenameFilter;)[Ljava/lang/String; +8 -1: checkJar +28 -1: default format locale = +13 -1: normalizeTime +26 -1: java/util/AbstractList$Itr +30 -1: java/util/function/IntFunction +7 -1: TIS-620 +12 -1: reverseBytes +2 -1: of +26 -1: java/lang/ClassFormatError +109 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +10 -1: delimiters +20 -1: indexOfSupplementary +16 -1: aliases_UTF_32LE +134 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/Map<TK;TV;>; +17 -1: [Ljava/lang/Long; +2 -1: or +12 -1: getClassName +31 -1: (Ljava/nio/charset/Charset;FF)V +6 -1: ([FF)I +39 -1: (Ljava/util/Locale;)[Ljava/lang/String; +33 -1: java/util/WeakHashMap$KeyIterator +8 -1: UTF_16LE +12 -1: isISOControl +75 -1: (Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;Z)Ljava/nio/charset/CoderResult; +7 -1: ([I[I)Z +28 -1: Self-causation not permitted +6 -1: ([FF)V +14 -1: defaultCharset +12 -1: isJavaLetter +2 -1: pm +39 -1: cannot reflectively invoke MethodHandle +20 -1: getSystemClassLoader +42 -1: ([Ljava/lang/Object;IILjava/lang/Object;)I +56 -1: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; +42 -1: ([Ljava/lang/Object;IILjava/lang/Object;)V +12 -1: ZipFile.java +43 -1: (Ljava/util/zip/ZipFile;)Ljava/lang/String; +22 -1: Ljava/net/InetAddress; +15 -1: getCharVolatile +32 -1: ()[Ljava/lang/reflect/Parameter; +13 -1: delimsChanged +13 -1: getFileSystem +13 -1: METHOD_RETURN +20 -1: sun/misc/PerfCounter +61 -1: (Ljava/util/HashMap<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>; +8 -1: newIndex +18 -1: getDisplayLanguage +36 -1: (C)Ljava/lang/AbstractStringBuilder; +36 -1: java/lang/StringCoding$StringEncoder +12 -1: forEachEntry +23 -1: [Ljava/io/Serializable; +13 -1: totalCapacity +26 -1: java/io/FileOutputStream$1 +14 -1: signatureArity +90 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/security/cert/Certificate;)V +17 -1: reflectionFactory +6 -1: ibm737 +17 -1: fillInStackTrace0 +16 -1: allocateInstance +52 -1: (Lsun/util/locale/BaseLocale$Key;)Ljava/lang/String; +9 -1: addExtURL +16 -1: copyFromIntArray +65 -1: (Ljava/security/Permission;Z)Ljava/security/PermissionCollection; +57 -1: java/util/concurrent/ConcurrentHashMap$SearchMappingsTask +10 -1: toEpochDay +4 -1: gate +24 -1: sun/nio/cs/UTF_8$Decoder +8 -1: entries2 +18 -1: isCharsetSupported +10 -1: toCustomID +2 -1: rw +33 -1: java/nio/ByteBufferAsFloatBufferB +25 -1: (ID)Ljava/nio/ByteBuffer; +12 -1: addTimeOfDay +61 -1: (Ljava/security/ProtectionDomain;Ljava/security/Permission;)Z +2 -1: sd +25 -1: (Ljava/net/InetAddress;)V +33 -1: java/nio/ByteBufferAsFloatBufferL +2 -1: se +15 -1: hasQueuedThread +24 -1: assertMemberIsConsistent +37 -1: java/util/Collections$UnmodifiableMap +2 -1: sp +20 -1: setJavaUtilJarAccess +96 -1: (Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class; +8 -1: language +76 -1: <T:Ljava/lang/Object;>(Ljava/util/SortedSet<TT;>;)Ljava/util/SortedSet<TT;>; +11 -1: findLibrary +61 -1: (Ljava/lang/Class<*>;)Lsun/reflect/annotation/AnnotationType; +6 -1: ([BZ)V +24 -1: DEFAULT_BYTE_BUFFER_SIZE +25 -1: (Ljava/util/ArrayList;I)V +2 -1: th +47 -1: (Ljava/util/Collection;)Ljava/util/Enumeration; +49 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/net/URL; +7 -1: ([D[D)Z +2 -1: to +22 -1: java/util/Locale$Cache +8 -1: iterator +30 -1: (Ljava/lang/StringBuilder;IZ)V +17 -1: ()Ljava/util/Set; +2 -1: tr +27 -1: (Ljava/nio/ByteBuffer;ISZ)V +6 -1: method +13 -1: allPermission +9 -1: ruleArray +8 -1: UTC_TIME +10 -1: LF_COUNTER +26 -1: Lsun/nio/cs/StreamDecoder; +25 -1: ()Lsun/util/calendar/Era; +6 -1: LOCHDR +21 -1: sun/net/www/MimeTable +12 -1: Cannot cast +2 -1: us +2 -1: ut +52 -1: Ljava/lang/invoke/MethodHandle$PolymorphicSignature; +6 -1: encode +15 -1: CharBuffer.java +24 -1: (C)Ljava/nio/ByteBuffer; +56 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)V +18 -1: getEnclosingMethod +56 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)Z +6 -1: ibm775 +9 -1: (IIIIII)I +44 -1: ([JLjava/util/function/IntToLongFunction;I)V +9 -1: (IIIIII)J +64 -1: (Ljava/util/Locale$LocaleKey;)Lsun/util/locale/LocaleExtensions; +2 -1: x- +2 -1: vm +5 -1: clock +9 -1: (IIIIII)V +10 -1: XmlSupport +19 -1: sun/nio/cs/US_ASCII +10 -1: toRealPath +5 -1: cp367 +6 -1: ST_END +58 -1: [Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule; +12 -1: hasSameRules +108 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/LocaleExtensions; +22 -1: java/lang/Class$Atomic +4 -1: sync +6 -1: listen +12 -1: firstElement +142 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B[B)Ljava/lang/reflect/Method; +18 -1: internalProperties +28 -1: (Ljava/lang/StringBuffer;C)V +7 -1: factory +18 -1: ()Ljava/util/List; +50 -1: (Ljava/util/concurrent/CountedCompleter;[D[DIIII)V +44 -1: (Ljava/lang/Class;)Lsun/invoke/util/Wrapper; +83 -1: (JLjava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)D +12 -1: erasedType: +53 -1: (Ljava/util/AbstractList;Ljava/util/AbstractList$1;)V +20 -1: Cannot find package +27 -1: java/util/ArrayList$ListItr +10 -1: copyMethod +23 -1: java/lang/ThreadLocal$1 +16 -1: iso_646.irv:1983 +42 -1: (Ljava/lang/Thread;Ljava/lang/Throwable;)V +19 -1: DEFAULT_LOAD_FACTOR +40 -1: ([Ljava/lang/Object;)[Ljava/lang/Object; +10 -1: (JJJ[BII)I +12 -1: singletonMap +8 -1: RESERVED +9 -1: zipAccess +21 -1: SynchronizedSortedMap +4 -1: flag +15 -1: UnmodifiableSet +18 -1: WrappedPrintWriter +7 -1: resume0 +2 -1: yi +10 -1: erasedType +31 -1: CHECK_AWT_EVENTQUEUE_PERMISSION +8 -1: <clinit> +59 -1: (Ljava/lang/String;)Ljava/security/cert/CertificateFactory; +40 -1: java/lang/management/MemoryManagerMXBean +33 -1: newGetIntIllegalArgumentException +16 -1: iso_646.irv:1991 +58 -1: (Ljava/lang/ClassValue$Entry;)Ljava/lang/ClassValue$Entry; +2 -1: zc +26 -1: (Ljava/util/AbstractMap;)V +6 -1: THROWS +11 -1: toCharArray +64 -1: (Ljava/lang/reflect/Constructor;)Ljava/lang/reflect/Constructor; +2 -1: zh +68 -1: Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>; +25 -1: (Ljava/util/Collection;)V +20 -1: getJdkSpecialVersion +17 -1: getTypeParameters +32 -1: [Ljava/lang/ClassValue$Entry<*>; +25 -1: (Ljava/util/Collection;)Z +26 -1: Lsun/nio/ch/Interruptible; +5 -1: 0.0p0 +5 -1: CACHE +7 -1: namesOK +21 -1: Ljava/lang/Exception; +51 -1: (Ljava/net/URL;Lsun/net/www/protocol/jar/Handler;)V +19 -1: jdk_special_version +66 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;)Ljava/util/List<TT;>; +75 -1: (Ljava/util/Comparator;Ljava/util/function/Function;)Ljava/util/Comparator; +11 -1: Arrays.java +19 -1: (Ljava/lang/Byte;)I +17 -1: java/lang/Class$1 +17 -1: java/lang/Class$2 +17 -1: java/lang/Class$3 +47 -1: java/lang/invoke/MethodHandleImpl$WrappedMember +17 -1: java/lang/Class$4 +44 -1: (Ljava/lang/Throwable;)Ljava/lang/Throwable; +9 -1: charCount +24 -1: ()Ljava/net/FileNameMap; +44 -1: sun/util/locale/provider/TimeZoneNameUtility +17 -1: not an array type +2 -1: {} +24 -1: (Lsun/misc/Launcher$1;)V +12 -1: directMemory +10 -1: parameters +5 -1: java. +14 -1: allocateDirect +51 -1: (Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder; +23 -1: java/nio/file/Watchable +37 -1: createDiagnosticFrameworkNotification +54 -1: (Ljava/lang/Class<*>;Z)Ljava/lang/invoke/MethodHandle; +35 -1: sun/nio/cs/StandardCharsets$Aliases +9 -1: retDelims +11 -1: MAX_ENTRIES +12 -1: CumulateTask +64 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedKeyTask +3 -1: iae +12 -1: AF_PUTSTATIC +21 -1: java/lang/Throwable$1 +45 -1: (Ljava/util/HashMap;)Ljava/util/HashMap$Node; +77 -1: (JLjava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)I +5 -1: after +29 -1: (Ljava/security/CodeSource;)Z +248 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V +6 -1: H_URIC +7 -1: L_DIGIT +7 -1: toNanos +24 -1: (D)Ljava/nio/ByteBuffer; +25 -1: getDiagnosticCommandMBean +6 2: [LFoo; +14 -1: path.separator +16 -1: toUnsignedString +40 -1: DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR +87 -1: (Ljava/security/Permission;[Ljava/security/cert/Certificate;)Ljava/security/Permission; +16 -1: inheritedChannel +11 -1: audio/basic +27 -1: sun.classloader.findClasses +10 -1: queryCount +20 -1: NF_ensureInitialized +12 -1: getBufIfOpen +23 -1: sun/nio/cs/UTF_16LE_BOM +134 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; +17 -1: getImplMethodName +14 -1: linkMethod => +25 -1: Ljava/lang/invoke/Stable; +32 -1: Ljava/lang/annotation/Retention; +7 -1: doInput +9 -1: -_.!~*'() +62 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;B)V +18 -1: separateWithCommas +43 -1: com/sun/crypto/provider/CipherBlockChaining +14 -1: createTempFile +9 -1: implFlush +20 -1: getOffsetsByStandard +21 -1: OutOfMemoryError.java +7 -1: jce.jar +46 -1: java/util/Collections$UnmodifiableNavigableMap +30 -1: (Ljava/io/File;)Ljava/io/File; +15 -1: LinkedList.java +15 -1: iso_8859-9:1989 +50 -1: sun/reflect/generics/factory/CoreReflectionFactory +10 -1: : JVM has +19 -1: HeapByteBuffer.java +6 -1: getURL +37 -1: java/security/cert/CertificateFactory +23 -1: getAllowUserInteraction +12 -1: otherParents +25 -1: ARRAY_BOOLEAN_BASE_OFFSET +9 -1: L_UPALPHA +41 -1: ([Ljava/util/Hashtable$Entry<**>;TK;TV;)V +6 -1: LOCHOW +11 -1: access$1300 +30 -1: sun/reflect/MethodAccessorImpl +130 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;)Ljava/util/List<Ljava/util/Locale;>; +9 -1: MALFORMED +38 -1: (Ljava/lang/String;I)Ljava/lang/Short; +26 -1: File format not recognised +12 -1: setTimeOfDay +19 -1: java/lang/Exception +15 -1: getOutputStream +74 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +41 -1: (Ljava/lang/String;Z)Ljava/util/TimeZone; +49 -1: Lsun/reflect/generics/repository/ClassRepository; +8 -1: getCerts +90 -1: (Ljava/lang/Class<*>;ZLjava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>; +5 -1: clone +32 -1: ()Ljava/lang/ref/Reference$Lock; +15 -1: caseIgnoreMatch +23 -1: (Ljava/util/Set<TE;>;)V +25 -1: enumerateStringProperties +9 -1: inherited +4 -1: flip +8 -1: setMonth +38 -1: (Ljava/util/function/Consumer<-TV;>;)V +55 -1: java/util/concurrent/ConcurrentHashMap$ReduceValuesTask +37 -1: getJavaSecurityProtectionDomainAccess +67 -1: (Ljava/util/NavigableSet;Ljava/lang/Class;)Ljava/util/NavigableSet; +10 -1: reduceKeys +14 -1: MAX_CODE_POINT +24 -1: getGenericExceptionTypes +8 -1: fraction +30 -1: java/lang/InterruptedException +46 -1: (Ljava/lang/String;II[BI)Ljava/nio/ByteBuffer; +114 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$TreeNode<TK;TV;>;)V +66 -1: (Ljava/lang/String;Ljava/lang/Throwable;)Ljava/lang/InternalError; +45 -1: (Ljava/lang/Object;)Ljava/lang/StringBuilder; +8 -1: putLongB +101 -1: (Ljava/nio/channels/ReadableByteChannel;Ljava/nio/charset/CharsetDecoder;I)Lsun/nio/cs/StreamDecoder; +16 -1: standardProvider +14 -1: parameterCount +59 -1: (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/util/List; +8 -1: putLongL +12 -1: (TT;TV;TV;)Z +37 -1: Lsun/reflect/ConstructorAccessorImpl; +11 -1: ([CII[CII)V +14 -1: parameterArray +15 -1: | interpretName +62 -1: (JLjava/util/function/Function;Ljava/util/function/Consumer;)V +30 -1: (Z)Lsun/reflect/FieldAccessor; +19 -1: jvm_special_version +22 -1: java/util/ArrayDeque$1 +12 -1: initVersions +22 -1: java/lang/CharSequence +21 -1: NF_internalMemberName +5 -1: ()TE; +97 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;IZ)Ljava/lang/invoke/MethodHandle; +25 -1: java/nio/MappedByteBuffer +6 -1: addURL +17 -1: isConvertibleFrom +14 -1: extendWithType +9 -1: interrupt +11 -1: floorDivide +16 -1: x-ISO-2022-CN-GB +12 -1: CheckedQueue +7 -1: setLong +64 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V +108 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;TV;>;)Ljava/util/NavigableMap<TK;TV;>; +38 -1: java/nio/channels/spi/SelectorProvider +17 -1: makeGuardWithTest +20 -1: (Ljava/util/List;Z)V +8 -1: val$path +12 -1: Runtime.java +7 -1: channel +71 -1: <T:Ljava/lang/Object;>([TT;IILjava/util/function/BinaryOperator<TT;>;)V +18 -1: initializedHeaders +32 -1: ()[Lsun/launcher/LauncherHelper; +13 -1: jvInitialized +10 -1: getSeconds +7 -1: Decoder +20 -1: getYearFromFixedDate +6 -1: PREFIX +21 -1: sun.boot.library.path +11 -1: FIXED_DATES +33 -1: (JLjava/util/function/Consumer;)V +27 -1: initializeJavaAssertionMaps +13 -1: toOctalString +9 -1: fixResult +10 -1: typeParams +94 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)Ljava/util/concurrent/ConcurrentHashMap$Node; +4 -1: Help +11 -1: setIfNotSet +39 -1: java/lang/UnsupportedOperationException +15 -1: zip file closed +8 -1: floorDiv +10 -1: canExecute +10 -1: encodeLoop +18 -1: addRequestProperty +56 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V +10 -1: superclass +5 -1: close +56 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)Z +6 -1: ignore +32 -1: ()Ljava/lang/ref/ReferenceQueue; +19 -1: java/util/Formatter +27 -1: java/lang/ClassLoaderHelper +23 -1: (Ljava/lang/Object;JZ)V +29 -1: java/nio/file/WatchEvent$Kind +6 -1: CENLEN +4 -1: SIZE +68 -1: <T:Ljava/lang/Object;>(Ljava/util/Deque<TT;>;)Ljava/util/Queue<TT;>; +9 -1: isEscaped +12 -1: LF_INTERPRET +22 -1: (I)Ljava/lang/Integer; +60 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName; +49 -1: ([Ljava/lang/Class<*>;Ljava/lang/StringBuilder;)V +11 -1: getZipEntry +16 -1: metaInfFilenames +27 -1: (Ljava/lang/StringBuffer;)V +57 -1: (Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry<TK;TV;>; +76 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class<*>; +8 -1: ([CII)[B +27 -1: (Ljava/lang/StringBuffer;)Z +24 -1: getManifestFromReference +8 -1: ([CII)[C +10 -1: H_LOWALPHA +18 -1: FileURLMapper.java +12 -1: fxLaunchMode +24 -1: isMethodHandleInvokeName +17 -1: winTimeToFileTime +19 -1: checkTopLevelWindow +26 -1: MapReduceMappingsToIntTask +13 -1: NORM_PRIORITY +18 -1: lookupViaProviders +30 -1: (I)Ljava/util/LinkedList$Node; +3 -1: UTC +10 -1: UNMAPPABLE +68 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;B)V +9 -1: META-INF/ +13 -1: Iterable.java +71 -1: ([Ljava/lang/reflect/Field;Ljava/lang/String;)Ljava/lang/reflect/Field; +9 -1: setOffset +8 -1: FJObject +50 -1: (Ljava/lang/CharSequence;)Ljava/util/StringJoiner; +23 -1: java/nio/HeapByteBuffer +23 -1: sun/util/PreHashedMap$1 +23 -1: sun/util/PreHashedMap$2 +34 -1: newGetCharIllegalArgumentException +40 -1: jca JCA engine class debugging +39 -1: (Ljava/lang/Object;I)Ljava/lang/Object; +42 -1: (Ljava/lang/String;)Ljava/net/InetAddress; +25 -1: (Ljava/net/FileNameMap;)V +44 -1: (Ljava/util/SortedMap;)Ljava/util/SortedMap; +52 -1: (Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long; +27 -1: ()[Ljava/util/HashMap$Node; +29 -1: java/util/EmptyStackException +16 -1: not a field type +14 -1: Ljava/io/File; +28 -1: ()[Ljava/security/Principal; +69 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)V +5 -1: ()TK; +10 -1: ISO8859-13 +40 -1: java/lang/invoke/DirectMethodHandle$Lazy +30 -1: The object is not initialized. +10 -1: ISO8859-15 +88 -1: <E:Ljava/lang/Object;>(Ljava/util/List<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/List<TE;>; +25 -1: (ZILjava/lang/String;II)Z +9 -1: stackSize +61 -1: (Ljava/util/Comparator;Ljava/lang/Object;Ljava/lang/Object;)I +16 -1: synchronizedList +90 -1: (Ljava/util/Comparator;Ljava/util/function/Function;Ljava/lang/Object;Ljava/lang/Object;)I +9 -1: nullCheck +174 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry<TT;>;Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry<TT;>;>; +14 -1: java/lang/Enum +3 -1: int +13 -1: detailMessage +56 -1: java/util/concurrent/ConcurrentHashMap$SearchEntriesTask +10 -1: ISO-8859-1 +10 -1: ISO-8859-2 +10 -1: ISO-8859-3 +10 -1: ISO-8859-4 +10 -1: ISO-8859-5 +10 -1: ISO-8859-6 +24 -1: ([Ljava/lang/Object;II)V +10 -1: ISO-8859-7 +10 -1: ISO-8859-8 +139 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)[Ljava/util/concurrent/ConcurrentHashMap$Node; +10 -1: ISO-8859-9 +5 -1: round +25 -1: DIRECTIONALITY_WHITESPACE +13 -1: NamedFunction +10 -1: startAgent +3 -1: ioe +7 -1: closing +24 -1: appendSchemeSpecificPart +56 -1: sun/reflect/ReflectionFactory$GetReflectionFactoryAction +83 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/reflect/ReflectionFactory;>; +17 -1: isCharsetDetected +11 -1: getJarFiles +22 -1: getEnclosingMethodInfo +11 -1: setReadable +61 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)V +10 -1: attachment +34 -1: (Ljava/io/File;)Ljava/lang/String; +18 -1: ZipFileInputStream +15 -1: CodeSource.java +61 -1: (Ljava/util/jar/JarFile;)Ljava/util/List<Ljava/lang/Object;>; +7 -1: cp00858 +108 -1: ([Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;II)Ljava/lang/invoke/LambdaForm$Name; +38 -1: ([DII)Ljava/util/Spliterator$OfDouble; +8 -1: val$name +11 -1: LF_REINVOKE +12 -1: validateTime +17 -1: copyFromCharArray +32 -1: throwSetIllegalArgumentException +14 -1: fieldModifiers +52 -1: (Ljava/lang/ClassValue;)Ljava/lang/ClassValue$Entry; +58 -1: (Ljava/io/OutputStream;Ljava/nio/charset/CharsetEncoder;)V +14 -1: generalInvoker +11 -1: interrupted +246 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToLongTask;Ljava/util/function/ToLongBiFunction;JLjava/util/function/LongBinaryOperator;)V +20 -1: java/util/Dictionary +17 -1: getDoubleVolatile +41 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;)Z +8 -1: intValue +24 -1: (F)Ljava/nio/ByteBuffer; +5 -1: reset +54 -1: (ILjava/util/List;)[Ljava/lang/invoke/LambdaForm$Name; +19 -1: [Ljava/util/Locale; +43 -1: (Ljava/lang/String;II)Ljava/nio/ByteBuffer; +38 -1: ()Ljava/io/ObjectInputStream$GetField; +18 -1: [Locked by thread +10 -1: checkedMap +16 -1: checkedSortedMap +14 -1: illegal symbol +16 -1: TITLECASE_LETTER +4 -1: root +22 -1: (ZLjava/lang/String;)V +27 -1: ([JII)Ljava/nio/LongBuffer; +15 -1: printStackTrace +30 -1: newConstructorForSerialization +14 -1: getPermissions +13 -1: toStringCache +15 -1: equalParamTypes +37 -1: throwFinalFieldIllegalAccessException +35 -1: (Ljava/lang/String;Ljava/io/File;)V +34 -1: java/nio/charset/CoderResult$Cache +3 -1: .\n\n +33 -1: Ljava/nio/charset/CharsetEncoder; +11 -1: lambdaForms +65 -1: <T::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TT;>;)TT; +39 -1: (CLjava/lang/Class;Ljava/lang/Object;)Z +3 -1: ise +14 -1: forLanguageTag +45 -1: ([Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Z +36 -1: RuntimeInvisibleParameterAnnotations +12 -1: binarySearch +24 -1: getAssociatedAnnotations +10 -1: decoderFor +6 -1: ibm813 +10 -1: logicalXor +10 -1: setVarargs +71 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Void;)V +4 -1: cast +6 -1: ibm819 +23 -1: getParentDelegationTime +8 -1: ([FII)[F +4 -1: .tmp +6 -1: (JII)J +27 -1: ()Ljava/lang/ref/Finalizer; +9 -1: sunec.jar +22 -1: java/net/URLConnection +41 -1: (Ljava/lang/Runnable;Ljava/lang/String;)V +20 -1: SecurityManager.java +18 -1: getZipFileOpenTime +7 -1: country +13 -1: inflaterCache +4 -1: +17 -1: java/lang/Runtime +125 -1: (Lsun/management/GcInfoBuilder;JJJ[Ljava/lang/management/MemoryUsage;[Ljava/lang/management/MemoryUsage;[Ljava/lang/Object;)V +24 -1: java/util/ResourceBundle +64 -1: Ljava/util/Hashtable<Lsun/misc/Signal;Lsun/misc/SignalHandler;>; +79 -1: ([Ljava/util/WeakHashMap$Entry<TK;TV;>;[Ljava/util/WeakHashMap$Entry<TK;TV;>;)V +8 -1: x-ibm737 +39 -1: (Ljava/security/AccessControlContext;)Z +21 -1: (Ljava/lang/Class;I)V +4 -1: - +15 -1: calculateFields +22 -1: Ljava/util/Properties; +8 -1: getArray +21 -1: (Ljava/lang/Class;I)Z +41 -1: (Ljava/lang/ThreadGroup;)Ljava/lang/Void; +17 -1: Ljava/io/Console; +115 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object; +12 -1: nextThreadID +81 -1: (Ljava/net/URLClassLoader;Ljava/lang/SecurityManager;Ljava/security/Permission;)V +23 -1: ARRAY_SHORT_BASE_OFFSET +10 -1: interpret_ +144 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +20 -1: isIPv6LiteralAddress +13 -1: launcher_name +36 -1: java/util/function/IntToLongFunction +38 -1: java/util/WeakHashMap$EntrySpliterator +8 -1: copyInto +3 -1: ACT +11 -1: metafactory +31 -1: ([BLjava/nio/charset/Charset;)V +30 -1: java/lang/annotation/Retention +13 -1: getYearLength +42 -1: java/util/AbstractMap$SimpleImmutableEntry +31 -1: ()Ljava/lang/invoke/MemberName; +21 -1: sun/misc/JavaIOAccess +16 -1: jdk_build_number +8 -1: ST_RESET +96 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName; +13 -1: getTypeString +15 -1: maxCharsPerByte +8 -1: checkKey +49 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/UTF_8$1;)V +80 -1: (Ljava/lang/ClassValue;Ljava/lang/ClassValue$Entry;)Ljava/lang/ClassValue$Entry; +32 -1: Ljava/security/ProtectionDomain; +5 -1: ()TT; +6 -1: insert +10 -1: intersects +38 -1: ([Ljava/lang/Class;Ljava/lang/Class;)V +15 -1: java/lang/Class +12 -1: getPublicKey +37 -1: (ID)Ljava/lang/AbstractStringBuilder; +6 -1: ibm850 +6 -1: locsig +6 -1: ibm852 +19 -1: changeReferenceKind +3 -1: AET +42 -1: (Ljava/util/Collection;Ljava/lang/Class;)V +6 -1: ibm855 +16 -1: getPolicyNoCheck +39 -1: ([B)[[Ljava/lang/annotation/Annotation; +6 -1: ibm857 +10 -1: Error.java +38 -1: ()Ljava/util/List<Ljava/lang/Object;>; +14 -1: createInstance +5 -1: cp437 +28 -1: Lsun/util/locale/BaseLocale; +17 -1: getStandardOffset +29 -1: sun/nio/cs/StandardCharsets$1 +36 -1: Ljava/security/ProtectionDomain$Key; +14 -1: ArrayList.java +78 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName; +38 -1: java/lang/invoke/MethodHandleImpl$Lazy +42 -1: (Ljava/util/LinkedHashMap$Entry<TK;TV;>;)V +8 -1: getLongB +7 -1: vmentry +21 -1: lookupExtendedCharset +32 -1: <T:Ljava/lang/Object;>([TT;)[TT; +53 -1: Ljava/util/concurrent/ConcurrentHashMap$EntrySetView; +6 -1: ibm862 +8 -1: getLongL +32 -1: (Ljava/lang/invoke/MemberName;)J +67 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/misc/Perf;>; +6 -1: region +6 -1: ibm866 +89 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)Lsun/reflect/ConstructorAccessor; +22 -1: java/util/ListIterator +9 -1: wednesday +16 -1: unsuspendThreads +20 -1: Not a Proxy instance +45 -1: Ljava/lang/reflect/InvocationTargetException; +61 -1: (Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder; +5 -1: ()TV; +32 -1: (Ljava/lang/invoke/MemberName;)V +82 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)[Ljava/security/cert/Certificate; +34 -1: java/lang/ClassValue$ClassValueMap +32 -1: (Ljava/lang/invoke/MemberName;)Z +15 -1: SPACE_SEPARATOR +17 -1: caseIgnoreCompare +58 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodHandles$Lookup; +4 -1: Code +3 -1: AGT +54 -1: (Ljava/lang/StringBuilder;II)Ljava/lang/StringBuilder; +6 -1: ibm874 +15 -1: newMemberBuffer +42 -1: (Ljava/nio/file/Path;)Ljava/nio/file/Path; +33 -1: Ljava/lang/NumberFormatException; +10 -1: Field.java +67 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;)TU; +4 -1: rows +12 -1: MIN_PRIORITY +25 -1: URI has a query component +41 -1: provider security provider debugging +29 -1: sun/nio/cs/ISO_8859_1$Encoder +26 -1: (Ljava/util/zip/ZipFile;)I +26 -1: (Ljava/util/zip/ZipFile;)J +20 -1: Bad digit at end of +29 -1: Ljava/lang/RuntimePermission; +12 -1: initResolved +9 -1: loadFence +13 -1: fieldAccessor +26 -1: (Ljava/util/zip/ZipFile;)V +36 -1: java/lang/CloneNotSupportedException +19 -1: getBasicConstraints +16 -1: putOrderedObject +26 -1: (Ljava/util/zip/ZipFile;)Z +6 -1: target +50 -1: (Ljava/util/concurrent/CountedCompleter;[I[IIIII)V +16 -1: changeReturnType +13 -1: CAUSE_CAPTION +14 -1: checkExactType +50 -1: sun/util/locale/provider/LocaleServiceProviderPool +47 -1: java/lang/invoke/DirectMethodHandle$Constructor +20 -1: CallerSensitive.java +30 -1: java/security/ProtectionDomain +26 -1: java.launcher.opt.vmselect +4 -1: \tat +42 -1: java/util/ArraysParallelSortHelpers$FJLong +39 -1: (Ljava/lang/String;)[Ljava/lang/String; +19 -1: sun.net.www.content +34 -1: ([III)Ljava/util/stream/IntStream; +20 -1: (Ljava/util/Deque;)V +35 -1: (Ljava/lang/reflect/Constructor;)[B +16 -1: WeakHashMap.java +8 -1: ([III)[I +46 -1: (Ljava/util/Properties;Ljava/io/InputStream;)V +54 -1: (I)Ljava/util/concurrent/ConcurrentHashMap$KeySetView; +65 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/util/HashMap$Node; +27 -1: URI path component is empty +3 -1: -1- +32 -1: Ljava/io/InterruptedIOException; +9 -1: setLocale +7 -1: [^, ;]* +6 -1: format +61 -1: (Ljava/util/function/Supplier;IZ)Ljava/util/stream/IntStream; +20 -1: getRequestProperties +16 -1: reallocateMemory +28 -1: java/lang/IllegalAccessError +5 -1: query +83 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;II)Ljava/lang/invoke/MethodHandle; +7 -1: threadQ +6 -1: STATIC +7 -1: enqueue +21 -1: uninitializedCallSite +3 -1: -2- +26 -1: ()Ljava/util/NavigableSet; +27 -1: getUncaughtExceptionHandler +42 -1: ([Ljava/net/URL;)Ljava/net/URLClassLoader; +30 -1: java/lang/UnsatisfiedLinkError +39 -1: java/util/Collections$ReverseComparator +7 -1: resolve +4 -1: poll +7 -1: (TE;I)V +21 -1: : Unknown launch mode +53 -1: (Ljava/lang/Class;)[Ljava/lang/annotation/Annotation; +36 -1: sun.classloader.parentDelegationTime +25 -1: java/net/URLStreamHandler +39 -1: (Ljava/lang/Object;J)Ljava/lang/Object; +53 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>; +51 -1: java/util/ArraysParallelSortHelpers$FJDouble$Sorter +26 -1: getAnnotatedParameterTypes +18 -1: codePointCountImpl +7 -1: threads +12 -1: offsetBefore +29 -1: ()Ljava/util/Collection<TV;>; +58 -1: (Lsun/invoke/util/Wrapper;)Ljava/lang/invoke/MethodHandle; +17 -1: DMH.invokeSpecial +3 -1: -3- +16 -1: decodeBufferLoop +43 -1: java/util/concurrent/atomic/AtomicReference +16 -1: reduceKeysToLong +27 -1: newIllegalArgumentException +3 -1: ALL +5 -1: cache +5 -1: queue +4 -1: 8bit +3 -1: -4- +35 -1: Ljava/util/Set<Ljava/lang/String;>; +9 -1: MIN_RADIX +26 -1: ZipFileInflaterInputStream +13 -1: MANIFEST_NAME +18 -1: java/util/TimeZone +175 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/invoke/DirectMethodHandle$1;)V +16 -1: getContentLength +11 -1: setDoOutput +22 -1: (Ljava/io/Closeable;)V +13 -1: TimeZone.java +28 -1: sun/misc/ExtensionDependency +6 -1: bindTo +3 -1: -5- +40 -1: ()[Ljava/util/WeakHashMap$Entry<TK;TV;>; +20 -1: ()Lsun/misc/Cleaner; +9 -1: compareTo +68 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToDoubleTask +11 -1: checkedList +14 -1: (ITK;TV;ZZ)TV; +5 -1: greek +3 -1: jar +21 -1: (Ljava/lang/String;)B +21 -1: (Ljava/lang/String;)C +21 -1: (Ljava/lang/String;)D +5 -1: (JS)V +21 -1: (Ljava/lang/String;)F +13 -1: LETTER_NUMBER +14 -1: isAlphaNumeric +21 -1: (Ljava/lang/String;)I +73 -1: (Ljava/nio/charset/Charset;Ljava/lang/String;Ljava/lang/StringCoding$1;)V +21 -1: (Ljava/lang/String;)J +25 -1: makeExactOrGeneralInvoker +3 -1: -6- +25 -1: java/nio/StringCharBuffer +21 -1: Ljava/util/Hashtable; +8 -1: ENQUEUED +8 -1: finalize +22 -1: DirectLongBufferU.java +21 -1: (Ljava/lang/String;)S +10 -1: localhost: +33 -1: isKnownNotToHaveSpecialAttributes +21 -1: (Ljava/lang/String;)V +45 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;Z)V +21 -1: (Ljava/lang/String;)Z +22 -1: (Ljava/util/Vector;I)V +44 -1: java/nio/charset/UnsupportedCharsetException +23 -1: java/lang/CharacterName +7 -1: checkIO +33 -1: (I)Lsun/misc/URLClassPath$Loader; +90 -1: (Ljava/lang/Class<*>;Ljava/lang/reflect/Constructor<*>;)Ljava/lang/reflect/Constructor<*>; +18 -1: getHeaderFieldDate +9 -1: MIN_VALUE +95 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Ljava/util/Collection; +44 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration; +8 -1: NOVEMBER +4 -1: gcal +17 -1: getConnectTimeout +124 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; +24 -1: INDEXOFSUBLIST_THRESHOLD +16 -1: isJulianLeapYear +21 -1: reduceEntriesToDouble +15 -1: getPrefixLength +17 -1: is not param at +14 -1: inDaylightTime +39 -1: (Ljava/lang/Class;[I)Ljava/lang/Object; +5 -1: force +98 -1: (Ljava/lang/Class;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z +40 -1: (Lsun/reflect/ConstructorAccessorImpl;)V +27 -1: RuntimeInvisibleAnnotations +17 -1: checkTargetChange +9 -1: skipBytes +4 -1: port +25 -1: sun/nio/cs/UTF_16$Encoder +28 -1: MIN_SUPPLEMENTARY_CODE_POINT +4 -1: node +11 -1: not param: +9 -1: debugInit +6 -1: setURL +14 -1: getMonthLength +23 -1: ()Ljava/nio/ByteBuffer; +17 -1: CALENDAR_JAPANESE +11 -1: access$1400 +16 -1: ()Ljava/net/URI; +29 -1: (IZ)Ljava/lang/StringBuilder; +15 -1: Native Library +30 -1: Invalid lambda deserialization +14 -1: throwException +18 -1: nothing to verify! +23 -1: (Ljava/lang/Object;JF)V +3 -1: ART +16 -1: isExtClassLoader +18 -1: Illegal Capacity: +26 -1: java/util/zip/ZipException +4 -1: /../ +34 -1: ([II)Ljava/util/Spliterator$OfInt; +26 -1: (I)Ljava/util/Enumeration; +60 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType; +43 -1: not a field or nested class, no simple type +12 -1: nextPutIndex +15 -1: getConstructor0 +3 -1: AST +11 -1: fromURIPath +49 -1: (Ljava/util/Collections$UnmodifiableCollection;)V +8 -1: makeImpl +51 -1: (Ljava/util/WeakHashMap;Ljava/util/WeakHashMap$1;)V +32 -1: (Ljava/util/function/Supplier;)V +20 -1: namedFunctionInvoker +37 -1: newGetBooleanIllegalArgumentException +19 -1: (Ljava/io/File;ZI)V +8 -1: NULL_KEY +36 -1: Ljava/lang/reflect/Constructor<TT;>; +21 -1: (Ljava/lang/Object;)B +21 -1: (Ljava/lang/Object;)C +21 -1: (Ljava/lang/Object;)D +21 -1: (Ljava/lang/Object;)F +30 -1: ()Lsun/util/locale/BaseLocale; +21 -1: (Ljava/lang/Object;)I +21 -1: (Ljava/lang/Object;)J +10 -1: lineNumber +95 -1: (JLjava/util/function/ToDoubleBiFunction<-TK;-TV;>;DLjava/util/function/DoubleBinaryOperator;)D +16 -1: CoderResult.java +44 -1: (Ljava/util/NavigableSet;Ljava/lang/Class;)V +21 -1: (Ljava/lang/Object;)S +13 -1: getNameString +129 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/invoke/DirectMethodHandle$1;)V +21 -1: (Ljava/lang/Object;)V +21 -1: (Ljava/lang/Object;)Z +81 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/util/LinkedList<Ljava/lang/String;>;>; +49 -1: java/util/concurrent/locks/ReentrantLock$FairSync +11 -1: csisolatin0 +11 -1: csisolatin1 +7 -1: isSpace +10 -1: getDefault +11 -1: csisolatin2 +16 -1: ()Ljava/net/URL; +11 -1: csisolatin4 +14 -1: invokeExact_MT +11 -1: csisolatin5 +28 -1: (Ljava/io/FileInputStream;)V +11 -1: csisolatin9 +8 -1: isLetter +15 -1: getConstructors +21 -1: mainAppContextDefault +29 -1: ()[Ljava/lang/reflect/Method; +23 -1: Ljava/util/WeakHashMap; +12 -1: LF_INVSTATIC +17 -1: DirectBuffer.java +10 -1: newEncoder +10 -1: getVersion +32 -1: java/lang/IllegalAccessException +20 -1: java/util/Collection +61 -1: (Ljava/util/concurrent/ConcurrentHashMap;Ljava/lang/Object;)V +19 -1: $deserializeLambda$ +8 -1: removeIf +25 -1: sun/reflect/FieldAccessor +129 -1: <U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;Ljava/util/Comparator<-TU;>;)Ljava/util/Comparator<TT;>; +17 -1: parseAbsoluteSpec +37 -1: ([Ljava/util/HashMap$Node<TK;TV;>;I)V +17 -1: java/util/HashSet +13 -1: spreadInvoker +20 -1: suppressAccessChecks +32 -1: Ljava/lang/InterruptedException; +11 -1: oldMappings +9 -1: lookupTag +16 -1: java/lang/System +5 -1: LFI: +6 -1: IBM737 +9 -1: SHORT_IDS +45 -1: ([IIILjava/util/function/IntBinaryOperator;)V +20 -1: getMetaInfEntryNames +10 -1: isReadOnly +50 -1: <E:Ljava/lang/Object;>()Ljava/util/SortedSet<TE;>; +12 -1: java.vm.name +30 -1: java/lang/Class$AnnotationData +61 -1: (ILjava/lang/invoke/LambdaForm;)Ljava/lang/invoke/LambdaForm; +7 -1: address +44 -1: (Ljava/util/function/BiConsumer<-TK;-TV;>;)V +58 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;I)V +112 -1: (Ljava/lang/Object;TV;Ljava/lang/ref/ReferenceQueue<Ljava/lang/Object;>;ILjava/util/WeakHashMap$Entry<TK;TV;>;)V +14 -1: aliases_KOI8_R +3 -1: AWT +14 -1: aliases_KOI8_U +24 -1: ARRAY_DOUBLE_BASE_OFFSET +23 -1: Ljava/util/jar/JarFile; +91 -1: (Ljava/lang/Class<TT;>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)V +14 -1: mappingAddress +19 -1: [Ljava/lang/Object; +17 -1: sun/misc/JarIndex +9 -1: image/jpg +49 -1: (Ljava/lang/String;I)Lsun/util/calendar/ZoneInfo; +37 -1: java/lang/invoke/DirectMethodHandle$1 +34 -1: java/util/Collections$SingletonMap +89 -1: (JLjava/util/function/ToIntBiFunction<-TK;-TV;>;ILjava/util/function/IntBinaryOperator;)I +46 -1: (Ljava/util/Comparator;)Ljava/util/Comparator; +11 -1: isTitleCase +38 -1: java/lang/IllegalMonitorStateException +33 -1: java/nio/BufferUnderflowException +28 -1: java/lang/ClassValue$Version +11 -1: printLocale +13 -1: STORE_BARRIER +42 -1: ([ILjava/util/function/IntUnaryOperator;)V +26 -1: sun/util/locale/BaseLocale +29 -1: java/io/ObjectStreamException +41 -1: sun/reflect/UnsafeStaticFieldAccessorImpl +60 -1: ([Ljava/lang/Object;Ljava/util/Iterator;)[Ljava/lang/Object; +30 -1: (Ljava/nio/charset/Charset;)[B +73 -1: ([Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process; +3 -1: VST +80 -1: Java(TM) SE Runtime Environment (build 1.8.0-internal-iklam_2013_11_27_21_25-b00 +85 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/net/URLStreamHandler;)V +20 -1: getStackTraceElement +36 -1: java/util/LinkedHashMap$LinkedKeySet +17 -1: getNormalizedYear +15 -1: maxBytesPerChar +16 -1: java/util/Random +34 -1: (I[C)Ljava/lang/invoke/LambdaForm; +11 -1: nbits < 0: +7 -1: H_PCHAR +29 -1: (Ljava/nio/charset/Charset;)I +36 -1: (I[I[C)Ljava/lang/invoke/LambdaForm; +23 -1: java/lang/ClassLoader$1 +23 -1: java/lang/ClassLoader$2 +23 -1: java/lang/ClassLoader$3 +9 -1: sizeTable +36 -1: (Z)Ljava/lang/AbstractStringBuilder; +29 -1: (Ljava/nio/charset/Charset;)V +25 -1: ARRAY_BOOLEAN_INDEX_SCALE +29 -1: (Ljava/nio/charset/Charset;)Z +6 -1: keySet +20 -1: declaredConstructors +25 -1: oracle/jrockit/jfr/Timing +12 -1: sizeIsSticky +6 -1: IBM775 +17 -1: currentTimeMillis +28 -1: java/nio/DirectDoubleBufferS +71 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/invoke/MethodType;B)V +28 -1: java/nio/DirectDoubleBufferU +19 -1: cachedFixedDateJan1 +15 -1: getClassContext +65 -1: (Ljava/security/CodeSource;Ljava/security/PermissionCollection;)V +16 -1: unmodifiableList +10 -1: getDoubleB +82 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/String; +11 -1: getEntryCrc +10 -1: getDoubleL +20 -1: (C)Ljava/lang/Class; +11 -1: Null action +22 -1: java/io/BufferedWriter +67 -1: (Ljava/lang/String;[Ljava/lang/Class<*>;)Ljava/lang/reflect/Method; +22 -1: parseSelectAnnotations +6 -1: rename +20 -1: acquireFieldAccessor +43 -1: (Ljava/util/Vector;[Ljava/lang/Object;III)V +32 -1: Ljava/lang/NullPointerException; +29 -1: (Ljava/lang/ThreadLocal<*>;)V +18 -1: descendingIterator +37 -1: java/util/Collections$SynchronizedSet +24 -1: mark/reset not supported +12 -1: ) > toIndex( +13 -1: <<ALL FILES>> +10 -1: fastRemove +4 -1: load +31 -1: sun/reflect/ReflectionFactory$1 +39 -1: (Ljava/util/List<*>;)Ljava/lang/Object; +39 -1: Ljava/util/Map<TE;Ljava/lang/Boolean;>; +9 -1: offerLast +31 -1: ()Ljava/lang/invoke/MethodType; +40 -1: <E:Ljava/lang/Object;>Ljava/lang/Object; +17 -1: getObjectVolatile +14 -1: suspendThreads +55 -1: (Ljava/lang/String;ZLjava/util/Set;)Lsun/misc/Resource; +75 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+Ljava/lang/Comparable<-TT;>;>;TT;)I +6 -1: Lookup +20 -1: java/io/OutputStream +41 -1: Could not create application class loader +28 -1: Lsun/misc/JavaUtilJarAccess; +46 -1: java/util/Collections$SynchronizedNavigableSet +8 -1: override +21 -1: threadLocalRandomSeed +10 -1: TEXT_PLAIN +22 -1: ([B)Ljava/lang/String; +29 -1: java/nio/InvalidMarkException +14 -1: Throwable.java +27 -1: newWrongMethodTypeException +6 -1: ptypes +8 -1: bugLevel +62 -1: (ILjava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder; +37 -1: ()Ljava/util/Locale$LocaleNameGetter; +14 -1: getEntryMethod +7 -1: getByte +12 -1: UTF-32BE-BOM +4 -1: lock +34 -1: java/security/AccessControlContext +79 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/MemberName; +5 -1: DEBUG +5 -1: unbox +17 -1: CLASSPATH_OPTOSFT +4 -1: cbrt +21 -1: LocalizedObjectGetter +21 -1: ProtectionDomain.java +22 -1: ([J)Ljava/util/BitSet; +17 -1: getUnresolvedName +34 -1: (Ljava/util/Map;Ljava/util/Map;I)V +7 -1: cskoi8r +14 -1: getInterfaces0 +48 -1: (Lsun/net/www/MessageHeader;)[Ljava/lang/String; +14 -1: getFileNameMap +16 -1: preserveCombiner +19 -1: getDefaultUseCaches +57 -1: (Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class; +21 -1: (D)Ljava/lang/Double; +15 -1: iso8859_15_fdis +23 -1: java/util/regex/Pattern +6 -1: ibm912 +14 -1: findBuiltinLib +42 -1: java/lang/annotation/AnnotationFormatError +6 -1: ibm914 +6 -1: ibm915 +44 -1: java/nio/charset/IllegalCharsetNameException +22 -1: COMBINING_SPACING_MARK +20 -1: ()Ljava/lang/Thread; +8 -1: readLine +12 -1: (unresolved +65 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration<Lsun/misc/Resource;>; +41 -1: ([Ljava/lang/String;[Ljava/lang/String;)V +30 -1: java/lang/Class$ReflectionData +8 -1: requests +52 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/US_ASCII$1;)V +12 -1: ACCESS_WRITE +6 -1: ibm920 +12 -1: CR_ERROR_MIN +6 -1: jarMap +6 -1: ibm923 +15 -1: java/lang/Error +11 -1: VM_SETTINGS +18 -1: name can't be null +7 -1: PRESENT +19 -1: setSecurityManager0 +101 -1: (Ljava/nio/channels/WritableByteChannel;Ljava/nio/charset/CharsetEncoder;I)Lsun/nio/cs/StreamEncoder; +25 -1: ()Ljava/util/jar/JarFile; +26 -1: java/io/ObjectOutputStream +13 -1: no !/ in spec +5 -1: (II)C +12 -1: setPriority0 +30 -1: (Z)[Ljava/lang/reflect/Method; +28 -1: sun/nio/cs/ThreadLocalCoders +5 -1: (II)I +22 -1: java/io/BufferedReader +26 -1: ()Lsun/misc/JavaNetAccess; +10 -1: Asia/Dhaka +14 -1: parallelStream +5 -1: (II)V +5 -1: (II)Z +31 -1: Ljava/lang/reflect/Constructor; +21 -1: ()Ljava/time/Instant; +31 -1: (Ljava/lang/String;III[J[I[IZ)V +8 -1: Volatile +23 -1: isUnicodeIdentifierPart +27 -1: longPrimitiveParameterCount +16 -1: Map is non-empty +24 -1: getLocalizedOutputStream +14 -1: java/lang/Byte +10 -1: staticBase +11 -1: lastElement +17 -1: replaceStaleEntry +17 -1: MAX_LOW_SURROGATE +28 -1: java.launcher.X.macosx.usage +20 -1: registerShutdownHook +16 -1: SECOND_IN_MILLIS +8 -1: Embedded +16 -1: BootstrapMethods +14 -1: numInvocations +79 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Enumeration<TT;>; +10 -1: rotateLeft +46 -1: ([Ljava/lang/Object;)Ljava/util/stream/Stream; +6 -1: verify +17 -1: OTHER_PUNCTUATION +26 -1: acquireConstructorAccessor +38 -1: (Ljava/lang/String;I)Ljava/lang/Class; +30 -1: java/net/UnknownContentHandler +20 -1: PREFIX_LENGTH_OFFSET +12 -1: nextGetIndex +14 -1: standardOffset +10 -1: entryNames +15 -1: application/xml +3 -1: BET +39 -1: ([DIII)Ljava/util/Spliterator$OfDouble; +83 -1: (JLjava/util/function/BiFunction;Ljava/util/function/BiFunction;)Ljava/lang/Object; +10 -1: initMethod +47 -1: (Ljava/util/LinkedList$Node;)Ljava/lang/Object; +8 -1: isSealed +12 -1: isAccessible +11 -1: audio/x-wav +46 -1: (Ljava/lang/String;)Ljava/util/jar/Attributes; +24 -1: ()Ljava/io/OutputStream; +15 -1: FIELD_MODIFIERS +30 -1: sun/misc/URLClassPath$Loader$1 +20 -1: recursive invocation +34 -1: (Ljava/lang/String;)Ljava/net/URL; +12 -1: linkNodeLast +34 -1: call site initialization exception +17 -1: casAnnotationType +8 -1: x-ibm874 +7 -1: isUpper +58 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesTask +31 -1: Ill-formed Unicode locale key: +12 -1: defineClass0 +12 -1: defineClass1 +12 -1: defineClass2 +59 -1: Can not call newInstance() on the Class for java.lang.Class +10 -1: codePoints +3 -1: ... +14 -1: readAheadLimit +14 -1: parallelSetAll +41 -1: ([Ljava/lang/Object;I)[Ljava/lang/Object; +148 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;[Ljava/lang/StackTraceElement;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set<Ljava/lang/Throwable;>;)V +10 -1: Deque.java +21 -1: Must be volatile type +7 -1: setForm +58 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Byte;>; +47 -1: (Ljava/lang/String;Ljava/security/CodeSource;)V +30 -1: java/io/InterruptedIOException +44 -1: java/util/Collections$SynchronizedCollection +16 -1: Invalid Jar file +32 -1: sun/util/calendar/CalendarSystem +67 -1: (JLsun/util/calendar/CalendarDate;)Lsun/util/calendar/CalendarDate; +19 -1: DEFAULT_BUFFER_SIZE +16 -1: readObjectNoData +16 -1: setJavaNioAccess +73 -1: (Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/Object;)Ljava/lang/Object; +14 -1: copyToIntArray +10 -1: hasWaiters +20 -1: (I)Ljava/lang/Class; +35 -1: all turn on all debugging +14 -1: Invalid host: +26 -1: Lsun/nio/cs/StreamEncoder; +43 -1: sun/misc/JavaSecurityProtectionDomainAccess +11 -1: getNamedCon +8 -1: H_SERVER +27 -1: java/util/function/Consumer +12 -1: isLocalClass +81 -1: (Ljava/util/LinkedHashMap$Entry<TK;TV;>;Ljava/util/LinkedHashMap$Entry<TK;TV;>;)V +83 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Boolean;>; +4 -1: exec +43 -1: java/lang/reflect/InvocationTargetException +35 -1: (Ljava/io/File;Ljava/lang/String;)V +9 -1: modifiers +35 -1: (Ljava/io/File;Ljava/lang/String;)Z +9 -1: Byte.java +12 -1: unknown mode +18 -1: initializeVerifier +24 -1: (Ljava/nio/ByteBuffer;)I +22 -1: ([Ljava/lang/Object;)I +11 -1: correctType +6 -1: escape +52 -1: (Ljava/security/ProtectionDomain;)Ljava/lang/String; +11 -1: annotations +121 -1: (Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName; +22 -1: using an instance of +14 -1: getSpeciesData +28 -1: ()Ljava/security/CodeSource; +12 -1: JZENTRY_NAME +24 -1: (Ljava/nio/ByteBuffer;)V +4 -1: ZBSC +22 -1: ([Ljava/lang/Object;)V +7 -1: isParam +165 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +70 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/LambdaForm; +24 -1: Ljava/io/FilePermission; +22 -1: ([Ljava/lang/Object;)Z +11 -1: mergeHeader +11 -1: applyAsLong +28 -1: (IJ)Ljava/lang/StringBuffer; +10 -1: arityCheck +52 -1: (ILjava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +13 -1: toUpperCaseEx +9 -1: nextIndex +11 -1: start > end +4 -1: long +6 -1: Static +27 -1: ()Ljava/lang/reflect/Field; +10 -1: bufUpdater +43 -1: averageBytesPerChar exceeds maxBytesPerChar +105 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/BaseLocale$1;)V +23 -1: ARRAY_SHORT_INDEX_SCALE +15 -1: getHeaderFields +36 -1: java/util/HashMap$HashMapSpliterator +10 -1: Guard.java +29 -1: java/util/RandomAccessSubList +6 -1: addAll +7 -1: getTime +16 -1: invokeHandleForm +32 -1: sun/util/locale/LocaleExtensions +16 -1: checkAndLoadMain +19 -1: INTERFACE_MODIFIERS +11 -1: resolveName +20 -1: getContentLengthLong +53 -1: (ICLjava/lang/Object;)Ljava/lang/invoke/MethodHandle; +19 -1: name cannot be null +23 -1: hasReceiverTypeDispatch +12 -1: getExtension +49 -1: Ljava/util/Set<Ljava/security/ProtectionDomain;>; +114 -1: (Ljava/lang/String;[J[I[J[I[Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)Lsun/util/calendar/ZoneInfo; +24 -1: NativeSignalHandler.java +58 -1: (Ljava/lang/Thread;)Ljava/lang/ThreadLocal$ThreadLocalMap; +10 -1: interface +68 -1: (Ljava/lang/String;)Ljava/lang/invoke/BoundMethodHandle$SpeciesData; +15 -1: addShutdownHook +32 -1: java/security/AccessController$1 +10 -1: filterTags +19 -1: [Ljava/lang/Number; +92 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToLongFunction<-TT;>;)Ljava/util/Comparator<TT;>; +43 -1: java/util/LinkedHashMap$LinkedEntryIterator +5 -1: utf-8 +11 -1: iso-8859-13 +11 -1: iso-8859-15 +58 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/util/jar/JarFile; +41 -1: CertPathValidator debugging +22 -1: ARRAY_LONG_BASE_OFFSET +57 -1: (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; +13 -1: queuePrintJob +14 -1: Watchable.java +15 -1: jdkMajorVersion +62 -1: (Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMXBean; +23 -1: twoToTheDoubleScaleDown +22 -1: registerVMNotification +30 -1: ()Lsun/misc/JavaUtilJarAccess; +17 -1: getTargetVolatile +4 -1: exit +13 -1: StringDecoder +12 -1: hasRemaining +9 -1: bigEndian +14 -1: checkMulticast +13 -1: clearProperty +18 -1: ForEachMappingTask +15 -1: Collection.java +55 -1: (Ljava/io/InputStream;)Ljava/security/cert/Certificate; +5 -1: UTF-8 +11 -1: transitions +7 -1: wrapAlt +27 -1: ClassNotFoundException.java +20 -1: UnresolvedPermission +14 -1: charsetForName +9 -1: getParent +18 -1: [Ljava/lang/Short; +24 -1: UnmodifiableNavigableMap +5 -1: xflow +10 -1: interfaces +19 -1: doubleToRawLongBits +73 -1: (Ljava/lang/reflect/Constructor<*>;[Ljava/lang/Object;)Ljava/lang/Object; +10 -1: getInCheck +21 -1: (Ljava/lang/Thread;)V +15 -1: fromIndex < 0: +63 -1: (ITK;TV;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V +9 -1: pollFirst +21 -1: (Ljava/lang/Thread;)Z +16 -1: checkProxyMethod +39 -1: generateLambdaFormInterpreterEntryPoint +15 -1: findLoadedClass +6 -1: system +5 -1: ITALY +45 -1: combiner SubjectDomainCombiner debugging +34 -1: NativeConstructorAccessorImpl.java +22 -1: ([C)Ljava/lang/String; +39 -1: (Ljava/lang/String;Ljava/lang/Class;Z)V +18 -1: java/lang/Thread$1 +20 -1: window can't be null +10 -1: Debug.java +17 -1: singletonIterator +53 -1: java/util/concurrent/ConcurrentHashMap$ForEachKeyTask +20 -1: java/security/Policy +13 -1: getDescriptor +32 -1: (I)Ljava/lang/invoke/MethodType; +15 -1: nativeLibraries +27 -1: sun/util/locale/LanguageTag +8 -1: priority +12 -1: IntegerCache +14 -1: connectTimeout +9 -1: namePairs +17 -1: vmAllowSuspension +16 -1: METHOD_MODIFIERS +51 -1: (Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +20 -1: MIN_TREEIFY_CAPACITY +13 -1: getEntryBytes +33 -1: ()Lsun/reflect/ReflectionFactory; +17 -1: getDisplayCountry +13 -1: isWrapperType +5 -1: utf16 +12 -1: parallelSort +27 -1: (Ljava/nio/ByteBuffer;IIZ)V +56 -1: (Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Class;I)Z +9 -1: isDefined +20 -1: sun/misc/FloatConsts +10 -1: putDoubleB +30 -1: java/lang/NoSuchFieldException +27 -1: Value out of range. Value:" +36 -1: sun/reflect/NativeMethodAccessorImpl +7 -1: decoder +38 -1: ([Ljava/lang/invoke/MutableCallSite;)V +10 -1: putDoubleL +68 -1: (Ljava/lang/reflect/Method;)Lsun/reflect/generics/scope/MethodScope; +37 -1: java/lang/invoke/MethodHandles$Lookup +9 -1: Void.java +28 -1: sun/util/locale/BaseLocale$1 +10 -1: stackTrace +7 -1: toClass +11 -1: access$1500 +41 -1: (Ljava/lang/Object;I)Ljava/lang/Class<*>; +148 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/Object;JLjava/lang/invoke/DirectMethodHandle$1;)V +22 -1: ARRAY_BYTE_BASE_OFFSET +13 -1: ZipEntry.java +56 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/util/List; +5 -1: utf32 +16 -1: ISO_646.irv:1991 +5 -1: p-126 +20 -1: sun.net.www.protocol +3 -1: key +20 -1: IMPLEMENTATION_TITLE +93 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Lsun/util/locale/LanguageTag; +66 -1: ([Ljava/lang/Object;[Ljava/lang/Object;IIILjava/util/Comparator;)V +27 -1: java/nio/DirectFloatBufferS +27 -1: java/nio/DirectFloatBufferU +15 -1: JZENTRY_COMMENT +8 -1: casTabAt +10 -1: getVariant +24 -1: Ljava/lang/Thread$State; +35 -1: ()Ljava/lang/AbstractStringBuilder; +114 -1: (Ljava/security/CodeSource;Ljava/security/PermissionCollection;Ljava/lang/ClassLoader;[Ljava/security/Principal;)V +16 -1: getShortVolatile +18 -1: SoftReference.java +3 -1: BST +12 -1: isCastableTo +28 -1: sun.zip.disableMemoryMapping +11 -1: copyOfRange +17 -1: ()Lsun/misc/Perf; +59 -1: (Ljava/lang/String;[Ljava/io/File;Ljava/lang/ClassLoader;)V +27 -1: (Lsun/misc/JavaAWTAccess;)V +8 -1: DECLARED +18 -1: loadedLibraryNames +6 -1: CENNAM +7 -1: encprop +5 -1: ABASE +27 -1: java/util/WeakHashMap$Entry +13 -1: wrapWithPrims +5 -1: UTF32 +29 -1: Ljava/net/URISyntaxException; +6 -1: groups +65 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<+TT;>;)Ljava/util/Set<TT;>; +32 -1: lambda$comparingByKey$6d558cbf$1 +15 -1: removeElementAt +49 -1: [Ljava/util/concurrent/ConcurrentHashMap$Segment; +32 -1: Sign character in wrong position +6 -1: IBM819 +39 -1: java/security/cert/CertificateException +4 -1: join +30 -1: Ljava/lang/invoke/ForceInline; +14 -1: expandCapacity +19 -1: Ljava/lang/Integer; +11 -1: NUMBER_THAI +10 -1: getExtURLs +9 -1: retainAll +21 -1: (S)Ljava/lang/String; +8 -1: truncate +51 -1: java/util/ArraysParallelSortHelpers$FJObject$Sorter +28 -1: newIndexOutOfBoundsException +26 -1: JavaUtilJarAccessImpl.java +22 -1: (II)Ljava/util/BitSet; +10 -1: getLongAt0 +65 -1: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA; +26 -1: (Ljava/lang/ThreadLocal;)I +5 -1: (J)[B +27 -1: Ljava/lang/CharacterData00; +18 -1: sun/misc/Cleaner$1 +59 -1: (Ljava/util/List;Ljava/lang/Object;Ljava/util/Comparator;)I +114 -1: (JLjava/util/function/ToDoubleFunction<Ljava/util/Map$Entry<TK;TV;>;>;DLjava/util/function/DoubleBinaryOperator;)D +28 -1: java/lang/ClassCastException +26 -1: (Ljava/lang/ThreadLocal;)V +27 -1: ()[Ljava/lang/reflect/Type; +13 -1: not invoker: +56 -1: (Ljava/net/URL;Ljava/net/Proxy;)Ljava/net/URLConnection; +6 -1: before +37 -1: ([DII)Ljava/util/stream/DoubleStream; +9 -1: logicalOr +9 -1: IS_METHOD +12 -1: SPACE_USABLE +12 -1: lastModified +10 -1: setSigners +8 -1: Invokers +7 -1: nCopies +12 -1: utf-32le-bom +7 -1: (IIII)J +17 -1: jdkSpecialVersion +26 -1: ()Ljava/lang/StringBuffer; +17 -1: SearchEntriesTask +14 -1: java/net/Parts +20 -1: Ljava/lang/Runnable; +35 -1: java/util/WeakHashMap$ValueIterator +19 -1: FinalReference.java +7 -1: (IIII)V +23 -1: Ljava/lang/ThreadGroup; +10 -1: nullsFirst +8 -1: setCache +55 -1: (Ljava/util/List;Ljava/lang/Object;Ljava/lang/Object;)Z +24 -1: java/util/SimpleTimeZone +6 -1: IBM850 +6 -1: IBM852 +25 -1: sun/net/www/MeteredStream +4 -1: exts +6 -1: IBM855 +16 -1: allocateElements +6 -1: IBM857 +19 -1: setDefaultUseCaches +6 -1: IBM858 +5 -1: slice +9 -1: marklimit +77 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/Void;>; +32 -1: java/util/Collections$CheckedSet +12 -1: getModifiers +8 -1: protocol +10 -1: getInteger +33 -1: ([J)Ljava/util/stream/LongStream; +6 -1: IBM862 +8 -1: Map.java +35 -1: java/lang/Class$EnclosingMethodInfo +25 -1: (J)Ljava/math/BigInteger; +31 -1: (Ljava/net/URL;Ljava/io/File;)V +6 -1: IBM866 +6 -1: unload +28 -1: sun/invoke/util/VerifyAccess +105 -1: ()Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>; +25 -1: Resetting to invalid mark +20 -1: java/util/Vector$Itr +5 -1: SHIFT +11 -1: NonfairSync +18 -1: getSecurityManager +34 -1: ()[Ljava/lang/ClassValue$Entry<*>; +28 -1: (J)Ljava/lang/StringBuilder; +28 -1: (Ljava/security/PublicKey;)V +12 -1: getResources +6 -1: IBM874 +27 -1: which Java does not define +36 -1: (Ljava/lang/invoke/MethodTypeForm;)V +48 -1: array length is not legal for long[] or double[] +18 -1: IS_FIELD_OR_METHOD +7 -1: Aliases +17 -1: checkedExceptions +13 -1: getDayOfMonth +51 -1: (Ljava/util/Spliterator;Z)Ljava/util/stream/Stream; +20 -1: java/io/EOFException +26 -1: Enclosing method not found +17 -1: flushLeftoverChar +122 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor<*>; +18 -1: buildAnnotatedType +21 -1: setContextClassLoader +22 -1: java/io/UnixFileSystem +20 -1: nonSyncContentEquals +43 -1: java/util/Collections$SynchronizedSortedMap +15 -1: Properties.java +35 -1: com.oracle.usagetracker.config.file +13 -1: java/util/Map +18 -1: setEagerValidation +13 -1: getSetMessage +6 -1: unlock +14 -1: refKindIsField +22 -1: bad field type alias: +17 -1: casAnnotationData +6 -1: AUGUST +106 -1: (Ljava/util/concurrent/CountedCompleter;[Ljava/lang/Object;[Ljava/lang/Object;IIIILjava/util/Comparator;)V +11 -1: monitorExit +17 -1: linkMethodTracing +69 -1: (Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/BaseLocale$1;)V +21 -1: java/lang/ClassLoader +39 -1: PKCS11 KeyStore debugging +10 -1: checkRtype +25 -1: getLocalGregorianCalendar +23 -1: GenericDeclaration.java +12 -1: isViewableAs +22 -1: static_oop_field_count +72 -1: (Ljava/util/function/ToDoubleFunction<-TT;>;)Ljava/util/Comparator<TT;>; +11 -1: languageKey +6 -1: Class +34 -1: java/util/HashMap$ValueSpliterator +37 -1: (IJ)Ljava/lang/AbstractStringBuilder; +17 -1: privilegedContext +36 -1: java/util/LinkedHashMap$LinkedValues +11 -1: getHostName +10 -1: beginEntry +7 -1: isAlpha +61 -1: (Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/LambdaForm; +10 -1: expandArgs +14 -1: Finalizer.java +14 -1: timeDefinition +28 -1: ()Ljava/util/jar/Attributes; +14 -1: ansi_x3.4-1968 +11 -1: setPriority +23 -1: (C)Ljava/lang/Class<*>; +26 -1: (Ljava/lang/Object;TV;)TV; +70 -1: (Ljava/util/function/BiFunction;Ljava/lang/Object;Ljava/lang/Object;)V +48 -1: ()Lsun/reflect/generics/factory/GenericsFactory; +25 -1: java/lang/invoke/CallSite +8 -1: tzdb.dat +17 -1: containsAllLimits +17 -1: fileNameMapLoaded +6 -1: values +17 -1: setLastAccessTime +12 -1: expandFromVM +50 -1: java/lang/invoke/MethodHandle$PolymorphicSignature +3 -1: .EC +14 -1: access denied +22 -1: java/util/AbstractList +47 -1: (IILjava/lang/String;)Ljava/lang/StringBuilder; +52 -1: ()Lsun/reflect/generics/repository/MethodRepository; +22 -1: (Ljava/lang/String;)[B +57 -1: (Ljava/lang/Object;)Ljava/lang/invoke/DirectMethodHandle; +18 -1: compareAndSwapLong +4 -1: != +6 -1: StdArg +29 -1: (Ljava/security/Permission;)V +22 -1: ([D)Ljava/lang/String; +28 -1: Lsun/reflect/MethodAccessor; +14 -1: ansi_x3.4-1986 +20 -1: getPeakFinalRefCount +29 -1: (Ljava/security/Permission;)Z +5 -1: debug +38 -1: (Ljava/lang/reflect/Constructor<*>;)[B +27 -1: java/util/GregorianCalendar +16 -1: Null replacement +26 -1: ()Ljava/lang/reflect/Type; +28 -1: DIRECTIONALITY_LEFT_TO_RIGHT +102 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/BaseLocale; +15 -1: isConvertibleTo +24 -1: ARRAY_DOUBLE_INDEX_SCALE +16 -1: getComponentType +29 -1: sun/util/locale/LocaleMatcher +11 -1: LOCALECACHE +6 -1: UNWRAP +16 -1: AbstractSet.java +3 -1: CAT +36 -1: java/lang/annotation/RetentionPolicy +14 -1: getParameters0 +8 -1: .Handler +33 -1: Ljava/lang/IllegalStateException; +10 -1: RAW_RETURN +20 -1: java/lang/ClassValue +16 -1: getDisplayString +152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;I)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +67 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/Map<TK;TV;>; +214 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +33 -1: java/lang/invoke/SerializedLambda +42 -1: ([Ljava/lang/Object;II)[Ljava/lang/Object; +22 -1: java/util/zip/ZipUtils +9 -1: setDaemon +26 -1: java/net/HttpURLConnection +6 -1: mkdirs +20 -1: (Ljava/io/Reader;I)V +28 -1: (IC)Ljava/lang/StringBuffer; +45 -1: ([Ljava/lang/Class<*>;I)[Ljava/lang/Class<*>; +29 -1: java/lang/invoke/MethodHandle +28 -1: sun/misc/CompoundEnumeration +6 -1: setVal +23 -1: INTERNED_ARGUMENT_LIMIT +4 -1: NULL +49 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class;)Z +43 -1: java/util/Collections$UnmodifiableSortedMap +39 -1: (Ljava/lang/Object;Ljava/lang/Object;)I +6 -1: ([JJ)I +19 -1: java/io/PrintWriter +25 -1: ()Ljava/lang/ThreadGroup; +5 -1: (IJ)J +16 -1: onMalformedInput +15 -1: decrementAndGet +11 -1: -2147483648 +6 -1: reduce +12 -1: asCharBuffer +39 -1: (Ljava/lang/Object;Ljava/lang/Object;)V +44 -1: (Ljava/util/SortedSet;)Ljava/util/SortedSet; +9 -1: backtrace +3 3: Bar +47 -1: ()Lsun/misc/JavaSecurityProtectionDomainAccess; +39 -1: (Ljava/lang/Object;Ljava/lang/Object;)Z +5 -1: (IJ)V +6 -1: ([JJ)V +22 -1: ([Ljava/lang/Thread;)I +5 -1: (IJ)Z +7 -1: ([BII)I +79 -1: <T:Ljava/lang/Object;>(Ljava/util/Comparator<-TT;>;)Ljava/util/Comparator<TT;>; +12 -1: getUnchecked +10 -1: getBaseURL +36 -1: (Ljava/lang/Object;)Ljava/util/List; +53 -1: (Ljava/util/function/Function;)Ljava/util/Comparator; +10 -1: getComment +7 -1: ([BII)V +30 -1: privateGetDeclaredConstructors +58 -1: (Ljava/lang/String;ZILjava/util/Locale;)Ljava/lang/String; +18 -1: unknown era name: +13 -1: invokeSpecial +9 -1: checkLink +16 -1: cspc8codepage437 +6 -1: stream +18 -1: sun/nio/cs/UTF_8$1 +18 -1: contextClassLoader +50 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)V +30 -1: sun/util/calendar/BaseCalendar +11 -1: enumeration +18 -1: key can't be empty +137 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU; +10 -1: getBoolean +5 -1: eetop +49 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/String; +43 -1: sun/reflect/generics/scope/ConstructorScope +13 -1: CANADA_FRENCH +39 -1: Ljava/nio/channels/ReadableByteChannel; +15 -1: java/lang/Float +29 -1: DIRECTIONALITY_OTHER_NEUTRALS +52 -1: (ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V +8 -1: appendTo +19 -1: PARAGRAPH_SEPARATOR +16 -1: (Unknown Source) +4 -1: tree +38 -1: (I[C)Ljava/lang/AbstractStringBuilder; +14 -1: VerifierStream +48 -1: (Ljava/util/Collection<TE;>;Ljava/lang/Object;)V +15 -1: releaseInflater +20 -1: getHeaderNamesInList +17 -1: getSystemPackages +8 -1: teardown +6 -1: (BZI)I +10 -1: checkWrite +19 -1: JavaLangAccess.java +31 -1: Ljava/lang/ClassValue$Identity; +50 -1: (Ljava/util/concurrent/CountedCompleter;[S[SIIII)V +24 -1: getDeclaredConstructors0 +3 -1: /.. +3 -1: /./ +16 -1: hashCodeForCache +18 -1: Property settings: +26 -1: Illegal initial capacity: +10 -1: text/plain +61 -1: (Ljava/util/function/ToDoubleFunction;)Ljava/util/Comparator; +24 -1: createMemoryManagerMBean +10 -1: ,lastRule= +9 -1: GMT-00:00 +5 -1: mtime +40 -1: (Ljava/lang/String;I)[Ljava/lang/String; +11 -1: (TT;TV;)TV; +154 -1: (Ljava/lang/Class<*>;Ljava/lang/String;[Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B[B)Ljava/lang/reflect/Method; +41 -1: (Ljava/util/jar/JarFile;)Ljava/util/List; +43 -1: (JILjava/lang/Object;)Ljava/nio/ByteBuffer; +19 -1: MethodTypeForm.java +21 -1: java/util/jar/JarFile +30 -1: java/lang/Integer$IntegerCache +22 -1: getDisplayVariantArray +6 -1: setAll +13 -1: ClassValueMap +52 -1: (Ljava/security/PublicKey;Ljava/security/Provider;)V +51 -1: java/util/concurrent/ConcurrentHashMap$BaseIterator +59 -1: (Ljava/lang/Runnable;Ljava/security/AccessControlContext;)V +100 -1: (Ljava/util/concurrent/ConcurrentMap;Ljava/util/function/BiFunction;)Ljava/util/function/BiConsumer; +8 -1: default +13 -1: compareAndSet +10 -1: iso8859-13 +9 -1: putShortB +14 -1: skipDelimiters +28 -1: URI has a fragment component +10 -1: iso8859-15 +42 -1: (Ljava/net/Proxy;)Ljava/net/URLConnection; +23 -1: needsPackageAccessCheck +9 -1: putShortL +3 -1: //[ +69 -1: (Ljava/security/AccessControlContext;Ljava/security/DomainCombiner;)V +18 -1: too many arguments +35 -1: ([III)Ljava/util/Spliterator$OfInt; +10 -1: CopiesList +10 -1: iso-8859-1 +9 -1: ([BII[C)I +10 -1: iso-8859-2 +11 -1: returnCount +10 -1: iso-8859-4 +10 -1: iso-8859-5 +8 -1: utf_16be +10 -1: iso-8859-7 +9 -1: isLimited +9 -1: parseByte +10 -1: iso-8859-9 +13 -1: , s.length() +10 -1: matchCerts +14 -1: RECURSIVE_CHAR +11 -1: reduceToInt +11 -1: displayName +9 -1: calendars +64 -1: (Ljava/lang/String;ZLjava/util/jar/JarEntry;)Lsun/misc/Resource; +11 -1: isProtected +78 -1: (Ljava/util/SortedMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/SortedMap; +4 -1: trim +20 -1: java/nio/FloatBuffer +17 -1: PreHashedMap.java +74 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/String;Ljava/lang/String;>; +22 -1: ([S)Ljava/lang/String; +19 -1: PrintStreamOrWriter +38 -1: java/util/Collections$EmptyEnumeration +22 -1: java/util/LinkedList$1 +13 -1: sunpkcs11.jar +25 -1: java/nio/DirectByteBuffer +96 -1: (ZLjava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +7 -1: isArray +43 -1: (Ljava/lang/String;)Ljava/util/Enumeration; +52 -1: java/lang/invoke/MethodHandleImpl$AsVarargsCollector +59 -1: (Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class<*>; +26 -1: setJavaNetHttpCookieAccess +15 -1: wrongTargetType +57 -1: java/util/concurrent/ConcurrentHashMap$ForEachMappingTask +33 -1: [Ljava/lang/reflect/TypeVariable; +5 -1: load0 +39 -1: (Ljava/lang/String;)Ljava/lang/Boolean; +21 -1: isHeldByCurrentThread +14 -1: outOfBoundsMsg +30 -1: Ljava/lang/ref/Reference$Lock; +11 -1: ISO-8859-13 +84 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;)Ljava/lang/ClassValue$Entry<TT;>; +11 -1: ISO-8859-15 +40 -1: (Ljava/net/URL;)Ljava/net/URLConnection; +84 -1: <T:Ljava/lang/Object;:Ljava/lang/Comparable<-TT;>;>(Ljava/util/Collection<+TT;>;)TT; +38 -1: sun/reflect/generics/scope/MethodScope +5 -1: mutex +11 -1: loaderTypes +8 -1: defaults +22 -1: getActualTypeArguments +41 -1: DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR +4 -1: keys +71 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +94 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/MethodHandle; +113 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/Set<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable; +12 -1: checkConnect +39 -1: (Ljava/lang/String;Ljava/util/Locale;)V +26 -1: ([CII[C)Ljava/lang/String; +12 -1: isDoubleWord +37 -1: configparser JAAS ConfigFile parsing +27 -1: sun/misc/Perf$GetPerfAction +44 -1: (Ljava/util/Collections$UnmodifiableList;I)V +4 -1: acos +26 -1: java/nio/DirectLongBufferS +7 -1: (ITE;)V +14 -1: putIntVolatile +24 -1: setContentHandlerFactory +26 -1: java/nio/DirectLongBufferU +10 -1: fieldCount +11 -1: invokeBasic +50 -1: (Ljava/util/zip/ZipEntry;)Ljava/util/jar/JarEntry; +24 -1: java/util/Locale$Builder +9 -1: setParent +11 -1: asLifoQueue +33 -1: lambda$comparingDouble$8dcf42ea$1 +24 -1: (Ljava/lang/Throwable;)I +35 -1: (Lsun/misc/JavaUtilZipFileAccess;)V +49 -1: (ILjava/lang/Object;)Ljava/util/HashMap$TreeNode; +10 -1: CLASS_PATH +6 -1: tclass +11 -1: getExponent +23 -1: getAnnotatedReturnType0 +18 -1: checkPackageAccess +35 -1: Can not instantiate java.lang.Class +24 -1: (Ljava/lang/Throwable;)V +195 -1: (Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/BoundMethodHandle$SpeciesData;Ljava/lang/invoke/BoundMethodHandle$SpeciesData;)Ljava/lang/invoke/LambdaForm; +17 -1: Empty replacement +3 -1: .SF +14 -1: ByteOrder.java +39 -1: ()Lsun/util/calendar/BaseCalendar$Date; +35 -1: ()[Ljava/security/ProtectionDomain; +12 -1: setElementAt +30 -1: (Ljava/security/CodeSource;Z)Z +45 -1: (Ljava/lang/Class<*>;)Ljava/lang/ClassLoader; +52 -1: (Ljava/nio/charset/Charset;)Ljava/util/zip/ZipCoder; +13 -1: foldArguments +23 -1: java/time/LocalDateTime +30 -1: [Lsun/launcher/LauncherHelper; +16 -1: 0123456789abcdef +60 -1: (Ljava/util/Spliterator$OfInt;Z)Ljava/util/stream/IntStream; +33 -1: (ILjava/lang/String;IIIIIIIIIII)V +20 -1: DMH.newInvokeSpecial +28 -1: java/nio/charset/CoderResult +33 -1: sun/nio/cs/StandardCharsets$Cache +11 -1: saveConvert +14 -1: ExtClassLoader +12 -1: parentOrNull +20 -1: insertParameterTypes +32 -1: (II)Ljava/util/stream/IntStream; +13 -1: setStackTrace +20 -1: is not an enum type +3 -1: CNT +4 -1: host +85 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;)Ljava/util/function/IntConsumer; +11 -1: batchRemove +8 -1: newField +16 5: sun/nio/cs/UTF_8 +104 -1: (Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)Ljava/lang/invoke/LambdaForm$Name; +8 -1: saturday +35 -1: java/util/ArraysParallelSortHelpers +15 -1: java/util/Queue +40 -1: (Ljava/lang/Class<*>;)Ljava/lang/String; +7 -1: toChars +5 -1: first +17 -1: ArrayDecoder.java +30 -1: ()Lsun/reflect/MethodAccessor; +26 -1: thread group can't be null +13 -1: IllegalName: +32 -1: java/util/Collections$SetFromMap +14 -1: line.separator +17 -1: getDeclaredMethod +10 -1: getMinutes +35 -1: (Lsun/util/locale/BaseLocale$Key;)I +40 -1: ([Ljava/lang/String;)Ljava/lang/Process; +31 -1: Ljava/util/LinkedHashMap$Entry; +13 -1: , str.length +8 -1: getProbe +6 -1: ([DI)I +5 -1: (CI)I +23 -1: saveAndRemoveProperties +6 -1: rehash +3 -1: lcb +31 -1: Ljava/util/Arrays$NaturalOrder; +55 -1: (IILjava/lang/String;)Ljava/lang/AbstractStringBuilder; +10 -1: loadFactor +15 -1: putLongVolatile +34 -1: sun/misc/URLClassPath$FileLoader$1 +12 -1: Europe/Paris +8 -1: DECEMBER +86 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/misc/Launcher$AppClassLoader;>; +22 -1: getImplMethodSignature +38 -1: Malformed enclosing method information +8 -1: maskNull +3 -1: lct +21 -1: CONSTRUCTOR_MODIFIERS +36 -1: ()Lsun/misc/JavaNetHttpCookieAccess; +12 -1: HashIterator +84 -1: (Ljava/lang/Class;Ljava/lang/Class$AnnotationData;Ljava/lang/Class$AnnotationData;)Z +33 -1: java/lang/Character$UnicodeScript +5 -1: toHex +27 -1: java/security/AllPermission +17 -1: appendReplacement +20 -1: SimpleImmutableEntry +18 -1: getRequestProperty +12 -1: compareCerts +44 -1: java/util/ArrayPrefixHelpers$IntCumulateTask +19 -1: makeSpreadArguments +222 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V +8 -1: addFirst +6 -1: nextUp +35 -1: (Ljava/net/ContentHandlerFactory;)V +40 -1: (Ljava/lang/String;)Ljava/lang/Class<*>; +23 -1: java/util/LocaleISOData +14 -1: PREPARED_FORMS +6 -1: FJByte +20 -1: getGenericSuperclass +6 -1: offset +16 -1: LocaleUtils.java +12 -1: isUnresolved +18 -1: aliases_ISO_8859_1 +18 -1: aliases_ISO_8859_2 +15 -1: isSurrogatePair +18 -1: aliases_ISO_8859_4 +18 -1: aliases_ISO_8859_5 +6 -1: EXTLEN +18 -1: aliases_ISO_8859_7 +15 -1: Comparator.java +18 -1: aliases_ISO_8859_9 +15 -1: ISO_8859-2:1987 +22 -1: Ljava/util/List<+TE;>; +16 -1: Unknown Category +3 -1: CST +51 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>; +6 -1: FRIDAY +40 -1: (Ljava/lang/String;ZZ)Ljava/lang/String; +13 -1: isInterrupted +8 -1: utf_16le +89 -1: (BLjava/lang/Class<*>;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle; +22 -1: checkInvocationCounter +12 -1: EPOCH_OFFSET +35 -1: (JJILjava/nio/DirectByteBuffer$1;)V +7 -1: canRead +9 -1: getLoader +18 -1: publicConstructors +23 -1: factory already defined +33 -1: java/lang/ref/ReferenceQueue$Null +37 -1: (Ljava/util/List;Ljava/util/Random;)V +25 -1: setPackageAssertionStatus +20 -1: MapReduceEntriesTask +11 -1: OPEN_DELETE +35 -1: (Ljava/util/Set;Ljava/lang/Class;)V +9 -1: rootGroup +10 -1: updateForm +22 -1: JavaUtilJarAccess.java +3 -1: CTT +57 -1: (Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Constructor; +82 -1: <T:Ljava/lang/Object;>(Ljava/util/NavigableSet<TT;>;)Ljava/util/NavigableSet<TT;>; +13 -1: getReturnType +34 -1: java/util/HashMap$EntrySpliterator +14 -1: TIME_UNDEFINED +32 -1: com/sun/crypto/provider/AESCrypt +7 -1: H_DIGIT +20 -1: clearAssertionStatus +44 -1: java/lang/invoke/MethodHandleImpl$BindCaller +8 -1: scloader +6 -1: IBM923 +5 -1: read0 +5 -1: read1 +4 -1: true +9 -1: BA_HIDDEN +16 -1: jvmUpdateVersion +36 -1: java/lang/StringCoding$StringDecoder +37 -1: (J)Ljava/nio/file/attribute/FileTime; +3 -1: lib +17 -1: getParameterTypes +15 -1: FinalizerThread +31 -1: ()Lsun/util/calendar/Gregorian; +50 -1: (Ljava/lang/CharSequence;)Ljava/lang/StringBuffer; +13 -1: PROP_SETTINGS +33 -1: java/util/function/BinaryOperator +70 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType; +25 -1: getDefaultRequestProperty +27 -1: (Ljava/util/jar/JarEntry;)V +27 -1: SPLITERATOR_CHARACTERISTICS +18 -1: FieldAccessor.java +10 -1: setComment +62 -1: (Ljava/lang/String;)Ljava/lang/management/MemoryManagerMXBean; +11 -1: array_klass +39 -1: ()Ljava/lang/Class$EnclosingMethodInfo; +67 -1: ([Ljava/lang/ClassValue$Entry<*>;ILjava/lang/ClassValue$Entry<*>;)I +9 -1: (II[CII)I +50 -1: (Ljava/util/jar/JarFile;Ljava/util/zip/ZipEntry;)V +20 -1: SPECIFICATION_VENDOR +72 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/net/URL;)Ljava/util/jar/JarFile; +87 -1: <T:Ljava/lang/Object;>(Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<TT;>;>;TT;)V +9 -1: isVarArgs +10 -1: setBoolean +12 -1: (TK;TV;TV;)Z +16 -1: findSharedClass0 +5 -1: csize +49 -1: Ljava/security/cert/CertificateEncodingException; +40 -1: java/util/concurrent/locks/ReentrantLock +86 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamEncoder; +5 -1: ready +38 -1: Ljava/security/AccessControlException; +28 -1: UnmodifiableRandomAccessList +69 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/function/BinaryOperator<TT;>;)V +27 -1: Ljava/lang/invoke/Invokers; +39 -1: java/util/LinkedList$DescendingIterator +11 -1: writeFields +17 -1: classLoaderDepth0 +18 -1: permutedTypesMatch +52 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/List; +12 -1: linkToStatic +10 -1: CheckedMap +6 -1: CENOFF +8 -1: lastRule +15 -1: java/lang/Short +39 -1: ()Ljava/lang/Class$ReflectionData<TT;>; +8 -1: nextDown +14 -1: image/x-pixmap +39 -1: (Ljava/lang/Class;[Ljava/lang/Object;)V +25 -1: defineClassSourceLocation +23 -1: sun/misc/PostVMInitHook +15 -1: could not load +16 -1: allowArraySyntax +90 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/io/BufferedInputStream;[B>; +10 -1: putBoolean +11 -1: has params +14 -1: setMaxPriority +10 -1: mayContain +46 -1: java/lang/reflect/MalformedParametersException +10 -1: baseLocale +14 -1: isSubwordOrInt +10 -1: nextDouble +32 -1: java/lang/Character$UnicodeBlock +85 -1: (JLjava/util/function/ToDoubleBiFunction;DLjava/util/function/DoubleBinaryOperator;)D +20 -1: numberOfLeadingZeros +59 -1: (I[Ljava/lang/Class<*>;)[Ljava/lang/invoke/LambdaForm$Name; +7 -1: setSize +29 -1: java/io/FileNotFoundException +9 -1: getString +24 -1: ([CII)Ljava/lang/String; +38 -1: (Ljava/lang/String;Ljava/lang/Class;)V +5 -1: shift +18 -1: getConstructorSlot +41 -1: java/lang/ThreadLocal$SuppliedThreadLocal +16 -1: UNASSIGNED_STACK +20 -1: Malformed class name +12 -1: ofEpochMilli +34 -1: sun/launcher/LauncherHelper$StdArg +33 -1: java/nio/ByteBufferAsShortBufferB +7 -1: convert +21 -1: ()[Ljava/util/Locale; +15 -1: ISO_8859-5:1988 +35 -1: av[0] not instace of MethodHandle: +33 -1: java/nio/ByteBufferAsShortBufferL +5 -1: hypot +16 -1: InputStream.java +13 -1: reinvokerForm +39 -1: JVMTI_THREAD_STATE_WAITING_INDEFINITELY +16 -1: sun/misc/Version +66 -1: <T::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TT;>;)[TT; +11 -1: codePointAt +30 -1: ([Ljava/lang/reflect/Method;)V +9 -1: duplicate +9 -1: interface +5 -1: X.509 +24 -1: SynchronizedNavigableSet +8 -1: us-ascii +17 -1: getUnresolvedType +21 -1: PRIVATE_USE_EXTENSION +4 -1: form +93 -1: (Ljava/util/ArrayPrefixHelpers$LongCumulateTask;Ljava/util/function/LongBinaryOperator;[JII)V +27 -1: sealing violation: package +34 -1: RuntimeVisibleParameterAnnotations +17 -1: LF_INVSTATIC_INIT +14 -1: Gregorian.java +32 -1: java/util/function/UnaryOperator +3 -1: log +3 -1: low +22 -1: sun/misc/JavaNetAccess +9 -1: getLength +21 -1: getRawTypeAnnotations +36 -1: (Ljava/lang/String;)Ljava/lang/Long; +9 -1: getNumber +66 -1: (ILjava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$Node; +20 -1: (Ljava/lang/Class;)C +89 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map$Entry<TK;TV;>; +6 -1: ENDSIG +20 -1: (Ljava/lang/Class;)I +20 -1: (Ljava/lang/Class;)J +24 -1: [[Ljava/io/Serializable; +22 -1: serialPersistentFields +7 -1: console +142 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +27 -1: (Ljava/nio/ByteBuffer;ICZ)V +20 -1: (Ljava/lang/Class;)V +40 -1: java/lang/ArrayIndexOutOfBoundsException +6 -1: this$0 +51 -1: (Ljava/lang/invoke/MemberName;[Ljava/lang/Object;)V +18 -1: packageAccessValid +33 -1: ([Ljava/lang/StackTraceElement;)V +8 -1: constant +113 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class<*>; +8 -1: isMethod +20 -1: (Ljava/lang/Class;)Z +6 -1: ENDSIZ +8 -1: newEntry +57 -1: (Ljava/lang/Object;)Ljava/lang/IndexOutOfBoundsException; +25 -1: (Ljava/util/Comparator;)V +8 -1: isBridge +6 -1: ([BI)I +6 -1: ([BI)J +16 -1: getReferenceKind +26 -1: [Ljava/security/Principal; +71 -1: (Ljava/lang/Class;[Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field; +32 -1: ()Ljava/lang/ClassValue$Version; +16 -1: SearchValuesTask +17 -1: setCompressedSize +16 -1: DEFAULT_CAPACITY +108 -1: <K:Ljava/lang/Object;V::Ljava/lang/Comparable<-TV;>;>()Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>; +27 -1: java/util/ComparableTimSort +41 -1: null StackTraceElement in serial stream. +6 -1: ([BI)V +44 -1: (Ljava/util/jar/JarFile;)Lsun/misc/JarIndex; +71 -1: (Ljava/util/jar/JarFile;Ljava/util/Enumeration;)Ljava/util/Enumeration; +52 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Z +36 -1: [Ljava/lang/reflect/TypeVariable<*>; +17 -1: OutputStream.java +8 -1: combiner +15 -1: decodeArrayLoop +19 -1: (Ljava/io/Writer;)V +41 -1: (Ljava/util/List<*>;Ljava/util/List<*>;)I +35 -1: ()[Ljava/security/cert/Certificate; +33 -1: ([I)Ljava/util/Spliterator$OfInt; +9 -1: NF_asType +17 -1: java/io/Closeable +11 -1: updateBytes +12 -1: charsets.jar +18 -1: getDeclaredFields0 +47 -1: (Ljava/lang/Object;I)Ljava/lang/reflect/Member; +60 -1: (Ljava/lang/String;ILjava/lang/String;)Ljava/nio/ByteBuffer; +15 -1: getTotalSeconds +57 -1: (Ljava/util/Collection<+Ljava/util/Map$Entry<TK;TV;>;>;)Z +4 -1: JULY +10 -1: Exceptions +41 -1: ()Ljava/util/List<Ljava/io/IOException;>; +14 -1: ParseUtil.java +13 -1: getJarFileURL +29 -1: setJavaIOFileDescriptorAccess +24 -1: ARRAY_OBJECT_BASE_OFFSET +21 -1: onUnmappableCharacter +53 -1: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map; +24 -1: MethodHandleNatives.java +40 -1: java/nio/charset/MalformedInputException +37 -1: [Ljava/lang/reflect/AnnotatedElement; +15 -1: CLASSPATH_CHARS +18 -1: [Ljava/lang/Class; +7 -1: FJFloat +47 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;I)V +19 -1: Ljava/lang/Runtime; +23 -1: java/lang/CharacterData +42 -1: (Ljava/lang/Void;Ljava/lang/ClassLoader;)V +76 -1: (Ljava/nio/channels/ReadableByteChannel;Ljava/nio/charset/CharsetDecoder;I)V +56 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;B)V +19 -1: java/util/zip/CRC32 +33 -1: <T:Ljava/lang/Object;>([TT;I)[TT; +22 -1: ([F)Ljava/lang/String; +5 -1: UTF_8 +20 -1: aliases_UTF_32BE_BOM +11 -1: Buffer.java +78 -1: <T:Ljava/lang/Object;>(Ljava/util/Comparator<TT;>;)Ljava/util/Comparator<TT;>; +44 -1: (Ljava/lang/String;)Ljava/util/zip/ZipEntry; +9 -1: malformed +4 -1: JUNE +51 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarFile$1;)V +6 -1: locale +34 -1: (Ljava/util/function/BiFunction;)V +10 -1: setMinutes +40 -1: (Ljava/lang/reflect/AccessibleObject;Z)V +12 -1: maybeCompile +46 -1: (Ljava/lang/Class;Ljava/lang/reflect/Method;)V +7 -1: getEras +55 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/Iterator<*>;)[TT; +17 -1: toUnsignedString0 +32 -1: (Ljava/lang/invoke/MethodType;)V +32 -1: (Ljava/lang/invoke/MethodType;)Z +10 -1: Class.java +27 -1: ()Ljava/util/Iterator<TK;>; +29 -1: WINDOWS_EPOCH_IN_MICROSECONDS +41 -1: (Ljava/io/InputStream;)Ljava/lang/String; +4 -1: prev +24 -1: ()Ljava/util/Properties; +11 -1: awaitBooted +19 -1: generateConstructor +22 -1: sun/misc/SharedSecrets +19 -1: getDateTimeInstance +43 -1: (IIILsun/util/calendar/BaseCalendar$Date;)J +5 -1: setID +11 -1: Locale.java +12 -1: getRootGroup +15 -1: setLastModified +7 -1: trouble +28 -1: (Z)Ljava/lang/StringBuilder; +5 -1: setIO +17 -1: loadClassInternal +23 -1: java/lang/ref/Finalizer +8 -1: EmptySet +16 -1: aliases_UTF_16BE +50 -1: (Ljava/util/NavigableMap;)Ljava/util/NavigableMap; +15 -1: unmodifiableMap +48 -1: (Ljava/lang/Class<*>;)Lsun/reflect/ConstantPool; +15 -1: arrayContentsEq +7 -1: EXT_TAG +31 -1: (Ljava/util/HashMap$TreeNode;)Z +5 -1: cp737 +22 -1: java/util/zip/Checksum +5 -1: names +22 -1: ConcurrentHashMap.java +7 -1: ([J[J)Z +7 -1: WAITING +31 -1: sun.launcher.resources.launcher +14 -1: getThreadGroup +8 -1: PutField +12 -1: hugeCapacity +9 -1: isPackage +72 -1: (Ljava/lang/ThreadLocal<*>;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +23 7: sun/nio/ch/DirectBuffer +13 -1: Checksum.java +25 -1: (Ljava/nio/ByteBuffer;I)C +51 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object; +25 -1: (Ljava/nio/ByteBuffer;I)D +7 -1: treeify +25 -1: (Ljava/nio/ByteBuffer;I)F +5 -1: setIn +25 -1: (Ljava/nio/ByteBuffer;I)I +20 -1: TRACE_METHOD_LINKAGE +25 -1: (Ljava/nio/ByteBuffer;I)J +7 -1: putIntB +22 -1: createGarbageCollector +50 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)[TT; +25 -1: (Ljava/nio/ByteBuffer;I)S +7 -1: putIntL +19 -1: (B)Ljava/lang/Byte; +14 -1: Hashtable.java +29 -1: java/lang/ArrayStoreException +11 -1: all_allowed +16 -1: getLastRawOffset +7 -1: inReady +36 -1: java/lang/ThreadLocal$ThreadLocalMap +40 -1: (ILjava/lang/String;Ljava/lang/String;)V +23 -1: Ljava/lang/ThreadLocal; +16 -1: classValueOrNull +62 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/MethodHandle; +23 -1: preparedFieldLambdaForm +22 -1: (Z)Ljava/lang/Boolean; +14 -1: ThreadLocalMap +27 -1: java/lang/StackTraceElement +13 -1: getEntryCSize +19 -1: java.security.debug +53 -1: (Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z +6 -1: LOCLEN +40 -1: Ljava/lang/Class<Ljava/lang/Character;>; +6 -1: (JJB)V +66 -1: Ljava/util/Hashtable<Ljava/lang/String;Ljava/net/ContentHandler;>; +31 -1: [[Ljava/lang/StackTraceElement; +9 -1: putStatic +16 -1: Asia/Ho_Chi_Minh +15 -1: getDisplayNames +13 -1: convertToAbbr +23 -1: Method not implemented. +15 -1: isCCLOverridden +14 -1: doubleCapacity +137 -1: (Ljava/lang/Class<*>;ZLjava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>; +219 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V +7 -1: native +29 -1: (Ljava/lang/reflect/Field;Z)V +18 -1: Ljava/util/Locale; +31 -1: Ljava/util/concurrent/TimeUnit; +16 -1: threadsSuspended +7 -1: ([III)V +20 -1: setMaxDelimCodePoint +18 -1: contentClassPrefix +13 -1: mappingOffset +10 -1: toIndex = +47 -1: (Ljava/lang/CharSequence;)Ljava/io/PrintStream; +12 -1: booleanValue +13 -1: putMapEntries +17 -1: defaultBundleName +50 -1: (Ljava/util/concurrent/CountedCompleter;[B[BIIII)V +10 -1: executable +20 -1: java/time/ZoneOffset +28 -1: java/lang/ref/FinalReference +11 -1: newTreeNode +7 -1: lookup2 +10 -1: TableStack +59 -1: Ljava/util/concurrent/ConcurrentHashMap$ValuesView<TK;TV;>; +11 -1: getAccessor +9 -1: available +18 -1: java/io/FileReader +34 -1: java/security/ProtectionDomain$3$1 +16 -1: integer overflow +11 -1: internTable +28 -1: Ljava/util/HashMap$TreeNode; +19 -1: | invocationCounter +12 -1: findResource +9 -1: isLoaded0 +5 -1: cp775 +24 -1: DIRECTIONALITY_UNDEFINED +9 -1: isInvalid +7 -1: lookupN +35 -1: (Lsun/reflect/MethodAccessorImpl;)V +6 -1: ENDSUB +4 -1: to +59 -1: ([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object; +10 -1: meta-index +6 -1: INDENT +9 -1: WEDNESDAY +40 -1: ()Ljava/lang/annotation/RetentionPolicy; +14 -1: getUsableSpace +7 -1: TUESDAY +51 -1: (Ljava/lang/Class;I)Ljava/lang/invoke/MethodHandle; +12 -1: getSubjectDN +21 -1: Ljava/io/InputStream; +25 -1: (IC)Ljava/nio/CharBuffer; +52 -1: (Ljava/nio/CharBuffer;)Ljava/util/function/Supplier; +17 -1: ()[Ljava/net/URL; +6 -1: search +10 -1: Main-Class +8 -1: ([CIIC)I +16 -1: Certificate.java +14 -1: spreadInvokers +22 -1: sun/nio/cs/ISO_8859_15 +6 -1: accept +18 -1: ReflectAccess.java +13 -1: java/nio/Bits +14 -1: linkToCallSite +46 -1: Ljava/nio/charset/UnsupportedCharsetException; +8 -1: ([CIIC)V +9 -1: (TT;TV;)V +26 -1: java/lang/OutOfMemoryError +34 -1: policy loading and granting +76 -1: (Ljava/nio/CharBuffer;ILjava/nio/ByteBuffer;I)Ljava/nio/charset/CoderResult; +13 -1: x-windows-949 +21 -1: Ljava/io/PrintStream; +9 -1: initNames +12 -1: testAnyFlags +65 -1: (Ljava/lang/reflect/Method;)Ljava/lang/invoke/DirectMethodHandle; +34 -1: (Ljava/util/List;)Ljava/util/List; +10 -1: CacheEntry +10 -1: hasAllPerm +26 -1: java/nio/charset/Charset$1 +26 -1: java/nio/charset/Charset$2 +19 -1: ()Ljava/util/Stack; +26 -1: java/nio/charset/Charset$3 +62 -1: (Ljava/lang/String;)Lsun/util/calendar/LocalGregorianCalendar; +23 -1: ARRAY_FLOAT_INDEX_SCALE +23 -1: (Ljava/lang/Object;IS)V +13 -1: x-windows-950 +31 -1: Ljava/util/Hashtable$Entry<**>; +87 -1: Ljava/util/WeakHashMap<Ljava/lang/ClassValue$Identity;Ljava/lang/ClassValue$Entry<*>;>; +9 -1: permClass +37 -1: (Ljava/security/ProtectionDomain$3;)V +99 -1: <S::Lsun/reflect/generics/tree/Signature;>Lsun/reflect/generics/repository/AbstractRepository<TS;>; +37 -1: ()Ljava/util/function/BinaryOperator; +64 -1: java/util/Collections$UnmodifiableNavigableMap$EmptyNavigableMap +91 -1: (Ljava/util/ArrayPrefixHelpers$IntCumulateTask;Ljava/util/function/IntBinaryOperator;[III)V +6 -1: getCrc +25 -1: ByteArrayInputStream.java +9 -1: SYNTHETIC +52 -1: Ljava/lang/ref/PhantomReference<Ljava/lang/Object;>; +246 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V +38 -1: java/lang/Throwable$WrappedPrintStream +21 -1: Illegal load factor: +43 -1: Ljava/util/Deque<Ljava/util/zip/Inflater;>; +3 -1: map +6 -1: expand +6 -1: access +3 -1: max +33 -1: impliesCreateAccessControlContext +3 -1: may +53 -1: java/util/concurrent/ConcurrentHashMap$ReduceKeysTask +91 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Lsun/misc/URLClassPath$Loader;>; +60 -1: attempt to add a Permission to a readonly Permissions object +21 -1: canonicalizeExtension +11 -1: copyValueOf +25 -1: (IJ)Ljava/nio/LongBuffer; +112 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU; +11 -1: DeqIterator +11 -1: SpeciesData +8 -1: getCause +16 -1: aliases_UTF_16LE +51 -1: (TT;TV;Ljava/util/function/BinaryOperator<TV;>;)TV; +25 -1: (JF)Ljava/nio/ByteBuffer; +16 -1: sun/misc/IOUtils +32 -1: Ljava/util/Locale$FilteringMode; +6 -1: .class +13 -1: getPermission +13 -1: startsWithLOC +8 -1: Identity +23 -1: ([BII)Ljava/lang/Class; +15 -1: putByteVolatile +36 -1: (Ljava/util/Deque;)Ljava/util/Queue; +22 -1: (Ljava/lang/Object;S)V +47 -1: java/util/concurrent/ConcurrentHashMap$BulkTask +4 -1: n = +9 -1: (ITE;)TE; +5 -1: zeroD +18 -1: formatUnsignedLong +29 -1: default display locale = +23 -1: java/io/File$PathStatus +5 -1: zeroF +20 -1: Ljava/util/Set<TK;>; +20 -1: (Ljava/util/List;I)V +5 -1: zeroI +5 -1: zeroJ +7 -1: context +39 -1: Ljava/nio/channels/WritableByteChannel; +5 -1: zeroL +34 -1: Lsun/util/calendar/CalendarSystem; +24 -1: JVMTI_THREAD_STATE_ALIVE +18 -1: (Ljava/util/Set;)V +18 -1: (Ljava/util/Set;)Z +42 -1: (TT;Ljava/lang/ref/ReferenceQueue<-TT;>;)V +7 -1: entries +30 -1: (Ljava/util/WeakHashMap;IIII)V +15 -1: csisolatingreek +38 -1: ([Ljava/lang/Class;)Ljava/lang/Object; +12 -1: isMalformed3 +12 -1: isMalformed4 +5 -1: FJInt +23 -1: java/util/LinkedHashMap +20 -1: malformedInputAction +12 -1: Charset.java +5 -1: LLL_L +42 -1: (Ljava/util/Collection;)Ljava/lang/Object; +22 -1: makeMethodHandleInvoke +3 -1: mdt +7 -1: unicode +12 -1: newInstance0 +10 -1: checkCerts +34 -1: java/util/WeakHashMap$HashIterator +23 -1: (Ljava/lang/Object;JI)I +9 -1: hexDigits +13 -1: javaToDosTime +24 -1: (I)Ljava/nio/LongBuffer; +6 -1: A_DATA +12 -1: deepToString +23 -1: (Ljava/lang/Object;JI)V +91 -1: (JLjava/util/function/ToLongBiFunction<-TK;-TV;>;JLjava/util/function/LongBinaryOperator;)J +23 -1: bad spread array length +11 -1: readTimeout +14 -1: toAbsolutePath +8 -1: isFinite +19 -1: currentLoadedClass0 +3 -1: \xef\xbf\xbd +23 -1: (Ljava/nio/file/Path;)I +8 -1: handlers +21 -1: (Ljava/util/List;II)V +89 -1: (Lsun/misc/URLClassPath$Loader;Ljava/lang/String;Ljava/net/URL;Ljava/net/URLConnection;)V +8 -1: OVERFLOW +8 -1: newTable +8 -1: THURSDAY +6 -1: notify +12 -1: initialValue +35 -1: (I)Ljava/util/LinkedList$Node<TE;>; +18 -1: AsVarargsCollector +26 -1: (Lsun/misc/JavaIOAccess;)V +18 -1: ()Ljava/lang/Void; +23 -1: (Ljava/nio/file/Path;)Z +16 -1: MINUTE_IN_MILLIS +67 -1: (Ljava/lang/Class;[Ljava/lang/Class;Z)Ljava/lang/invoke/MethodType; +18 -1: Ljava/util/Vector; +70 -1: (Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object; +40 -1: (Ljava/lang/Object;ILjava/lang/Object;)V +25 -1: UnresolvedPermission.java +14 -1: ReduceKeysTask +21 -1: ()[Ljava/lang/Object; +129 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;Ljava/io/Serializable; +16 -1: ClassLoader.java +46 -1: Ljava/util/Comparators$NaturalOrderComparator; +17 -1: compareAndSwapInt +22 -1: packageDefinitionValid +41 -1: ([Ljava/lang/Object;[Ljava/lang/Object;)Z +162 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;Ljava/util/Locale$FilteringMode;)Ljava/util/List<Ljava/lang/String;>; +16 -1: sun.zip.zipFiles +17 -1: java_runtime_name +31 -1: (Ljava/lang/ClassValue$Entry;)V +31 -1: (Ljava/lang/ClassValue$Entry;)Z +30 -1: <T:Ljava/lang/Object;>(TT;)TT; +39 -1: JavaSecurityProtectionDomainAccess.java +24 -1: (I)Ljava/lang/Throwable; +7 -1: FJShort +9 -1: putFloatB +19 -1: checkedNavigableSet +25 -1: java/lang/invoke/Invokers +18 -1: setIfModifiedSince +14 -1: parameterTypes +41 -1: (Ljava/lang/Object;Ljava/lang/Runnable;)V +9 -1: putFloatL +11 -1: getTypeCode +5 -1: (ZZ)I +24 -1: java/lang/ProcessBuilder +9 -1: UNDERFLOW +21 -1: VolatileCallSite.java +24 -1: (C)Ljava/nio/CharBuffer; +55 -1: java/util/concurrent/ConcurrentHashMap$ForEachValueTask +26 -1: (Ljava/lang/String;[CII)[B +18 -1: reduceKeysToDouble +5 -1: (ZZ)Z +23 -1: setCallSiteTargetNormal +3 -1: min +4 -1: ceil +62 -1: (Ljava/lang/String;)Ljava/util/LinkedList<Ljava/lang/String;>; +29 -1: (Ljava/util/AbstractList;II)V +32 -1: Ljava/lang/Class$AnnotationData; +21 -1: createFileExclusively +64 -1: (Ljava/lang/ref/SoftReference;I)Ljava/lang/Class$ReflectionData; +26 -1: java/lang/Short$ShortCache +54 -1: (Ljava/net/URL;Ljava/io/File;)Ljava/net/URLConnection; +29 -1: Lsun/nio/cs/Surrogate$Parser; +58 -1: (Ljava/lang/Class;)Lsun/reflect/annotation/AnnotationType; +8 -1: findForm +53 -1: Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet; +39 -1: (Lsun/misc/Perf;Ljava/nio/ByteBuffer;)V +16 -1: mergePermissions +11 -1: totalMemory +53 -1: java/lang/invoke/DirectMethodHandle$EnsureInitialized +139 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/concurrent/ConcurrentMap<TK;TV;>;Ljava/io/Serializable; +29 -1: java/util/HashMap$KeyIterator +20 -1: STACK_TRACE_SENTINEL +5 -1: order +18 -1: java/lang/Runnable +8 -1: GetField +13 -1: Empty command +7 -1: CONTROL +9 -1: blockedOn +12 -1: testAllFlags +11 -1: getInflater +16 -1: threadTerminated +44 -1: (Ljava/lang/ThreadGroup;Ljava/lang/String;)V +20 -1: java.runtime.version +8 -1: peekLast +23 -1: java/util/ArrayList$Itr +21 -1: (Ljava/util/Locale;)V +13 -1: isOptimizable +8 -1: FairSync +7 -1: CHINESE +15 -1: initHelpMessage +30 -1: ()Ljava/util/HashMap$TreeNode; +29 -1: Ljava/lang/SecurityException; +7 -1: charset +35 -1: sun/security/util/SecurityConstants +19 -1: sun.nio.cs.bugLevel +8 2: Foo.java +49 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;)V +12 -1: EntrySetView +37 -1: (Lsun/misc/JavaNetHttpCookieAccess;)V +35 -1: Ljava/util/Hashtable$Entry<TK;TV;>; +20 -1: NF_constructorMethod +8 -1: getMonth +38 -1: (Ljava/util/Iterator;Ljava/util/Map;)V +14 -1: getIntVolatile +6 -1: [name= +8 -1: oop_size +20 -1: Can't load library: +30 -1: ()Ljava/util/Spliterator<TV;>; +33 -1: Lsun/reflect/ConstructorAccessor; +61 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Integer;>; +15 -1: printVmSettings +33 -1: stack include stack trace +45 -1: ([Ljava/lang/Object;I)Ljava/util/Spliterator; +37 -1: sun/reflect/generics/scope/ClassScope +36 -1: java/io/UnsupportedEncodingException +24 -1: (J)Ljava/nio/LongBuffer; +11 -1: addressSize +15 -1: ByteBuffer.java +62 -1: (Ljava/lang/String;)Lsun/reflect/generics/tree/ClassSignature; +9 -1: (TT;TT;)I +25 -1: java/io/DefaultFileSystem +15 -1: BaseLocale.java +14 -1: BitSetIterator +17 -1: AbstractList.java +57 -1: Ljava/lang/ref/WeakReference<Ljava/lang/ThreadLocal<*>;>; +178 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +9 -1: arguments +26 -1: java/util/Locale$LocaleKey +9 -1: setLength +29 -1: sun/nio/cs/ISO_8859_1$Decoder +9 -1: zipfs.jar +24 -1: Ljava/util/zip/ZipCoder; +14 -1: , new state = +93 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIIJLjava/util/concurrent/ConcurrentHashMap;)V +39 -1: java/security/PrivilegedExceptionAction +9 -1: dnsns.jar +20 -1: iteratorBinarySearch +14 -1: initializePath +22 -1: DefaultFileSystem.java +17 -1: Ljava/util/Deque; +8 -1: DEFLATED +11 -1: Can't load +9 -1: ArrayList +21 -1: negativeZeroFloatBits +41 -1: (Ljava/lang/String;ILjava/util/Locale;)[C +14 -1: ANSI_X3.4-1968 +39 -1: sun/reflect/annotation/AnnotationType$1 +3 -1: mod +62 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/ByteBuffer;>; +29 -1: interpretWithArgumentsTracing +6 -1: getDay +47 -1: sun/reflect/generics/repository/ClassRepository +19 -1: refKindDoesDispatch +20 -1: getAnnotationsByType +14 -1: needsExpansion +18 -1: lastIndexOfSubList +26 -1: JavaUtilZipFileAccess.java +59 -1: (Ljava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder; +12 -1: ptypesOffset +8 -1: hashcode +18 -1: ([Ljava/net/URL;)V +8 -1: iso-ir-6 +7 -1: jzentry +52 -1: only dump output if specified codebase +31 -1: lambda$comparingLong$6043328a$1 +5 -1: MARCH +14 -1: ANSI_X3.4-1986 +14 -1: isMalformed3_2 +7 -1: IS_TYPE +68 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/nio/charset/Charset;>; +30 -1: protocol doesn't support input +17 -1: getExtClassLoader +14 -1: setProxiedHost +73 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>; +16 -1: traceInterpreter +17 -1: (Ljava/net/URL;)I +5 -1: expm1 +18 -1: createInheritedMap +66 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedEntryTask +17 -1: getTimeOfDayValue +15 -1: zeroLengthArray +20 -1: invalid permission: +6 -1: REPORT +15 -1: isNumericString +78 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter; +6 -1: (TV;)Z +25 -1: Lsun/misc/JavaLangAccess; +29 -1: (I)Ljava/lang/reflect/Method; +17 -1: (Ljava/net/URL;)V +34 -1: ()Ljava/lang/Class$ReflectionData; +50 -1: java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE +10 -1: copyWith: +17 -1: (Ljava/net/URL;)Z +32 -1: ()Ljava/util/stream/Stream<TE;>; +22 -1: quickCheckMemberAccess +29 -1: ()Lsun/net/www/MessageHeader; +19 -1: getAssignedCombiner +8 -1: ([JIIJ)I +17 -1: formatUnsignedInt +68 -1: <V:Ljava/lang/Object;>Ljava/util/AbstractMap<Ljava/lang/String;TV;>; +34 -1: java/nio/ByteBufferAsDoubleBufferB +32 -1: ([I)Ljava/util/stream/IntStream; +9 -1: init_lock +18 -1: must be resolved: +42 -1: ()Ljava/nio/channels/spi/SelectorProvider; +8 -1: ([JIIJ)V +33 -1: IncompatibleClassChangeError.java +34 -1: java/nio/ByteBufferAsDoubleBufferL +31 -1: ()Ljava/util/function/Function; +43 -1: Ljava/lang/Enum<Ljava/io/File$PathStatus;>; +17 -1: availableCharsets +49 -1: java/util/ArraysParallelSortHelpers$FJChar$Sorter +22 -1: permission=<classname> +22 -1: getAnnotatedSuperclass +20 -1: isObjectPublicMethod +15 -1: Attempt to get +10 -1: createLong +14 -1: HASH_INCREMENT +32 -1: sun/management/ManagementFactory +13 -1: separatorChar +15 -1: bad field type +8 -1: november +27 -1: (F)Ljava/lang/StringBuffer; +3 -1: EAT +3 -1: mst +54 -1: (Ljava/lang/reflect/Method;)Ljava/lang/reflect/Method; +18 -1: Ljava/lang/Object; +7 -1: ;:&=+$, +12 -1: Handler.java +7 -1: isDirty +127 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;[Ljava/security/Permission;)TT; +14 -1: asTypeUncached +5 -1: split +200 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/reflect/AnnotatedElement;Ljava/lang/Class;Ljava/lang/reflect/Type;Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget;)Ljava/lang/reflect/AnnotatedType; +47 -1: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +22 -1: sun/invoke/empty/Empty +66 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map; +18 -1: jvm_update_version +32 -1: (Ljava/util/Map;)Ljava/util/Map; +14 -1: cacheLoadLimit +8 -1: javaHome +52 -1: (Ljava/lang/reflect/Field;)Ljava/lang/reflect/Field; +20 -1: [[Ljava/lang/Object; +19 -1: isJavaLetterOrDigit +11 -1: loadLibrary +32 -1: java/io/StreamCorruptedException +14 -1: setAccessible0 +27 -1: sun/nio/cs/UTF_16LE$Encoder +60 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; +8 -1: segments +10 -1: UTF_8.java +3 -1: ECT +5 -1: cp813 +5 -1: cp819 +61 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/Comparator;)I +5 -1: Cache +4 -1: sinh +32 -1: java/util/function/ToIntFunction +10 -1: setFactory +24 -1: Illegal mappings count: +16 -1: fileToEncodedURL +38 -1: Ljava/lang/annotation/RetentionPolicy; +27 -1: Ljava/net/SocketPermission; +46 -1: (Ljava/lang/CharSequence;I)[Ljava/lang/String; +11 -1: cardinality +13 -1: getMonthValue +64 -1: (Ljava/lang/invoke/MethodType;II)Ljava/lang/invoke/MethodHandle; +6 -1: ENDTOT +12 -1: getBytesUTF8 +9 -1: cacheLoad +13 -1: packageAccess +14 -1: sharedToString +5 -1: merge +29 -1: parameter type cannot be void +27 -1: makePreparedFieldLambdaForm +40 -1: Couldn't find 3-letter country code for +166 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/net/URL;Ljava/lang/ClassLoader;)V +19 -1: (Ljava/util/Map;Z)V +13 -1: setExecutable +17 -1: objectFieldOffset +57 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +128 -1: (Ljava/lang/Class<*>;ZLjava/lang/String;Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>; +61 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToIntTask +6 -1: asType +25 -1: java/io/ObjectStreamField +15 -1: jvmMajorVersion +124 -1: (Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object; +23 -1: (Ljava/lang/Class<*>;)C +6 -1: andNot +15 -1: getResponseCode +59 -1: (Ljava/lang/StringBuffer;)Ljava/lang/AbstractStringBuilder; +23 -1: (Ljava/lang/Class<*>;)I +7 -1: seeAllp +44 -1: (Ljava/lang/ClassLoader;[Ljava/lang/Class;)V +13 -1: loadFromCache +35 -1: sun/nio/cs/HistoricallyNamedCharset +38 -1: (Ljava/lang/Class;[Ljava/lang/Class;)V +19 -1: INVOKER_METHOD_TYPE +16 -1: putShortVolatile +12 -1: Asia/Karachi +8 -1: cyrillic +12 -1: getISO2Table +23 -1: (Ljava/lang/Class<*>;)V +3 -1: 1.4 +15 -1: LongBuffer.java +6 -1: (IFZ)V +23 -1: (Ljava/lang/Class<*>;)Z +10 -1: initOutput +9 -1: CELLSBUSY +39 -1: java/security/PrivilegedActionException +31 -1: sun/util/calendar/CalendarUtils +202 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/reflect/AnnotatedElement;Ljava/lang/Class;[Ljava/lang/reflect/Type;Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget;)[Ljava/lang/reflect/AnnotatedType; +20 -1: Ljava/lang/Class<*>; +5 -1: cp850 +25 -1: (JI)Ljava/nio/ByteBuffer; +5 -1: cp852 +24 -1: Invalid parameter name " +39 -1: ([CII)Ljava/lang/AbstractStringBuilder; +5 -1: cp855 +11 -1: Deallocator +5 -1: cp857 +5 -1: cp858 +7 -1: ([SI)[S +37 -1: ([C)Ljava/lang/AbstractStringBuilder; +27 -1: java/lang/SecurityException +82 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)V +38 -1: (Ljava/lang/String;)Ljava/lang/String; +7 -1: connect +7 -1: isEmpty +11 -1: replaceNode +19 -1: SuppliedThreadLocal +12 -1: asFixedArity +12 -1: fromIndex = +19 -1: createMemoryManager +9 -1: List.java +8 -1: FEBRUARY +21 -1: UnicodeLittleUnmarked +6 -1: a null +30 -1: ()Ljava/util/Spliterator<TT;>; +5 -1: cp862 +17 -1: ZoneInfoFile.java +5 -1: cp866 +8 -1: BulkTask +53 -1: java/util/concurrent/locks/AbstractQueuedSynchronizer +20 -1: FileInputStream.java +12 -1: java.vm.info +10 -1: newDecoder +5 -1: (JB)V +8 -1: filePath +17 -1: spreadArrayChecks +44 -1: ([Ljava/lang/Object;Ljava/util/Comparator;)V +33 -1: java/util/Collections$AsLIFOQueue +32 -1: Ljava/util/LinkedList$Node<TE;>; +5 -1: cp874 +78 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; +39 -1: (JLjava/util/function/Consumer<-TK;>;)V +15 -1: appendCodePoint +20 -1: primitiveReturnCount +54 -1: only dump output if specified permission +20 -1: getGenericInterfaces +41 -1: ([Ljava/lang/reflect/AccessibleObject;Z)V +17 -1: nUnstartedThreads +33 -1: (Ljava/lang/invoke/MemberName;Z)V +24 -1: ARRAY_OBJECT_INDEX_SCALE +40 -1: (Ljava/lang/String;ILjava/util/Locale;)I +17 -1: java/io/Flushable +22 -1: newConstructorAccessor +26 -1: sun/misc/JavaUtilJarAccess +6 -1: booted +10 -1: setDoInput +36 -1: (Ljava/lang/Class;)[Ljava/lang/Enum; +19 -1: java/lang/Character +52 -1: ([Ljava/net/URL;Ljava/net/URLStreamHandlerFactory;)V +24 -1: (Ljava/nio/LongBuffer;)I +16 -1: start > length() +28 -1: (I)Ljava/lang/CharacterData; +5 -1: val$c +61 -1: (Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V +13 -1: resolveOrNull +9 -1: L_ESCAPED +27 -1: MapReduceValuesToDoubleTask +15 -1: getPreparedForm +33 -1: (I)[Ljava/util/WeakHashMap$Entry; +54 -1: ()Ljava/util/stream/Stream<+Ljava/util/zip/ZipEntry;>; +16 -1: bad method type +5 -1: val$s +17 -1: Null charset name +36 -1: java/lang/invoke/LambdaForm$Compiled +24 -1: (Ljava/util/SortedMap;)V +19 -1: java/time/LocalTime +29 -1: not invocable, no method type +21 -1: recalculateWordsInUse +6 -1: val$id +39 -1: sun/security/util/ManifestEntryVerifier +60 -1: ([Ljava/lang/Class<*>;I)Ljava/lang/reflect/Constructor<TT;>; +45 -1: java/util/ArrayPrefixHelpers$LongCumulateTask +5 -1: OfInt +11 -1: environment +60 -1: ([Ljava/lang/Class<*>;[B)[[Ljava/lang/annotation/Annotation; +7 -1: (JJJZ)V +10 -1: BufferPool +6 -1: isUTF8 +12 -1: threadLocals +35 -1: (Ljava/lang/String;)[Ljava/net/URL; +21 -1: Ljava/nio/LongBuffer; +15 -1: copyConstructor +25 -1: setCallSiteTargetVolatile +15 -1: getNumericValue +26 -1: Ljava/security/CodeSource; +18 -1: Null output stream +14 -1: cloneWithIndex +23 -1: LOCAL_LISTEN_PERMISSION +6 -1: (TT;)I +46 -1: (Ljava/security/PublicKey;Ljava/lang/String;)V +6 -1: setCrc +26 -1: java/io/FilterOutputStream +10 -1: access$000 +10 -1: access$001 +10 -1: access$002 +6 -1: (TT;)V +78 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>([TU;IILjava/lang/Class<+[TT;>;)[TT; +41 -1: java/util/concurrent/atomic/AtomicInteger +8 -1: renameTo +40 -1: (Ljava/lang/Class<*>;)Ljava/lang/Object; +17 -1: getRawAnnotations +29 -1: java/lang/VirtualMachineError +37 -1: java/lang/management/MemoryPoolMXBean +25 -1: (II)Ljava/util/List<TE;>; +6 -1: utf_16 +23 -1: (Ljava/lang/String;[B)V +19 -1: MIN_ARRAY_SORT_GRAN +25 -1: array length is not legal +45 -1: java/util/concurrent/locks/ReentrantLock$Sync +36 -1: Ljava/security/AccessControlContext; +48 -1: sun/reflect/generics/repository/MethodRepository +24 -1: MethodHandleStatics.java +24 -1: addThreadDumpForMonitors +64 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;)Ljava/util/Set<TT;>; +58 -1: (Ljava/lang/String;[Ljava/lang/Object;Ljava/lang/Object;)Z +37 -1: (III)Lsun/util/calendar/CalendarDate; +9 -1: createURI +15 -1: unreserveMemory +52 -1: (Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Method; +66 -1: (Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +51 -1: (Ljava/lang/reflect/Constructor;)Ljava/lang/String; +23 -1: inheritableThreadLocals +63 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>; +16 -1: setContentLength +16 -1: LOWERCASE_LETTER +4 -1: size +25 -1: java.launcher.opt.hotspot +19 -1: buildAnnotatedTypes +26 -1: JAVAFX_LAUNCHER_CLASS_NAME +11 -1: getAliasMap +19 -1: CheckedNavigableSet +15 -1: getAbsolutePath +11 -1: doubleValue +6 -1: utf_32 +22 -1: IMPLEMENTATION_VERSION +3 -1: ne1 +11 -1: contentType +8 -1: canWrite +11 -1: Object.java +14 -1: America/Denver +8 -1: fileName +13 -1: allPermDomain +27 -1: ()Ljava/util/Iterator<TE;>; +31 -1: (Lsun/reflect/MethodAccessor;)V +11 -1: asTypeCache +13 -1: lineSeparator +9 -1: JarLoader +15 -1: replacementNode +18 -1: getContentEncoding +12 -1: invoke_LLL_L +22 -1: ()Ljava/util/TimeZone; +17 -1: Reference Handler +33 -1: java/lang/invoke/MethodHandleImpl +47 -1: ()Ljava/util/concurrent/ConcurrentHashMap$Node; +12 -1: invoke_LLL_V +8 -1: form << +23 -1: (Ljava/lang/Object;JJ)J +15 -1: isHighSurrogate +31 -1: (Ljava/util/Collection<+TV;>;)Z +36 -1: ([Ljava/util/HashMap$Node<TK;TV;>;)V +23 -1: (Ljava/lang/Object;JJ)V +12 -1: utf_32be_bom +40 -1: sun/util/calendar/LocalGregorianCalendar +27 -1: [Ljava/security/CodeSigner; +15 -1: afterNodeAccess +13 -1: nextThreadNum +18 -1: INTERNED_ARGUMENTS +11 -1: getMillisOf +18 -1: offsetByCodePoints +11 -1: writeObject +48 -1: (Ljava/util/Locale$Category;Ljava/util/Locale;)V +3 -1: nfe +41 -1: (Ljava/util/Properties;Ljava/io/Reader;)V +50 -1: (Ljava/util/concurrent/CountedCompleter;[C[CIIII)V +15 -1: implFlushBuffer +33 -1: (I)[Ljava/lang/invoke/MemberName; +12 -1: Unicode.java +17 -1: DMH.invokeVirtual +5 -1: setup +51 -1: (Ljava/util/Collection;[Ljava/lang/reflect/Field;)V +3 -1: EST +7 -1: TREEBIN +7 -1: getFile +10 -1: isLeapYear +18 -1: LinkedHashIterator +14 -1: DISPLAY_SCRIPT +25 -1: privateGetDeclaredMethods +68 -1: (Ljava/util/function/Function;Ljava/lang/Object;Ljava/lang/Object;)I +22 -1: ()Ljava/util/Iterator; +21 -1: sun/management/Sensor +15 -1: getAvailableIDs +51 -1: Lsun/util/PreHashedMap<Ljava/nio/charset/Charset;>; +8 -1: elot_928 +6 -1: LATIN0 +45 -1: ([Ljava/lang/Object;II[Ljava/lang/Object;II)V +10 -1: [Unlocked] +15 -1: internArguments +6 -1: LATIN9 +33 -1: (II)Ljava/lang/invoke/MethodType; +12 -1: Version.java +17 -1: setConnectTimeout +75 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; +13 -1: highSurrogate +12 -1: Africa/Cairo +21 -1: synchronizedSortedMap +21 -1: in java.library.path +45 -1: sun/reflect/generics/tree/FormalTypeParameter +24 -1: UncaughtExceptionHandler +14 -1: previousOrSame +24 -1: java/security/Permission +9 -1: x-ISCII91 +5 -1: L_HEX +35 -1: java/lang/invoke/DirectMethodHandle +35 -1: java/util/ArrayDeque$DeqSpliterator +14 -1: java/util/List +11 -1: toLowerCase +24 -1: java/nio/charset/Charset +10 -1: MIN_NORMAL +110 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +13 -1: regionMatches +17 -1: newMethodAccessor +26 -1: (Ljava/net/InetAddress;B)V +68 -1: (Ljava/util/zip/ZipFile;Ljava/lang/String;J)Ljava/util/zip/ZipEntry; +22 -1: ()Ljava/io/FileSystem; +19 -1: primitiveSimpleName +5 -1: MOVED +9 -1: STATE_RED +13 -1: linkToSpecial +19 -1: AnnotationType.java +20 -1: (II)Ljava/util/List; +252 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToDoubleTask;Ljava/util/function/ToDoubleBiFunction;DLjava/util/function/DoubleBinaryOperator;)V +12 -1: callSiteForm +22 -1: isSiblingBindingBefore +62 -1: <T:Ljava/lang/Object;>([TT;IITT;Ljava/util/Comparator<-TT;>;)I +15 -1: buildEmptyNames +11 -1: Thread.java +30 -1: Ljava/lang/ref/Reference<TT;>; +35 -1: sun/reflect/MethodAccessorGenerator +152 -1: (Ljava/util/function/Function;Ljava/util/function/Function;Ljava/util/function/BinaryOperator;Ljava/util/function/Supplier;)Ljava/util/stream/Collector; +5 -1: total +242 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V +27 -1: javax/security/auth/Subject +43 -1: JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER +17 -1: getSignerCertPath +15 -1: registerNatives +21 -1: sun/reflect/FieldInfo +54 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/ISO_8859_1$1;)V +17 -1: unwrapWithNoPrims +17 -1: instanceof Long: +20 -1: hasRealParameterData +23 -1: ()Ljava/time/LocalTime; +14 -1: getAnnotations +8 -1: optimize +7 -1: setChar +11 -1: OFFSET_MASK +4 -1: TYPE +177 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiFunction;Ljava/util/concurrent/atomic/AtomicReference;)V +18 -1: removeShutdownHook +27 -1: ()Ljava/security/Principal; +29 -1: JAVAFX_APPLICATION_CLASS_NAME +6 -1: digits +37 -1: [Ljava/lang/reflect/Constructor<TT;>; +45 -1: ()Ljava/lang/Thread$UncaughtExceptionHandler; +7 -1: tryLock +19 -1: java/net/Proxy$Type +21 -1: setJavaSecurityAccess +13 -1: tieBreakOrder +3 -1: no +16 -1: Australia/Sydney +13 -1: DAY_IN_MILLIS +19 -1: ()Ljava/nio/Buffer; +12 -1: Integer.java +14 -1: isBmpCodePoint +6 -1: daemon +23 -1: Lsun/misc/JavaIOAccess; +106 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;Ljava/util/function/Consumer<-TU;>;)V +10 -1: getFloatAt +15 -1: content/unknown +52 -1: ()Ljava/util/Enumeration<+Ljava/util/zip/ZipEntry;>; +123 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z +4 -1: nsme +12 -1: prefixLength +9 -1: flagsMods +95 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MemberName; +62 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/LambdaForm; +16 -1: Illegal mode: 0x +24 -1: java/io/FileOutputStream +41 -1: [Pp][Ee][Rr][Mm][Ii][Ss][Ss][Ii][Oo][Nn]= +24 -1: java/security/CodeSource +16 -1: DUMP_CLASS_FILES +25 -1: ([C)Ljava/nio/CharBuffer; +12 -1: bindArgument +50 -1: Ljava/lang/ref/FinalReference<Ljava/lang/Object;>; +21 -1: unmodifiableSortedMap +10 -1: jarHandler +73 -1: (Ljava/lang/Class;[Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method; +67 -1: ()Ljava/util/Map<Ljava/lang/Thread;[Ljava/lang/StackTraceElement;>; +15 -1: threadSeqNumber +18 -1: AutoCloseable.java +9 -1: holdsLock +25 -1: (Ljava/lang/Object;JJJJ)V +7 -1: (IJII)I +15 -1: copyToLongArray +58 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Package;>; +84 -1: (Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +32 -1: getExecutableTypeAnnotationBytes +17 -1: streamHandlerLock +35 -1: java/lang/IndexOutOfBoundsException +15 -1: moveRootToFront +28 -1: ()Ljava/nio/file/FileSystem; +14 -1: content-length +61 -1: (Ljava/lang/invoke/CallSite;Ljava/lang/invoke/MethodHandle;)V +7 -1: csASCII +18 -1: staticIsConsistent +21 -1: sharedToGenericString +8 -1: linkLast +21 -1: isUnicodeExtensionKey +7 -1: readInt +7 -1: compile +32 -1: ()Ljava/lang/reflect/Executable; +4 -1: Big5 +20 -1: Ljava/util/Set<TE;>; +18 -1: ExpiringCache.java +44 -1: (Ljava/lang/String;[BII)Ljava/lang/Class<*>; +18 -1: LinkedHashMap.java +32 -1: Ljava/lang/UnsatisfiedLinkError; +13 -1: parameterType +28 -1: (ID)Ljava/lang/StringBuffer; +15 -1: synchronizedSet +9 -1: implClose +6 -1: member +14 -1: MH_INVOKE_MODS +21 -1: forOutputStreamWriter +37 -1: Lsun/misc/JavaIOFileDescriptorAccess; +28 -1: java/lang/ProcessEnvironment +17 -1: setNormalizedYear +14 -1: isMalformed4_2 +14 -1: isMalformed4_3 +38 -1: (Ljava/lang/Object;)Ljava/lang/String; +16 -1: getJavaAWTAccess +12 -1: isPrivileged +30 -1: java/util/Collections$EmptyMap +17 -1: LinkedKeyIterator +7 -1: vmcount +27 -1: java/lang/ref/WeakReference +5 -1: march +13 -1: addOldMapping +58 -1: (Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner; +56 -1: (ILjava/lang/String;)[Ljava/lang/invoke/LambdaForm$Name; +65 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToLongTask +55 -1: (Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; +17 -1: getEnclosingClass +35 -1: (I)Lsun/util/calendar/BaseCalendar; +13 -1: binarySearch0 +25 -1: ([J)Ljava/nio/LongBuffer; +19 -1: java/util/Map$Entry +22 -1: java/util/HashMap$Node +26 -1: sun/reflect/MethodAccessor +8 -1: LASTYEAR +7 -1: disable +36 -1: sun/launcher/LauncherHelper$FXHelper +6 -1: toPath +10 -1: shortValue +6 -1: remove +59 -1: ([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process; +55 -1: java/util/concurrent/ConcurrentHashMap$ValueSpliterator +64 -1: (Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/Collection; +15 -1: asPrimitiveType +16 -1: PrintStream.java +10 -1: image/jpeg +22 -1: specificToStringHeader +7 -1: class " +38 -1: java/util/Collections$CheckedSortedMap +19 -1: SUPPRESSED_SENTINEL +16 -1: getEnumConstants +54 -1: (ILjava/lang/CharSequence;II)Ljava/lang/StringBuilder; +36 -1: Ljava/lang/Class<Ljava/lang/Short;>; +13 -1: toThreadState +38 -1: (Ljava/lang/Class;)Ljava/lang/Package; +24 -1: (C)Ljava/lang/Character; +19 -1: UNTREEIFY_THRESHOLD +4 -1: NCPU +23 -1: ()Ljava/lang/Exception; +42 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)V +15 -1: nothingToVerify +15 -1: setInitialValue +15 -1: getTimeInMillis +12 -1: getDoubleAt0 +18 -1: parameterTypeCache +86 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;)Ljava/nio/file/WatchKey; +49 -1: java/util/concurrent/ConcurrentHashMap$ValuesView +13 -1: <all actions> +7 -1: exitVM. +34 -1: Ljava/lang/ClassNotFoundException; +68 -1: (Ljava/util/Map;Ljava/lang/Class;)[Ljava/lang/annotation/Annotation; +28 -1: getCalendarDateFromFixedDate +28 -1: UnsafeFieldAccessorImpl.java +27 -1: java/lang/RuntimePermission +74 -1: Ljava/lang/Object;Ljava/lang/Comparable<Lsun/util/locale/BaseLocale$Key;>; +62 -1: ()Ljava/util/Iterator<Ljava/nio/charset/spi/CharsetProvider;>; +13 -1: LanguageRange +239 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V +37 -1: DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE +62 -1: (Ljava/lang/invoke/MethodType;II)Ljava/lang/invoke/MethodType; +16 -1: DMH.invokeStatic +11 -1: (TK;TV;)TV; +7 -1: ([FI)[F +14 -1: newPerfCounter +35 -1: ([JI)Ljava/util/Spliterator$OfLong; +30 -1: java/util/AbstractList$ListItr +5 -1: cp912 +5 -1: cp914 +45 -1: (Ljava/io/BufferedWriter;Ljava/lang/String;)V +6 -1: LOCNAM +8 -1: launcher +5 -1: cp915 +14 -1: standardString +26 -1: ()Ljava/lang/Thread$State; +10 -1: L_ALPHANUM +8 -1: (C[CII)I +9 -1: SEPTEMBER +20 -1: java/text/DateFormat +38 -1: Ljava/lang/CloneNotSupportedException; +5 -1: cp920 +23 -1: getConstructorSignature +16 -1: ReferenceHandler +19 -1: America/Puerto_Rico +5 -1: cp923 +10 -1: typeString +30 -1: Self-suppression not permitted +25 -1: (Ljava/io/OutputStream;)V +9 -1: implReset +12 -1: fullAddCount +34 -1: java/lang/invoke/LambdaForm$Hidden +25 -1: ()Lsun/misc/JavaIOAccess; +9 -1: Math.java +9 -1: getAndSet +7 -1: failure +14 -1: LINE_SEPARATOR +6 -1: parent +30 -1: java/lang/BootstrapMethodError +8 -1: indexMap +9 -1: ALL_KINDS +23 -1: desiredAssertionStatus0 +39 -1: (ILjava/lang/Object;)Ljava/lang/Object; +22 -1: RuntimePermission.java +21 -1: getContextClassLoader +14 -1: VARARGS_INVOKE +8 -1: zoneinfo +130 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;ILjava/lang/invoke/DirectMethodHandle$1;)V +18 -1: java/lang/Readable +11 -1: containsAll +11 -1: newPosition +57 -1: sun/reflect/InstantiationExceptionConstructorAccessorImpl +13 -1: REVERSE_ORDER +29 -1: Required array size too large +6 -1: sunday +44 -1: <T:Ljava/lang/Object;>()Ljava/util/Set<TT;>; +10 -1: toIntExact +33 -1: ([BIILjava/nio/charset/Charset;)V +14 -1: indexOfSubList +15 -1: tryAcquireNanos +31 -1: java/lang/InvalidClassException +22 -1: SecureClassLoader.java +12 -1: proxiedHosts +7 -1: ([CII)I +11 -1: toHexString +30 -1: sun/util/calendar/ZoneInfoFile +31 -1: Ljava/util/jar/Attributes$Name; +10 -1: L_USERINFO +25 -1: (IB)Ljava/nio/ByteBuffer; +11 -1: parseDouble +7 -1: ([CII)V +13 -1: Asia/Shanghai +5 -1: [...] +57 -1: ([Ljava/lang/Class;[B)[[Ljava/lang/annotation/Annotation; +19 -1: java/nio/LongBuffer +15 -1: getCertificates +9 -1: comparing +83 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;IILjava/lang/String;[B)V +43 -1: Ljava/util/concurrent/atomic/AtomicInteger; +23 -1: toFieldDescriptorString +20 -1: Lsun/misc/MetaIndex; +37 -1: java/util/Collections$UnmodifiableSet +5 -1: (JC)V +37 -1: nanosecond timeout value out of range +26 -1: AbstractStringBuilder.java +43 -1: java/lang/invoke/DirectMethodHandle$Special +49 -1: ([Ljava/nio/file/LinkOption;)Ljava/nio/file/Path; +15 -1: currentPosition +14 -1: java/net/Proxy +13 -1: asConstructor +8 -1: userInfo +14 -1: parseClassPath +15 -1: legacyMergeSort +34 -1: java/security/UnresolvedPermission +97 -1: Lsun/util/locale/LocaleObjectCache<Lsun/util/locale/BaseLocale$Key;Lsun/util/locale/BaseLocale;>; +9 -1: freeEntry +19 -1: delimiterCodePoints +34 -1: Should be non-empty if initialized +23 -1: (I)Ljava/lang/Class<*>; +11 -1: Reader.java +26 -1: checkClassLoaderPermission +27 -1: java/nio/DirectShortBufferS +95 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Lsun/misc/Launcher$ExtClassLoader;>; +27 -1: java/nio/DirectShortBufferU +20 -1: ()[Ljava/lang/Class; +56 -1: ()[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +19 -1: sun/nio/cs/UTF_16BE +24 -1: java/util/AbstractList$1 +10 -1: ([CIIIII)V +60 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/Object;>; +37 -1: (Ljava/lang/invoke/LambdaForm$Name;)S +9 -1: putDouble +52 -1: ([Ljava/security/CodeSource;)Ljava/util/Enumeration; +37 -1: (Ljava/lang/invoke/LambdaForm$Name;)Z +22 -1: ()[Ljava/lang/Package; +41 -1: java/lang/CharSequence$1CodePointIterator +13 -1: auditSubclass +24 -1: Ljava/util/jar/JarEntry; +20 -1: findMethodHandleType +15 -1: MAX_BUFFER_SIZE +19 -1: FilePermission.java +18 -1: WrappedPrintStream +36 -1: (D)Ljava/lang/AbstractStringBuilder; +11 -1: unfinalized +10 -1: getFileURL +37 -1: (Ljava/io/FileFilter;)[Ljava/io/File; +54 -1: (Ljava/nio/ByteBuffer;I)Ljava/nio/charset/CoderResult; +7 -1: ([ZI)[Z +64 -1: (Ljava/security/CodeSource;)Ljava/security/PermissionCollection; +6 -1: PUBLIC +83 -1: (Ljava/util/jar/JarFile;Ljava/net/URL;Ljava/lang/String;)Ljava/security/CodeSource; +17 -1: lockInterruptibly +67 -1: ([Ljava/util/Hashtable$Entry;Ljava/lang/Object;Ljava/lang/Object;)V +42 -1: java/util/ArraysParallelSortHelpers$FJChar +21 -1: defaultCharBufferSize +14 -1: unalignedKnown +23 -1: ()Ljava/net/Proxy$Type; +4 -1: TZDB +14 -1: CharacterCache +13 -1: lengthOfMonth +13 -1: hasExtensions +23 -1: Prefix string too short +15 -1: Executable.java +10 -1: forEachKey +6 -1: getEra +13 -1: appendEncoded +21 -1: java/util/AbstractMap +10 -1: access$100 +10 -1: access$102 +30 -1: javafx.application.Application +46 -1: (Ljava/lang/Thread$UncaughtExceptionHandler;)V +43 -1: Ljava/lang/invoke/LambdaForm$NamedFunction; +101 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;IILjava/lang/String;[B)Ljava/lang/reflect/Field; +9 -1: WILD_CHAR +21 -1: SynchronizedSortedSet +28 -1: (Ljava/util/Collection<*>;)Z +29 -1: (I[C)Ljava/lang/StringBuffer; +65 -1: (Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method; +7 -1: putByte +6 -1: H_MARK +49 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/Object; +98 -1: ([Ljava/lang/ClassValue$Entry<*>;ILjava/lang/ClassValue$Entry<*>;Z)Ljava/lang/ClassValue$Entry<*>; +23 -1: array is not of length +52 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;TT;TT;)Z +41 -1: Ljava/util/Collections$EmptyListIterator; +8 -1: +11 -1: updateCheck +29 -1: getBootClassPathEntryForClass +27 -1: sun/nio/cs/US_ASCII$Encoder +10 -1: bindCaller +18 -1: Ljava/util/BitSet; +10 -1: checkRange +77 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/Void;)V +7 -1: Classes +6 -1: store0 +23 -1: java/lang/Thread$Caches +25 -1: Ljava/lang/CharacterData; +7 -1: (JI[C)V +49 -1: (Ljava/util/LinkedList;Ljava/util/LinkedList$1;)V +16 -1: getGcInfoBuilder +12 -1: counterCells +14 -1: memoryLimitSet +7 -1: , nojit +9 -1: sharpsMap +7 -1: october +13 -1: isProxiedHost +9 -1: rawOffset +18 -1: toJavaFormatString +19 -1: sun.boot.class.path +60 -1: (ILjava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder; +11 -1: spliterator +13 -1: contentLength +18 -1: unixTimeToFileTime +31 -1: Lsun/reflect/LangReflectAccess; +16 -1: while Java has +79 -1: (JLjava/util/function/ToIntBiFunction;ILjava/util/function/IntBinaryOperator;)I +12 -1: timeEndOfDay +17 -1: getCustomTimeZone +25 -1: Ljava/lang/ref/Reference; +20 -1: ()Ljava/util/Locale; +64 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;Z)V +13 -1: MAX_JVM_ARITY +72 -1: (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType; +11 -1: AsLIFOQueue +84 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/lang/Object;)Ljava/util/List<TT;>; +12 -1: mappingCount +29 -1: (Ljava/io/FileOutputStream;)V +50 -1: <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;TT;)V +23 -1: Category cannot be NULL +10 -1: normalized +5 -1: CLASS +28 -1: (IZ)Ljava/lang/StringBuffer; +18 -1: java/lang/System$1 +18 -1: java/lang/System$2 +9 -1: getResult +44 -1: ()Ljava/util/Collection<Ljava/lang/Thread;>; +8 -1: isNative +59 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Short;>; +24 -1: [Ljava/lang/ThreadGroup; +24 -1: (B)Ljava/nio/ByteBuffer; +4 -1: READ +44 -1: (Ljava/io/FilePermission;)Ljava/lang/String; +93 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedCollection<TE;>;Ljava/util/Set<TE;>; +12 -1: compileClass +12 -1: isProxyClass +20 -1: isSystemDomainLoader +74 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<-TT;>;)V +7 -1: getMask +72 -1: (Ljava/lang/ClassLoader;Ljava/lang/SecurityManager;Ljava/lang/String;I)V +20 -1: removeLastOccurrence +64 -1: (Ljava/lang/reflect/Field;)Ljava/lang/invoke/DirectMethodHandle; +57 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>; +11 -1: parkBlocker +40 -1: (Lsun/misc/JarIndex;Ljava/lang/String;)V +33 -1: [Ljava/security/ProtectionDomain; +14 -1: setContentType +14 -1: getEnumeration +18 -1: ProtectionDomain +76 -1: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar$Date; +16 -1: setRequestMethod +52 -1: (Ljava/util/List;Ljava/lang/Object;)Ljava/util/List; +32 -1: Lsun/util/calendar/BaseCalendar; +52 -1: (Ljava/net/URL;Ljava/lang/String;)Ljava/lang/String; +5 -1: .:@[] +7 -1: addLast +21 -1: AnnotatedElement.java +10 -1: defaultVal +16 -1: getCanonicalPath +17 -1: protection_domain +9 -1: strictfp +19 -1: sun/nio/cs/UTF_16LE +9 -1: readBytes +18 -1: removeStaleEntries +46 -1: java/util/Collections$UnmodifiableNavigableSet +5 -1: cpath +14 -1: COPY_THRESHOLD +8 -1: permsMap +8 -1: japanese +33 -1: java/nio/charset/StandardCharsets +47 -1: Lsun/reflect/DelegatingConstructorAccessorImpl; +19 -1: NF_checkGenericType +40 -1: java/util/Collections$UnmodifiableList$1 +4 -1: TERM +48 -1: The following can be used with stack and domain: +27 -1: ([BII)Ljava/nio/ByteBuffer; +40 -1: Ljava/util/Vector<Ljava/lang/Class<*>;>; +5 -1: digit +7 -1: isFinal +39 -1: (Ljava/lang/String;Ljava/lang/String;)I +26 -1: memberDeclaringClassOrNull +10 -1: ([CI[BII)I +38 -1: (Ljava/lang/Class;I)Ljava/lang/Object; +15 -1: isValidProtocol +17 -1: (this Collection) +11 -1: getTreeNode +16 -1: ThreadLocal.java +23 -1: java/nio/HeapLongBuffer +39 -1: (Ljava/lang/String;Ljava/lang/String;)V +12 -1: getNextEntry +39 -1: (Ljava/lang/String;Ljava/lang/String;)Z +28 -1: (C)Lsun/invoke/util/Wrapper; +9 -1: readFloat +21 -1: overrideFieldAccessor +16 -1: fillInStackTrace +16 -1: getDeclaredField +5 -1: deflt +8 -1: nextChar +10 -1: primCounts +22 -1: getAnnotatedInterfaces +26 -1: ()Ljava/util/zip/Inflater; +73 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;)TU; +16 -1: traceMethodCalls +10 -1: sun.nio.cs +7 -1: marshal +9 -1: ftypeKind +77 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class;)Ljava/lang/invoke/MemberName; +11 -1: sun/misc/VM +14 -1: GuardWithCatch +40 -1: (Ljava/lang/Object;)Ljava/util/Iterator; +13 -1: subtractExact +42 -1: (Ljava/lang/Object;ILjava/lang/Object;II)V +10 -1: isInfinite +18 -1: sun.util.calendar. +14 -1: java/lang/Math +16 -1: java/lang/String +10 -1: encodePath +14 -1: compactAndTrim +11 -1: getVariants +4 -1: from +47 -1: (Ljava/lang/ThreadLocal<*>;Ljava/lang/Object;)V +35 -1: serializeAgentPropertiesToByteArray +16 -1: computeIfPresent +9 -1: EMPTY_MAP +28 -1: java/lang/InstantiationError +68 -1: (Ljava/util/Comparator;Ljava/util/Comparator;)Ljava/util/Comparator; +14 -1: getDisplayName +14 -1: limitedContext +23 -1: usagetracker.properties +52 -1: java/util/concurrent/locks/ReentrantLock$NonfairSync +7 -1: public +17 -1: srcBegin > srcEnd +48 -1: (ILjava/util/List;)Ljava/lang/invoke/MethodType; +21 -1: java/lang/ThreadDeath +9 -1: gregorian +111 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;ILjava/lang/Object;Ljava/lang/Object;)Ljava/util/HashMap$TreeNode; +52 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;III)V +12 -1: generateFile +20 -1: appendParameterTypes +22 -1: sun/misc/FileURLMapper +13 -1: defaultDomain +25 -1: (I)Ljava/time/ZoneOffset; +21 -1: getBooleanAttributes0 +39 -1: (Ljava/lang/String;)Lsun/misc/Resource; +43 -1: Ljava/lang/Thread$UncaughtExceptionHandler; +23 -1: getTypeAnnotationBytes0 +8 -1: ELOT_928 +32 -1: sun/nio/cs/FastCharsetProvider$1 +34 -1: Ljava/nio/BufferOverflowException; +14 -1: AppClassLoader +10 -1: protected +12 -1: isAnnotation +16 -1: PerfCounter.java +51 -1: Ljava/util/Map<Ljava/io/File;Lsun/misc/MetaIndex;>; +160 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +11 -1: setLeapYear +16 -1: parseContextSpec +16 -1: setFieldAccessor +8 -1: writeInt +16 -1: java/lang/Number +33 -1: java/util/AbstractMap$SimpleEntry +9 -1: nextTable +8 -1: getQuery +14 -1: putOrderedLong +93 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V +52 -1: (Ljava/lang/String;)Lsun/reflect/generics/tree/Tree; +85 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap$Node;)V +9 -1: singleton +9 -1: destroyed +15 -1: iso_8859-2:1987 +12 -1: comparingInt +29 -1: [Ljava/lang/OutOfMemoryError; +39 -1: (Ljava/lang/String;Ljava/lang/Object;)V +27 -1: ([Ljava/util/Enumeration;)V +36 -1: Ljava/nio/charset/CodingErrorAction; +16 -1: getCanonicalName +41 -1: (Ljava/nio/LongBuffer;)Ljava/util/BitSet; +15 -1: DISPLAY_COUNTRY +32 -1: getFunctionalInterfaceMethodName +66 -1: (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V +24 -1: getUnresolvedPermissions +66 -1: ([Ljava/lang/ClassValue$Entry<*>;I)Ljava/lang/ClassValue$Entry<*>; +41 -1: ()Lsun/reflect/annotation/AnnotationType; +7 -1: afIndex +7 -1: csascii +20 -1: ()Ljava/util/Vector; +16 -1: EntrySpliterator +53 -1: (Ljava/lang/Throwable;I)Ljava/lang/StackTraceElement; +81 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle; +20 -1: classAssertionStatus +7 -1: EXECUTE +21 -1: ()Ljava/lang/Runtime; +6 -1: cesu-8 +7 -1: offset +23 -1: Ljava/lang/SafeVarargs; +34 -1: (Ljava/util/LinkedHashMap$Entry;)V +8 -1: entryFor +8 -1: getCache +46 -1: (Ljava/lang/Object;I)Ljava/lang/reflect/Field; +4 -1: skip +8 -1: (II[CI)V +4 -1: vart +12 -1: InnerClasses +68 -1: (Ljava/util/Map;Ljava/lang/Class;[Ljava/lang/String;)Ljava/util/Map; +19 -1: currentClassLoader0 +34 -1: getDefaultUncaughtExceptionHandler +11 -1: String.java +117 -1: (Ljava/lang/ThreadLocal<*>;ILjava/lang/ThreadLocal$ThreadLocalMap$Entry;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +7 -1: january +31 -1: (ILjava/lang/String;IIIIIIIII)V +24 -1: Lsun/misc/JavaAWTAccess; +5 -1: .path +15 -1: [Ljava/io/File; +11 -1: Writer.java +8 -1: , Size: +159 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Function;Ljava/util/function/Consumer;)V +32 -1: java/lang/invoke/LambdaForm$Name +51 -1: (Ljava/util/ArrayList;Ljava/util/AbstractList;III)V +7 -1: getZone +14 -1: JIS_X0212-1990 +21 -1: (Ljava/util/Set<*>;)Z +149 -1: (Lsun/util/locale/provider/LocaleServiceProviderPool$LocalizedObjectGetter;Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object; +21 -1: Illegal Load factor: +35 -1: ()Ljava/lang/invoke/MethodTypeForm; +22 -1: ([Z)Ljava/lang/String; +7 -1: setName +48 -1: <T:Ljava/lang/Object;>(TT;Ljava/lang/String;)TT; +9 -1: INTERFACE +22 -1: ARRAY_CHAR_INDEX_SCALE +19 -1: java/nio/ByteBuffer +23 -1: Ljava/io/ExpiringCache; +7 -1: streams +44 -1: java/lang/invoke/DirectMethodHandle$Accessor +36 -1: ([Ljava/security/ProtectionDomain;)V +16 -1: afterNodeRemoval +9 -1: prevIndex +49 -1: java/util/concurrent/ConcurrentHashMap$KeySetView +22 -1: getEnumConstantsShared +17 -1: java/lang/Integer +12 -1: getRawOffset +36 -1: ()Ljava/nio/file/attribute/FileTime; +7 -1: offsets +10 -1: ] throw => +3 -1: [[B +10 -1: hostsEqual +18 -1: compareComparables +35 -1: sun/reflect/ConstructorAccessorImpl +8 -1: december +36 -1: Invalid binary time-zone data: TZDB: +24 -1: synchronizedNavigableMap +72 -1: sun/util/locale/provider/LocaleServiceProviderPool$LocalizedObjectGetter +19 -1: parseCustomTimeZone +88 -1: (Ljava/lang/Class<*>;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle; +9 -1: findClass +6 -1: OBJECT +3 -1: GBK +110 -1: (JLjava/util/function/ToLongFunction<Ljava/util/Map$Entry<TK;TV;>;>;JLjava/util/function/LongBinaryOperator;)J +6 -1: UTF_16 +17 -1: <all permissions> +13 -1: lookupCharset +28 -1: ConstructorAccessorImpl.java +62 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToLongTask +30 -1: Ljava/lang/ClassCastException; +56 -1: ()Ljava/util/Spliterator<Ljava/util/Map$Entry<TK;TV;>;>; +11 -1: invokerType +23 -1: java/lang/StringBuilder +12 -1: deleteCharAt +11 -1: CR_OVERFLOW +14 -1: toExternalForm +3 -1: out +34 -1: Ljava/net/URLStreamHandlerFactory; +15 -1: encodeArrayLoop +30 -1: ()Ljava/util/Enumeration<TK;>; +27 -1: java/io/SyncFailedException +10 -1: checkedSet +16 -1: checkedSortedSet +22 -1: java/util/zip/ZipEntry +43 -1: java/util/LinkedHashMap$LinkedValueIterator +22 -1: UnmodifiableCollection +32 -1: java/nio/ByteBufferAsCharBufferB +11 -1: blockerLock +27 -1: checkExtensionsDependencies +11 -1: fromIndex: +32 -1: java/nio/ByteBufferAsCharBufferL +6 -1: UTF_32 +30 -1: java/util/Hashtable$Enumerator +92 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<+TK;+TV;>;)Ljava/util/Map<TK;TV;>; +4 -1: tail +5 -1: (BB)C +16 -1: isValidSignature +9 -1: addMillis +9 -1: peekFirst +86 -1: <E:Ljava/lang/Object;>(Ljava/util/Set<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Set<TE;>; +5 -1: (BB)I +15 -1: jvmMicroVersion +28 -1: [Ljava/util/Hashtable$Entry; +15 -1: iso_8859-5:1988 +14 -1: HOUR_IN_MILLIS +67 -1: (Ljava/util/PrimitiveIterator$OfInt;I)Ljava/util/Spliterator$OfInt; +5 -1: (BB)S +21 -1: UnmodifiableSortedMap +79 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap;)V +56 -1: Ljava/util/Stack<Ljava/lang/ClassLoader$NativeLibrary;>; +5 -1: (BB)Z +10 -1: treeifyBin +8 -1: isOpaque +27 -1: java.launcher.ergo.message1 +27 -1: java.launcher.ergo.message2 +101 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;Ljava/lang/Comparable<Ljava/util/Date;>; +31 -1: (Ljava/io/ObjectOutputStream;)V +3 -1: GET +13 -1: matchLocation +13 -1: WrappedMember +63 -1: NoSuchMethodException:\n could not find proper constructor for +14 -1: gssloginconfig +70 -1: (Ljava/util/LinkedList$Node<TE;>;TE;Ljava/util/LinkedList$Node<TE;>;)V +57 -1: (Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object; +11 -1: toByteArray +49 -1: java/util/ArraysParallelSortHelpers$FJByte$Sorter +13 -1: no protocol: +940 -1: aaaarababkaeaveafafrakakaamamhanargararaasasmavavaayaymazazebabakbebelbgbulbhbihbibisbmbambnbenbobodbrbrebsboscacatcechechchacocoscrcrecscescuchucvchvcycymdadandedeudvdivdzdzoeeeweelellenengeoepoesspaetesteueusfafasfffulfifinfjfijfofaofrfrafyfrygaglegdglaglglggngrngugujgvglvhahauhehebhihinhohmohrhrvhthathuhunhyhyehzheriainaidindieileigiboiiiiiikipkinindioidoisislititaiuikuiwhebjajpnjiyidjvjavkakatkgkonkikikkjkuakkkazklkalkmkhmknkankokorkrkaukskaskukurkvkomkwcorkykirlalatlbltzlgluglilimlnlinlolaoltlitlulublvlavmgmlgmhmahmimrimkmkdmlmalmnmonmomolmrmarmsmsamtmltmymyananaunbnobndndenenepngndonlnldnnnnononornrnblnvnavnynyaocociojojiomormororiososspapanpipliplpolpspusptporququermrohrnrunroronrurusrwkinsasanscsrdsdsndsesmesgsagsisinskslkslslvsmsmosnsnasosomsqsqisrsrpsssswstsotsusunsvsweswswatatamteteltgtgkththatitirtktuktltgltntsntotontrturtstsotttattwtwitytahuguigukukrururduzuzbvevenvivievovolwawlnwowolxhxhoyiyidyoyorzazhazhzhozuzul +42 -1: java/util/LinkedHashMap$LinkedHashIterator +39 -1: java/util/ArrayDeque$DescendingIterator +15 -1: MODIFIER_LETTER +21 -1: (Lsun/misc/Cleaner;)Z +12 -1: PACKAGE_NAME +14 -1: getMappedValue +10 -1: interrupt0 +8 -1: LF_LIMIT +17 -1: getDeclaringClass +42 -1: (ZILjava/lang/String;)Ljava/lang/Class<*>; +57 -1: (Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;[I)V +15 -1: java/nio/Bits$1 +61 -1: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class; +28 -1: java/lang/ref/ReferenceQueue +33 -1: [Ljava/security/cert/Certificate; +44 -1: (Ljava/util/Hashtable;I)Ljava/util/Iterator; +24 -1: java/security/CodeSigner +19 -1: Non-positive length +24 -1: [Ljava/util/Enumeration; +38 -1: ()Ljava/security/PermissionCollection; +16 -1: checkGenericType +56 -1: java/util/concurrent/ConcurrentHashMap$ReduceEntriesTask +7 -1: getYear +5 -1: atime +19 -1: Ljava/util/HashMap; +125 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;Ljava/util/function/Consumer<-TU;>;)V +22 -1: STOP_THREAD_PERMISSION +46 -1: (Ljava/util/Enumeration;)Ljava/util/ArrayList; +16 -1: getPathSeparator +10 -1: getMembers +8 -1: getIntAt +26 -1: java/io/File$TempDirectory +48 -1: java/lang/invoke/MethodHandleImpl$GuardWithCatch +25 -1: CaseInsensitiveComparator +8 -1: pollLast +21 -1: GET_POLICY_PERMISSION +23 -1: uninitialized call site +75 -1: (Ljava/util/function/Function;Ljava/util/Comparator;)Ljava/util/Comparator; +9 -1: directory +51 -1: (JLjava/util/function/BiFunction<-TV;-TV;+TV;>;)TV; +41 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;)V +42 -1: (Ljava/lang/Throwable;Ljava/lang/String;)V +26 -1: (FLjava/lang/Appendable;)V +5 -1: stop0 +9 -1: substring +64 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)V +5 -1: (JD)V +9 -1: getShortB +10 -1: nextOrSame +24 -1: [ interpretWithArguments +6 -1: GB2312 +32 -1: java/nio/BufferOverflowException +23 -1: sun/nio/cs/ArrayEncoder +4 -1: tanh +9 -1: getShortL +55 -1: (JLjava/util/function/BiFunction;)Ljava/util/Map$Entry; +10 -1: getMessage +12 -1: findTreeNode +38 -1: DIRECTIONALITY_COMMON_NUMBER_SEPARATOR +23 -1: [Ljava/lang/Comparable; +17 -1: getCallSiteTarget +55 -1: (Ljava/nio/ByteBuffer;II)Ljava/nio/charset/CoderResult; +19 -1: java/util/ArrayList +17 -1: java/io/DataInput +13 -1: getPrincipals +52 -1: <E:Ljava/lang/Object;>(TE;)Ljava/util/Iterator<TE;>; +44 -1: sun/reflect/generics/tree/ClassTypeSignature +6 -1: loaded +20 -1: ()Ljava/lang/Object; +36 -1: Ljava/util/concurrent/ConcurrentMap; +13 -1: classValueMap +33 -1: java/lang/SystemClassLoaderAction +6 -1: loader +31 -1: java/lang/annotation/Annotation +5 -1: colon +11 -1: Vector.java +24 -1: CharacterDataLatin1.java +12 -1: setUseCaches +22 -1: getTypeAnnotationBytes +9 -1: readShort +24 -1: longPrimitiveReturnCount +5 -1: get16 +34 -1: setDefaultUncaughtExceptionHandler +17 -1: cachedInputStream +29 -1: java/util/LinkedHashMap$Entry +17 -1: java/lang/Boolean +50 -1: ()[Lsun/reflect/generics/tree/FormalTypeParameter; +14 -1: AnnotationData +21 -1: Ljava/net/Proxy$Type; +13 -1: invokeVirtual +14 -1: Parameter.java +21 -1: getDayOfWeekDateAfter +92 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +56 -1: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +46 -1: sun/util/locale/provider/LocaleProviderAdapter +29 -1: Ljava/lang/StackTraceElement; +47 -1: (TK;Ljava/util/function/Function<-TK;+TV;>;)TV; +32 -1: enableContextClassLoaderOverride +68 -1: (Ljava/lang/AbstractStringBuilder;)Ljava/lang/AbstractStringBuilder; +51 -1: Ljava/util/concurrent/ConcurrentHashMap$KeySetView; +9 -1: addAndGet +5 -1: store +7 -1: ([JII)V +15 -1: signatureReturn +18 -1: NF_reinvokerTarget +22 -1: ()Ljava/nio/file/Path; +25 -1: (C)Ljava/lang/Appendable; +7 -1: expires +18 -1: initializeInvokers +19 -1: application/java-vm +13 -1: stopOrSuspend +13 -1: rawOffsetDiff +6 -1: (JJZ)V +9 -1: findValue +5 -1: get32 +10 -1: asSubclass +6 -1: forJRE +3 -1: GMT +7 -1: delete0 +69 -1: ([Ljava/security/cert/Certificate;[Ljava/security/cert/Certificate;)Z +75 -1: (Ljava/util/LinkedList$Node;Ljava/lang/Object;Ljava/util/LinkedList$Node;)V +6 -1: setEra +24 -1: ()Ljava/util/Comparator; +10 -1: access$200 +10 -1: access$202 +20 -1: retrieveDisplayNames +3 -1: pae +24 -1: java/lang/Byte$ByteCache +7 -1: VM.java +9 -1: TRANSIENT +6 -1: setErr +16 -1: jdkUpdateVersion +10 -1: isResolved +35 -1: sun/misc/JavaIOFileDescriptorAccess +4 -1: char +13 -1: Readable.java +19 -1: UnixFileSystem.java +43 -1: (Ljava/lang/ThreadLocal;)Ljava/lang/Object; +40 -1: (Ljava/lang/String;[Ljava/lang/String;)V +7 -1: profile +11 -1: , version: +25 -1: PermissionCollection.java +13 -1: setNormalized +10 -1: access$210 +24 -1: (Ljava/lang/String;IJZ)J +19 -1: isAnnotationPresent +85 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)[Ljava/lang/annotation/Annotation; +19 -1: DMH.invokeInterface +157 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;TV;>;Ljava/util/Collection<TV;>;Ljava/io/Serializable; +94 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableCollection<TE;>;Ljava/util/List<TE;>; +41 -1: java/lang/StringIndexOutOfBoundsException +29 -1: java/net/UnknownHostException +11 -1: (BBBBBBBB)J +4 -1: iioe +12 -1: (TK;TV;Z)TV; +5 -1: ERROR +31 -1: (Ljava/io/File;Ljava/io/File;)I +32 -1: [Ljava/lang/invoke/MethodHandle; +27 -1: lambda$comparing$77a9974f$1 +13 -1: toLowerCaseEx +61 -1: (Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;)V +66 -1: (ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/HashMap$TreeNode; +43 -1: java/util/concurrent/ConcurrentHashMap$Node +23 -1: latestUserDefinedLoader +24 -1: buildAnnotatedSuperclass +19 -1: compareToIgnoreCase +31 -1: (Ljava/io/File;Ljava/io/File;)Z +40 -1: (I[C)[Ljava/lang/invoke/LambdaForm$Name; +16 -1: ROTATE_THRESHOLD +18 -1: getClassAtIfLoaded +37 -1: java/lang/IllegalThreadStateException +5 -1: get64 +12 -1: writeReplace +14 -1: DAYS_PER_CYCLE +4 -1: _get +6 -1: nChars +26 -1: ()Ljava/net/SocketAddress; +27 -1: setUncaughtExceptionHandler +6 -1: jzfile +42 -1: All subclasses should override this method +3 2: Foo +56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +136 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;Ljava/security/AccessControlContext;[Ljava/security/Permission;)TT; +3 -1: pdt +44 -1: sun/util/locale/LocaleObjectCache$CacheEntry +20 -1: java/util/Comparator +9 -1: suspended +11 -1: removeEntry +10 -1: SPACE_FREE +12 -1: processQueue +9 -1: java.home +5 -1: valid +23 -1: printEnclosedStackTrace +4 -1: push +5 -1: guard +23 -1: javaNetHttpCookieAccess +36 -1: (Ljava/security/cert/Certificate;)[B +9 -1: isWrapped +12 -1: CharIterator +26 -1: sun.io.useCanonPrefixCache +42 -1: (Lsun/misc/Cleaner;Ljava/lang/Throwable;)V +7 -1: prepare +8 -1: parseInt +13 -1: Invokers.java +63 -1: (Ljava/net/URLClassLoader;)Ljava/security/AccessControlContext; +18 -1: defaultWriteObject +5 -1: class +15 -1: EnclosingMethod +23 -1: ([BI)Ljava/lang/String; +16 -1: rangeCheckForAdd +11 -1: getTimeImpl +15 -1: arrayIndexScale +5 -1: scalb +5 -1: scale +45 -1: (Ljava/lang/String;J)Ljava/util/zip/ZipEntry; +8 -1: ([FIIF)I +16 -1: runAllFinalizers +27 -1: ()Lsun/net/ProgressMonitor; +15 -1: MemberName.java +27 -1: [Ljava/lang/reflect/Member; +6 -1: length +14 -1: genericInvoker +8 -1: ([FIIF)V +34 -1: Ljava/nio/file/attribute/FileTime; +6 -1: number +70 -1: (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder; +12 -1: printLocales +38 -1: java/lang/invoke/MethodHandleStatics$1 +8 -1: private +20 -1: invalid actions mask +10 -1: not found +13 -1: parseClassSig +22 -1: getAllAvailableLocales +175 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Function;Ljava/util/concurrent/atomic/AtomicReference;)V +8 -1: ([BII)[B +35 -1: (Ljava/util/HashMap$Node<TK;TV;>;)V +132 -1: (Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;[Ljava/lang/Object;[I[Ljava/lang/Object;)V +8 -1: ([BII)[C +20 -1: DECIMAL_DIGIT_NUMBER +40 -1: (Ljava/lang/String;[Ljava/lang/Object;)Z +7 -1: ([CCI)I +64 -1: (TV;)Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>; +8 -1: aliasMap +8 -1: checkfpx +44 -1: java/util/Comparators$NaturalOrderComparator +48 -1: (Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader; +12 -1: Name is null +10 -1: invoke_L_L +8 -1: URL.java +51 -1: (JILjava/time/ZoneOffset;)Ljava/time/LocalDateTime; +25 -1: Lsun/invoke/util/Wrapper; +19 -1: LauncherHelper.java +10 -1: invoke_L_V +5 -1: index +30 -1: sun/security/x509/X509CertImpl +8 -1: addClass +46 -1: (I)Ljava/lang/invoke/LambdaForm$NamedFunction; +20 -1: refKindIsConstructor +10 -1: getFieldAt +5 -1: log10 +13 -1: GetPerfAction +15 -1: isLetterOrDigit +27 -1: hasCheckedSpecialAttributes +25 -1: MapReduceEntriesToIntTask +17 -1: probeHomeLocation +9 -1: image/png +19 -1: checkSpreadArgument +12 -1: LinkedKeySet +13 -1: removeMapping +39 -1: sun/security/util/SecurityConstants$AWT +9 -1: MAX_RADIX +28 -1: (F)Ljava/lang/StringBuilder; +11 -1: ([C[B[I[I)Z +36 -1: (Ljava/lang/Class;)Ljava/lang/Class; +32 -1: java/lang/invoke/MagicLambdaImpl +6 -1: monday +16 -1: closeClassLoader +41 -1: (Ljava/util/concurrent/locks/Condition;)I +8 -1: reversed +27 -1: sun/util/calendar/Gregorian +49 -1: (Lsun/nio/cs/FastCharsetProvider;)Ljava/util/Map; +42 -1: (Ljava/lang/String;Ljava/lang/Throwable;)V +18 -1: java/util/Calendar +8 -1: vmtarget +17 -1: TREEIFY_THRESHOLD +41 -1: (Ljava/util/concurrent/locks/Condition;)Z +9 -1: deadChild +8 -1: EntrySet +5 -1: log1p +58 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/util/HashMap;)V +9 -1: pageCount +14 -1: slotToArgTable +24 -1: toMethodDescriptorString +25 -1: ([B)Ljava/nio/ByteBuffer; +21 -1: twoToTheDoubleScaleUp +32 -1: sun/misc/URLClassPath$FileLoader +40 -1: (Ljava/util/List<Ljava/lang/String;>;Z)V +6 -1: cache1 +6 -1: cache2 +27 -1: Ljava/net/URLStreamHandler; +20 -1: Detect premature EOF +94 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)Lsun/nio/cs/StreamEncoder; +12 -1: NF_checkBase +20 -1: (Ljava/nio/Buffer;)V +45 -1: <E:Ljava/lang/Object;>Ljava/util/Vector<TE;>; +4 -1: Sync +12 -1: H_UNRESERVED +32 -1: (Ljava/util/Map;)Ljava/util/Set; +32 -1: (Ljava/util/Map$Entry<TK;TV;>;)Z +25 -1: java/util/Locale$Category +8 -1: receiver +9 -1: MAX_VALUE +4 -1: .RSA +25 -1: Ljava/net/URLClassLoader; +15 -1: Terminator.java +26 -1: (Ljava/lang/String;TV;)TV; +4 -1: null +76 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V +9 -1: checkCast +39 -1: ()Ljava/lang/AssertionStatusDirectives; +15 -1: declaredMethods +5 -1: clazz +21 -1: Retention policy: +20 -1: spreadArgElementType +9 -1: WORD_MASK +27 -1: ([IILjava/io/InputStream;)I +11 -1: getMethodAt +31 -1: (Ljava/net/URL;Ljava/net/URL;)Z +13 -1: getLocaleName +14 -1: isEnumConstant +41 -1: (Ljava/lang/String;)Ljava/nio/CharBuffer; +16 -1: newInvokeSpecial +26 -1: (Ljava/nio/ByteBuffer;IS)V +22 -1: sun/misc/JavaNioAccess +184 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/security/AccessControlContext; +96 -1: (Ljava/lang/invoke/MethodHandle;ILjava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +5 -1: ([S)I +30 -1: java/security/AccessController +37 -1: sun/misc/Launcher$SharedArchiveLoader +4 -1: pack +5 -1: ([S)V +51 -1: failure before throwing exception, dump stack +10 -1: dayOfMonth +6 -1: CENSIG +28 -1: java/util/function/Predicate +26 -1: Malformed \\uxxxx encoding. +9 -1: initIndex +11 -1: invoke_LL_L +23 -1: ConcurrentWeakInternSet +14 -1: java/lang/Void +42 -1: java/lang/String$CaseInsensitiveComparator +38 -1: Ljava/lang/invoke/LambdaForm$Compiled; +34 -1: java/util/Collections$SingletonSet +142 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/invoke/CallSite; +35 -1: too many bootstrap method arguments +17 -1: maxDelimCodePoint +13 -1: previousIndex +3 -1: pop +6 -1: CENSIZ +7 -1: ([Z[Z)Z +11 -1: invoke_LL_V +3 -1: pos +33 -1: java/nio/file/WatchEvent$Modifier +3 -1: pow +12 -1: nextClearBit +15 -1: Dictionary.java +27 -1: sun/reflect/CallerSensitive +13 -1: signatureType +10 -1: dstSavings +13 -1: UnicodeLittle +16 -1: America/New_York +82 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale; +25 -1: referenceKindIsConsistent +62 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/LongBuffer;>; +4 -1: INTS +11 -1: LOAD_FACTOR +40 -1: sun/reflect/DelegatingMethodAccessorImpl +16 -1: accumulateAndGet +21 -1: (B)Ljava/lang/String; +6 -1: UNSAFE +13 -1: resolveOrFail +21 -1: MapReduceMappingsTask +5 -1: arity +77 -1: (Ljava/io/FileDescriptor;ZZLjava/lang/Object;)Ljava/nio/channels/FileChannel; +5 -1: value +61 -1: Ljava/util/concurrent/ConcurrentHashMap$EntrySetView<TK;TV;>; +6 -1: forJar +52 -1: <T:Ljava/lang/Object;>Ljava/lang/ref/Reference<TT;>; +21 -1: CREATE_ACC_PERMISSION +28 -1: sun/misc/NativeSignalHandler +11 -1: defineClass +5 -1: match +93 -1: (Ljava/util/zip/ZipFile;Ljava/util/zip/ZipFile$ZipFileInputStream;Ljava/util/zip/Inflater;I)V +11 -1: Double.java +30 -1: America/Argentina/Buenos_Aires +8 -1: previous +37 -1: (Ljava/io/Writer;Ljava/lang/String;)V +9 -1: Synthetic +18 -1: isVMAnonymousClass +62 -1: ([Ljava/lang/Object;Ljava/lang/Object;Ljava/util/Comparator;)I +22 -1: (JI)Ljava/lang/String; +49 -1: (Ljava/lang/CharSequence;II)Ljava/io/PrintStream; +52 -1: ([Ljava/lang/Thread;)[[Ljava/lang/StackTraceElement; +12 -1: setDayOfWeek +11 -1: getManifest +30 -1: java/util/Locale$FilteringMode +54 -1: (Ljava/lang/String;)Lsun/util/calendar/CalendarSystem; +12 -1: nextHashCode +19 -1: ()Ljava/io/Console; +42 -1: AccessControlContext invoking the Combiner +19 -1: shouldBeInitialized +17 -1: invocationCounter +21 -1: IMPLEMENTATION_VENDOR +7 -1: chararr +60 -1: (Ljava/lang/String;)Ljava/util/Iterator<Ljava/lang/String;>; +11 -1: asCollector +52 -1: (ILjava/lang/CharSequence;)Ljava/lang/StringBuilder; +9 -1: exception +22 -1: newInstanceCallerCache +20 -1: nativeLibraryContext +24 -1: MODIFY_THREAD_PERMISSION +4 -1: WRAP +19 -1: Method name is null +32 -1: sun/invoke/util/ValueConversions +7 -1: APP_TAG +24 -1: (Ljava/util/Hashtable;)I +23 -1: METHOD_FORMAL_PARAMETER +18 -1: inflationThreshold +24 -1: java/io/DeleteOnExitHook +3 -1: pst +13 -1: BITS_PER_WORD +25 -1: getConstructorAnnotations +17 -1: CheckedCollection +24 -1: (Ljava/util/Hashtable;)V +7 -1: inClass +5 -1: getFD +8 -1: readLong +19 -1: aliases_ISO_8859_13 +19 -1: invokeWithArguments +19 -1: aliases_ISO_8859_15 +42 -1: ()Ljava/util/concurrent/ConcurrentHashMap; +8 -1: expandTo +23 -1: java/text/MessageFormat +8 -1: classID0 +11 -1: replaceWith +21 -1: Invalid port number : +65 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V +19 -1: isGregorianLeapYear +16 -1: asSpreaderChecks +11 -1: withInitial +10 -1: X-UTF-32BE +57 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V +12 -1: wrong type: +57 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z +17 -1: sun/misc/Resource +14 -1: getReadTimeout +8 -1: safeTrim +38 -1: DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING +14 -1: isElementIndex +23 -1: FilterOutputStream.java +6 -1: (IJI)V +3 -1: put +57 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; +43 -1: Expecting an absolute path of the library: +8 -1: checkRef +53 -1: (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder; +74 -1: (Ljava/lang/Class<*>;[Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field; +11 -1: user.script +10 -1: H_ALPHANUM +17 -1: getCalendarSystem +48 -1: Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>; +17 -1: EnsureInitialized +82 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;Ljava/lang/Object;)V +243 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToIntTask;Ljava/util/function/ToIntBiFunction;ILjava/util/function/IntBinaryOperator;)V +26 -1: getAnnotatedExceptionTypes +10 -1: validIndex +6 -1: ([CC)I +8 -1: overflow +5 -1: getID +93 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)Lsun/nio/cs/StreamDecoder; +22 -1: java/util/StringJoiner +9 -1: putFields +18 -1: USE_SHARED_ARCHIVE +8 -1: thursday +8 -1: nanoTime +59 -1: (Lsun/reflect/annotation/AnnotationType;Ljava/lang/Class;)V +69 -1: (Ljava/nio/charset/CoderResult$Cache;I)Ljava/nio/charset/CoderResult; +36 -1: (J)Ljava/lang/AbstractStringBuilder; +16 -1: java/util/Arrays +6 -1: ([CC)V +25 -1: registerAsParallelCapable +4 -1: slot +24 -1: java/net/URLConnection$1 +6 -1: koi8-r +19 -1: should be of type +46 -1: java/util/concurrent/ConcurrentHashMap$TreeBin +6 -1: koi8-u +34 -1: data type scale not a power of two +53 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>; +21 -1: AccessibleObject.java +53 -1: <E:Ljava/lang/Object;>()Ljava/util/NavigableSet<TE;>; +88 -1: (Ljava/util/List;Ljava/util/Collection;Ljava/util/Locale$FilteringMode;)Ljava/util/List; +17 -1: getAnnotationType +36 -1: Ljava/util/HashMap$TreeNode<TK;TV;>; +14 -1: declaringClass +10 -1: read,write +5 -1: getId +10 -1: BIG_ENDIAN +20 -1: PolymorphicSignature +16 -1: comparingByValue +67 -1: (ILjava/lang/invoke/MethodType;)[Ljava/lang/invoke/LambdaForm$Name; +19 -1: java/util/Hashtable +20 -1: getUnicodeLocaleKeys +27 -1: ()Ljava/util/LinkedHashMap; +51 -1: (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder; +30 -1: java/util/HashMap$HashIterator +22 -1: (IZ)Ljava/lang/String; +5 -1: yield +34 -1: java/lang/Throwable$SentinelHolder +62 -1: (Ljava/lang/invoke/MethodType;ZI)Ljava/lang/invoke/LambdaForm; +5 -1: (FD)F +17 -1: java/util/SubList +24 -1: (Ljava/io/PrintStream;)V +7 -1: LF.zero +25 -1: java/util/StringTokenizer +15 -1: ISO8859_15_FDIS +11 -1: powerOfTwoD +11 -1: powerOfTwoF +22 -1: WeakHashMapSpliterator +15 -1: refKindIsGetter +12 -1: setRawOffset +11 -1: setProperty +23 -1: (Ljava/lang/Object;IB)V +45 -1: (ILjava/lang/Object;)Ljava/util/HashMap$Node; +13 -1: applyAsDouble +83 -1: (Lsun/misc/URLClassPath$FileLoader;Ljava/lang/String;Ljava/net/URL;Ljava/io/File;)V +19 -1: MethodAccessor.java +9 -1: WALL_TIME +7 -1: INVOKES +13 -1: java.ext.dirs +9 -1: getStatic +56 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String; +42 -1: (Ljava/util/function/UnaryOperator<TE;>;)V +27 -1: java/lang/Class$MethodArray +10 -1: H_USERINFO +19 -1: PostVMInitHook.java +7 -1: running +32 -1: Warning: passing argument as-is +13 -1: EntryIterator +22 -1: NF_checkSpreadArgument +46 -1: ([DLjava/util/function/IntToDoubleFunction;I)V +7 -1: .<init> +13 -1: mappingLength +20 -1: implOnMalformedInput +53 -1: (Ljava/lang/String;ILjava/lang/reflect/Executable;I)V +26 -1: cannot make variable arity +18 -1: SharedSecrets.java +27 -1: (Ljava/io/InputStream;IZ)[B +9 -1: Asia/Gaza +55 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>; +5 -1: NTLM +19 -1: defaultCenturyStart +18 -1: addElapsedTimeFrom +38 -1: (Lsun/misc/Cleaner;)Lsun/misc/Cleaner; +44 -1: (Ljava/io/OutputStream;ZLjava/lang/String;)V +22 -1: (Ljava/lang/Object;B)V +6 1: [LBar; +7 -1: classes +23 -1: java/net/URLClassLoader +17 -1: sun/misc/Launcher +163 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +12 -1: updateAndGet +90 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V +9 -1: increment +27 -1: (Ljava/lang/CharSequence;)V +16 -1: Ljava/io/Reader; +27 -1: java/io/PushbackInputStream +6 -1: (JFZ)V +17 -1: getAppClassLoader +35 -1: sun/reflect/generics/tree/Signature +9 -1: elementAt +27 -1: (Ljava/lang/CharSequence;)Z +10 -1: readDouble +37 -1: ([B)Ljava/nio/charset/CharsetEncoder; +46 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V +4 -1: park +36 -1: java/lang/NegativeArraySizeException +49 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Z +121 -1: <T:Ljava/lang/Object;U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;)Ljava/util/Comparator<TT;>; +101 -1: (Ljava/lang/annotation/Annotation;Ljava/lang/annotation/Annotation;)Ljava/lang/annotation/Annotation; +12 -1: .$|()[{^?*+\\ +6 -1: manRef +3 -1: 437 +15 -1: newStringUnsafe +15 -1: constantPoolOop +10 -1: getPackage +24 -1: FastCharsetProvider.java +18 -1: getAnnotationBytes +187 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class<*>;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/security/AccessControlContext; +33 -1: Cannot suppress a null exception. +27 -1: sun/nio/cs/StandardCharsets +33 -1: (BB)Ljava/lang/invoke/MemberName; +61 -1: ([Ljava/lang/ClassValue$Entry;ILjava/lang/ClassValue$Entry;)I +10 -1: X-UTF-32LE +5 -1: toMap +89 -1: (Ljava/lang/Class<*>;Ljava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType; +46 -1: ([Ljava/lang/Object;IILjava/util/Comparator;)V +66 -1: ([Ljava/lang/reflect/Constructor;)[Ljava/lang/reflect/Constructor; +22 -1: ()Ljava/nio/ByteOrder; +20 -1: isMethodHandleInvoke +25 -1: sun/net/www/URLConnection +89 -1: Ljava/util/concurrent/ConcurrentHashMap<Ljava/lang/String;Ljava/lang/invoke/LambdaForm;>; +49 -1: (Ljava/nio/charset/Charset;FFLjava/lang/String;)V +17 -1: MIN_LOW_SURROGATE +23 -1: AbstractCollection.java +19 -1: (Ljava/util/Date;)I +19 -1: (Ljava/util/Date;)J +12 -1: cldrdata.jar +4 -1: path +77 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;IILjava/lang/String;[B)V +6 -1: MS1250 +37 -1: setJavaSecurityProtectionDomainAccess +11 -1: isDelimiter +5 -1: char0 +6 -1: MS1251 +5 -1: char1 +16 -1: getJavaNetAccess +6 -1: MS1252 +6 -1: MS1253 +6 -1: MS1254 +51 -1: (Ljava/lang/Class;)Ljava/security/ProtectionDomain; +6 -1: MS1257 +93 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class<*>; +10 -1: access$300 +5 -1: (JZ)C +19 -1: (Ljava/util/Date;)Z +5 -1: (JZ)D +26 -1: java/lang/Character$Subset +10 -1: access$302 +5 -1: (JZ)F +9 -1: emptyList +5 -1: (JZ)I +5 -1: (JZ)J +23 -1: (Ljava/lang/Runnable;)V +27 -1: java/lang/invoke/MemberName +29 -1: ()Ljava/util/Comparator<TT;>; +18 -1: retrieveDirectives +7 -1: ([F[F)Z +40 -1: (Lsun/misc/URLClassPath;Ljava/net/URL;)V +5 -1: (JZ)S +40 -1: (Ljava/util/function/IntUnaryOperator;)I +5 -1: (JZ)V +16 -1: parseAnnotations +21 -1: (C)Ljava/lang/String; +73 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(TK;TV;)Ljava/util/Map<TK;TV;>; +15 -1: charset encoder +17 -1: getDomainCombiner +9 -1: EmptyList +15 -1: java.vm.version +19 -1: getResourceAsStream +94 -1: Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<Ljava/lang/StringCoding$StringEncoder;>;>; +26 -1: java/util/HashMap$TreeNode +22 -1: (Ljava/util/HashMap;)V +23 -1: sun/misc/URLClassPath$1 +65 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;)Ljava/net/URLClassLoader; +20 -1: Hashtable Enumerator +23 -1: sun/misc/URLClassPath$2 +10 -1: Array.java +8 -1: FT_LIMIT +24 -1: ()[Ljava/lang/Throwable; +23 -1: sun/misc/URLClassPath$3 +14 -1: java/io/Writer +5 -1: chars +76 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/NavigableMap<TK;TV;>; +6 -1: final +5 -1: error +34 -1: java/lang/ApplicationShutdownHooks +29 -1: Lsun/launcher/LauncherHelper; +30 -1: ()Ljava/util/Enumeration<TE;>; +47 -1: (Ljava/util/List;)Ljava/lang/invoke/MethodType; +9 -1: scriptKey +5 -1: BYTES +12 -1: getException +69 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;)Ljava/lang/invoke/MethodType; +14 -1: Illegal Load: +189 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceValuesTask;Ljava/util/function/BiFunction;)V +66 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToLongTask +29 -1: getJavaIOFileDescriptorAccess +11 -1: toCodePoint +15 -1: setCreationTime +18 -1: NULL_CAUSE_MESSAGE +8 -1: elements +32 -1: ()Ljava/nio/charset/CoderResult; +8 -1: utf-32be +7 -1: addDate +4 -1: Cast +23 -1: sun/misc/JavaLangAccess +8 -1: 0{1,12}$ +27 -1: (Ljava/util/ArrayList;III)V +11 -1: lastIndexOf +14 -1: getCodeSources +53 -1: (Lsun/util/calendar/CalendarDate;Ljava/lang/String;)V +17 -1: cachedConstructor +7 -1: forName +74 -1: (Ljava/util/function/ToLongFunction;Ljava/lang/Object;Ljava/lang/Object;)I +47 -1: (Ljava/lang/Object;)Lsun/reflect/FieldAccessor; +8 -1: getDebug +18 -1: currentClassLoader +49 -1: Illegal leading minus sign on unsigned string %s. +20 -1: toLowerCaseCharArray +38 -1: java/util/concurrent/ConcurrentHashMap +8 -1: isHidden +92 -1: (Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;)V +29 -1: java/lang/ArithmeticException +26 -1: (Ljava/io/OutputStream;Z)V +66 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/RuntimeException; +38 -1: Ljava/util/Map<Ljava/lang/String;TT;>; +16 -1: getFindClassTime +34 -1: ([J)Ljava/util/Spliterator$OfLong; +24 -1: UnmodifiableNavigableSet +32 -1: java/lang/ClassNotFoundException +3 -1: \\n +6 -1: SEALED +14 -1: Flushable.java +3 -1: HST +24 -1: (Ljava/lang/Object;JII)Z +13 -1: toSecondOfDay +16 -1: thenComparingInt +26 -1: java/lang/NoSuchFieldError +18 -1: java/util/Locale$1 +25 -1: [Ljava/util/HashMap$Node; +75 -1: ([Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +11 -1: x-mswin-936 +32 -1: java/lang/management/MemoryUsage +25 -1: (JS)Ljava/nio/ByteBuffer; +25 -1: java/lang/ref/Finalizer$1 +25 -1: java/lang/ref/Finalizer$2 +21 -1: (Ljava/util/BitSet;)V +25 -1: java/lang/ref/Finalizer$3 +83 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;Ljava/util/Comparator<-TT;>;)TT; +24 -1: sun/nio/ch/Interruptible +21 -1: (Ljava/util/BitSet;)Z +72 -1: ([Ljava/security/ProtectionDomain;Ljava/security/AccessControlContext;)V +54 -1: Ljava/util/AbstractSet<Ljava/util/Map$Entry<TK;TV;>;>; +34 -1: getConstructorParameterAnnotations +4 -1: name +92 -1: <E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/lang/Comparable<TE;>;Ljava/io/Serializable; +11 -1: FORM_OFFSET +13 -1: getAliasTable +5 -1: (DD)D +23 -1: reflectionFactoryAccess +221 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V +59 -1: (Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path; +6 -1: DELETE +10 -1: returnType +5 -1: (DD)I +51 -1: ()Lsun/reflect/generics/repository/FieldRepository; +8 -1: delegate +12 -1: OTHER_LETTER +18 -1: getTransitionIndex +3 -1: HUP +10 -1: (IIII[CI)V +10 -1: ISO_8859-1 +17 -1: ArrayEncoder.java +10 -1: ISO_8859-2 +34 -1: java/lang/reflect/AnnotatedElement +10 -1: ISO_8859-4 +27 -1: sun/nio/cs/UTF_16LE$Decoder +10 -1: ISO_8859-5 +13 -1: prefetchWrite +9 -1: getFloatB +10 -1: ISO_8859-7 +37 -1: (I)Ljava/lang/invoke/LambdaForm$Name; +10 -1: ISO_8859-9 +10 -1: getActions +11 -1: negateExact +10 -1: isAbstract +9 -1: getFloatL +29 -1: java/lang/ClassValue$Identity +14 -1: java/io/Reader +8 -1: getOwner +24 -1: java/lang/AssertionError +17 -1: MethodHandle.java +19 -1: classRedefinedCount +10 -1: cachedYear +15 -1: getAndIncrement +26 -1: java.protocol.handler.pkgs +14 -1: cleanSomeSlots +27 -1: java/util/Spliterator$OfInt +31 -1: getRawExecutableTypeAnnotations +20 -1: ensureInitialization +7 -1: os.arch +57 -1: (Ljava/security/cert/CertPath;Ljava/security/Timestamp;)V +21 -1: UNSAFE_COPY_THRESHOLD +20 -1: toUnsignedBigInteger +82 -1: (Ljava/util/concurrent/locks/Condition;)Ljava/util/Collection<Ljava/lang/Thread;>; +15 -1: Reflection.java +12 -1: decryptBlock +3 -1: \\r +8 -1: newArray +8 -1: Category +36 -1: java/lang/reflect/GenericDeclaration +22 -1: (Ljava/lang/String;Z)V +8 -1: suspend0 +10 -1: getSigners +22 -1: (Ljava/lang/String;Z)Z +31 -1: Unable to create temporary file +117 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;Ljava/lang/ClassValue$Entry<TT;>;)Ljava/lang/ClassValue$Entry<TT;>; +17 -1: channelsAvailable +9 -1: Date.java +13 -1: toIndex < 0: +18 -1: mark > position: ( +11 -1: loadConvert +4 -1: july +42 -1: (Ljava/math/BigInteger;)Ljava/lang/String; +6 -1: enable +47 -1: (Ljava/util/zip/ZipEntry;)Ljava/io/InputStream; +6 -1: unpack +13 -1: setDayOfMonth +19 -1: name can't be empty +16 -1: getExtensionKeys +15 -1: getAndDecrement +36 -1: Ljava/lang/ClassValue$ClassValueMap; +38 -1: (Ljava/lang/Class;Ljava/lang/String;)V +11 -1: csISOlatin0 +9 -1: retention +23 -1: system protocol handler +9 -1: nullsLast +15 -1: refKindIsStatic +3 -1: (\n +48 -1: sun/launcher/LauncherHelper$ResourceBundleHolder +29 -1: ()Ljava/util/LinkedList<TE;>; +11 -1: csISOlatin9 +31 -1: ([CII)Ljava/lang/StringBuilder; +29 -1: (II)Ljava/lang/StringBuilder; +7 -1: pdcache +4 -1: june +12 -1: ;/?:@&=+$,[] +141 -1: ([Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)[Ljava/lang/invoke/LambdaForm$Name; +32 -1: ()[Ljava/util/WeakHashMap$Entry; +110 -1: (Ljava/lang/Class<*>;ILjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +81 -1: (Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z +46 -1: String value %s exceeds range of unsigned int. +6 -1: close0 +6 -1: (JDZ)V +160 -1: <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/reflect/GenericDeclaration;Ljava/lang/reflect/Type;Ljava/lang/reflect/AnnotatedElement; +12 -1: LinkedValues +24 -1: java/nio/HeapCharBufferR +23 -1: jvmVersionInfoAvailable +16 -1: classLoaderDepth +33 -1: (Lsun/nio/ch/DirectBuffer;IIIII)V +32 -1: java/nio/file/attribute/FileTime +50 -1: java/util/concurrent/ConcurrentHashMap$CounterCell +73 -1: (Lsun/misc/URLClassPath$JarLoader;Lsun/misc/JarIndex;)Lsun/misc/JarIndex; +60 -1: (Ljava/net/URL;Ljava/lang/String;)Ljava/security/CodeSource; +25 -1: Ljava/lang/ref/Finalizer; +12 -1: utf-32be-bom +40 -1: (Ljava/lang/String;ILjava/lang/Object;)V +9 -1: THROW_UCS +23 -1: java/util/AbstractMap$1 +23 -1: java/util/AbstractMap$2 +10 -1: x-utf-32be +4 -1: (S)B +60 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/LambdaForm; +20 -1: ResourceBundleHolder +4 -1: (S)I +13 -1: getSuppressed +17 -1: jdk_micro_version +4 -1: (S)J +9 -1: isNumeric +10 -1: variantKey +8 -1: utf-32le +47 -1: java/util/concurrent/ConcurrentHashMap$MapEntry +25 -1: ()Ljava/lang/Class<-TT;>; +6 -1: closed +4 -1: (S)S +15 -1: setStandardTime +10 -1: ShortCache +4 -1: (S)V +40 -1: sun/net/www/MessageHeader$HeaderIterator +17 -1: jdk_major_version +8 -1: FXHelper +6 -1: CENTIM +19 -1: java/security/Guard +46 -1: java.lang.invoke.MethodHandle.DUMP_CLASS_FILES +4 -1: ENUM +27 -1: Ljava/lang/SecurityManager; +39 -1: ([Ljava/lang/Class;[Ljava/lang/Class;)Z +11 -1: getFieldAt0 +12 -1: user.variant +28 -1: (Ljava/io/DataInputStream;)V +44 -1: ([JLjava/util/function/LongBinaryOperator;)V +7 -1: getRoot +3 -1: + +16 -1: identityHashCode +25 -1: java/security/Permissions +16 -1: Ljava/net/Proxy; +23 -1: java/io/ExpiringCache$1 +5 -1: more +10 -1: formatList +49 -1: (Ljava/lang/String;)Lsun/launcher/LauncherHelper; +29 -1: Relative path in absolute URI +11 -1: checkMapped +8 -1: Checksum +8 -1: " Radix: +9 -1: getAndAdd +9 -1: implReady +16 -1: SynchronizedList +30 -1: [Ljava/lang/StackTraceElement; +5 -1: right +13 -1: UTF_16BE.java +4 -1: HEAD +11 -1: isInvocable +6 -1: ENDCOM +15 -1: getPropertiesEx +6 -1: Unsafe +7 -1: IBM-819 +37 -1: : 0 <= i2 && i2 < names.length: 0 <= +19 -1: filterAndAddHeaders +22 -1: nativeParkEventPointer +18 -1: checkPositionIndex +13 -1: invalid url: +25 -1: out of range from input +9 -1: loadClass +12 -1: encodingName +9 -1: x-JIS0208 +38 -1: (Ljava/lang/Class;Ljava/lang/Object;)Z +28 -1: (I)Ljava/lang/reflect/Field; +17 -1: getJvmVersionInfo +6 -1: LIJFDV +21 -1: (D)Ljava/lang/String; +7 -1: oomeMsg +30 -1: java/io/InvalidObjectException +25 -1: java/io/FilterInputStream +32 -1: Ljava/net/ContentHandlerFactory; +13 -1: toUnsignedInt +17 -1: reconstitutionPut +37 -1: (Ljava/lang/Object;)Ljava/lang/Class; +14 -1: getContentType +43 -1: java/util/Collections$SynchronizedSortedSet +24 -1: (II)Ljava/nio/file/Path; +25 -1: JAVAFX_APPLICATION_MARKER +29 -1: (IC)Ljava/lang/StringBuilder; +13 -1: java/util/Set +10 -1: clearError +64 -1: (Ljava/lang/invoke/MethodType;[I)Ljava/lang/invoke/MethodHandle; +25 -1: java/io/FileInputStream$1 +8 -1: getFirst +36 -1: (Lsun/reflect/ConstructorAccessor;)V +84 -1: (Ljava/util/NavigableMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/NavigableMap; +42 -1: (Ljava/lang/CharSequence;)Ljava/io/Writer; +52 -1: ([Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +6 -1: (IFI)V +62 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Queue<TE;>; +17 -1: getHeaderFieldInt +16 -1: CheckedSortedMap +39 -1: (ZILjava/lang/String;)Ljava/lang/Class; +10 -1: getterName +10 -1: Asia/Tokyo +4 -1: Node +7 -1: rotate1 +13 -1: Stream closed +7 -1: rotate2 +9 -1: checkExec +17 -1: NF_checkExactType +18 -1: ReverseComparator2 +18 -1: arrayElementGetter +97 -1: (Ljava/util/ArrayPrefixHelpers$DoubleCumulateTask;Ljava/util/function/DoubleBinaryOperator;[DII)V +9 -1: iso8859-1 +9 -1: iso8859-2 +9 -1: iso8859-4 +9 -1: iso8859-5 +9 -1: iso8859-7 +16 -1: getPermissions +9 -1: iso8859-9 +9 -1: fromClass +17 -1: with modifiers " +8 -1: isBooted +24 -1: getCommonPoolParallelism +10 -1: initialize +47 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/Set<TT;>; +17 -1: checkElementIndex +14 -1: openConnection +47 -1: (Ljava/lang/Thread;Lsun/nio/ch/Interruptible;)V +12 -1: isAuthorized +17 -1: ReduceEntriesTask +7 -1: command +24 -1: ArithmeticException.java +24 -1: ensureOpenOrZipException +67 -1: (Lsun/util/calendar/CalendarDate;I)Lsun/util/calendar/CalendarDate; +39 -1: Ljava/lang/invoke/MethodHandles$Lookup; +31 -1: Enclosing constructor not found +34 -1: ()Lsun/util/calendar/BaseCalendar; +25 -1: (JLjava/lang/Object;JJJ)V +12 -1: > toIndex: +22 -1: LocaleObjectCache.java +19 -1: sun/misc/Launcher$1 +31 -1: java/util/HashMap$EntryIterator +8 -1: contains +60 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object; +53 -1: (I[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +19 -1: java/io/PrintStream +42 -1: java/lang/Math$RandomNumberGeneratorHolder +100 -1: <K:Ljava/lang/Object;>(I)Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;Ljava/lang/Boolean;>; +14 -1: getCapturedArg +14 -1: getCodeSigners +20 -1: mark() not supported +56 -1: (Lsun/misc/URLClassPath;I)Lsun/misc/URLClassPath$Loader; +20 -1: java/lang/StrictMath +14 -1: annotationType +3 -1: IET +4 -1: lang +13 -1: JarEntry.java +9 -1: ([CIIII)I +9 -1: canEncode +5 -1: extra +30 -1: java/lang/ref/ReferenceQueue$1 +37 -1: java/nio/channels/ReadableByteChannel +73 -1: (Ljava/util/Map$Entry<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>;)Z +24 -1: java/io/BufferedReader$1 +10 -1: x-utf-32le +16 -1: methodDescriptor +25 -1: (IJ)Ljava/nio/ByteBuffer; +18 -1: getSystemResources +46 -1: (Ljava/lang/String;I)Ljava/util/regex/Pattern; +46 -1: (Ljava/net/URL;)Lsun/misc/URLClassPath$Loader; +20 -1: calendars.properties +25 -1: implOnUnmappableCharacter +12 -1: signers_name +40 -1: (ZILjava/util/Locale;)Ljava/lang/String; +8 -1: (TE;)TE; +50 -1: ()Lsun/util/locale/provider/LocaleProviderAdapter; +11 -1: key is null +7 -1: encrypt +21 -1: millisUntilExpiration +55 -1: Unable to parse property sun.reflect.inflationThreshold +9 -1: checkExit +5 -1: SHORT +43 -1: java/util/Collections$UnmodifiableSortedSet +10 -1: ISO8859_15 +16 -1: verifyParameters +24 -1: buildAnnotatedInterfaces +15 -1: refKindIsSetter +45 -1: (JLjava/util/function/BiConsumer<-TK;-TV;>;)V +23 -1: (Ljava/lang/Object;IC)V +90 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue$Entry<TT;>;)Ljava/lang/ClassValue$Entry<TT;>; +30 -1: (Ljava/net/URL;)Ljava/net/URI; +60 -1: (BLjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;)V +43 -1: (Ljava/util/Collection;Ljava/lang/Object;)I +44 -1: (Ljava/net/URLConnection;)Ljava/lang/Object; +17 -1: Stream not marked +11 -1: targetCheck +127 -1: (Ljava/lang/Class<*>;ILjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName; +11 -1: debugString +112 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +43 -1: (Ljava/util/Collection;Ljava/lang/Object;)V +15 -1: NF_staticOffset +22 -1: CASE_INSENSITIVE_ORDER +6 -1: unpark +29 -1: (Ljava/lang/CharSequence;II)I +59 -1: Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>; +19 -1: defaultFormatLocale +7 -1: combine +58 -1: (Ljava/util/Locale$LocaleKey;)Lsun/util/locale/BaseLocale; +29 -1: (Ljava/lang/CharSequence;II)V +35 -1: sun/util/calendar/BaseCalendar$Date +57 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater; +75 -1: ([Ljava/lang/reflect/Member;[Ljava/lang/String;)[Ljava/lang/reflect/Member; +15 -1: MethodType_init +5 -1: (JF)V +20 -1: AUTOSELECT_FILTERING +12 -1: invokeStatic +18 -1: readFileDescriptor +22 -1: java/lang/Terminator$1 +72 -1: (Ljava/lang/Object;Ljava/util/function/UnaryOperator;)Ljava/lang/Object; +17 -1: EMPTY_STACK_TRACE +13 -1: isSamePackage +32 -1: ()Ljava/security/DomainCombiner; +10 -1: decodeLoop +30 -1: DIRECTIONALITY_EUROPEAN_NUMBER +112 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;)Ljava/lang/String; +33 -1: (Ljava/util/function/Predicate;)Z +35 -1: logincontext login context results +17 -1: sun/nio/cs/UTF_16 +13 -1: SingletonList +5 -1: end= +7 -1: getURLs +17 -1: traceInstructions +22 -1: generateCustomizedCode +9 -1: NO_CHANGE +11 -1: Number.java +49 -1: <T:Ljava/lang/Object;>(ITT;)Ljava/util/List<TT;>; +19 -1: checkSpecifyHandler +8 -1: setValue +30 -1: (Ljava/net/URL;)Ljava/net/URL; +22 -1: (Ljava/lang/Object;C)V +19 -1: Negative capacity: +24 -1: ArrayStoreException.java +52 -1: (Ljava/lang/StringBuffer;II)Ljava/lang/StringBuffer; +14 -1: linkMethodImpl +42 -1: java/util/InvalidPropertiesFormatException +4 -1: last +19 -1: getLocalizedMessage +65 -1: (Ljava/text/MessageFormat;[Ljava/lang/String;)[Ljava/lang/String; +61 -1: Ljava/lang/Object;Ljava/util/Enumeration<Ljava/lang/Object;>; +40 -1: (I[CII)Ljava/lang/AbstractStringBuilder; +27 -1: java.launcher.opt.datamodel +29 -1: (Ljava/lang/reflect/Method;)V +19 -1: SPECIFICATION_TITLE +123 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;ILjava/lang/Class;)Lsun/reflect/SerializationConstructorAccessorImpl; +32 -1: (I[CII)Ljava/lang/StringBuilder; +29 -1: (Ljava/lang/reflect/Method;)Z +23 -1: (Z[B)Ljava/lang/String; +27 -1: sun/nio/cs/Surrogate$Parser +32 -1: (Ljavax/security/auth/Subject;)Z +29 -1: ()Ljava/lang/invoke/Invokers; +7 -1: getPool +7 -1: textOut +12 -1: getEntryTime +14 -1: classModifiers +47 -1: (Ljava/util/Locale$Category;)Ljava/util/Locale; +32 -1: getInheritedAccessControlContext +4 -1: rcbt +37 -1: (Ljava/lang/Class<*>;Ljava/io/File;)Z +25 -1: AccessControlContext.java +22 -1: FileURLConnection.java +12 -1: NaturalOrder +34 -1: sun/util/calendar/CalendarSystem$1 +94 -1: ([Ljava/lang/reflect/Method;Ljava/lang/String;[Ljava/lang/Class<*>;)Ljava/lang/reflect/Method; +17 -1: No enum constant +7 -1: ([DII)V +8 -1: register +23 -1: FINAL_QUOTE_PUNCTUATION +27 -1: ACCESS_CLIPBOARD_PERMISSION +15 -1: verifyConstants +20 -1: (Ljava/io/Writer;I)V +45 -1: (Ljava/lang/String;)Ljava/lang/reflect/Field; +24 -1: linkMethodHandleConstant +43 -1: (Ljava/io/OutputStream;Ljava/lang/String;)V +125 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Comparator<-TV;>;)Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>; +14 -1: ALL_PERMISSION +12 -1: createObject +10 -1: CRC32.java +14 -1: reservedMemory +22 -1: ensureCapacityInternal +11 -1: FormatData_ +9 -1: maxMemory +27 -1: (I)Ljava/util/ListIterator; +8 -1: UTF-16BE +27 -1: AbstractSequentialList.java +10 -1: access$400 +16 -1: Locale settings: +10 -1: access$402 +12 -1: HashSet.java +24 -1: java/lang/Long$LongCache +30 -1: [Ljava/lang/reflect/Parameter; +11 -1: single_step +45 -1: (Ljava/lang/String;)Lsun/security/util/Debug; +97 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)Ljava/lang/invoke/SimpleMethodHandle; +16 -1: indexOfBangSlash +7 -1: region= +26 -1: ([BII)Ljava/lang/Class<*>; +8 -1: bitCount +3 -1: INT +67 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/function/IntFunction<+TT;>;)V +35 -1: newGetFloatIllegalArgumentException +11 -1: ([DII[DII)V +22 -1: makePreparedLambdaForm +3 -1: < +35 -1: sun/management/GarbageCollectorImpl +4 -1: (*)* +7 -1: getPort +18 -1: java/io/FileSystem +7 -1: getNode +38 -1: (Ljava/lang/Object;I)Ljava/lang/Class; +36 -1: $SwitchMap$java$util$Locale$Category +18 -1: securityCheckCache +5 -1: cdate +10 -1: childValue +19 -1: getMainClassFromJar +70 -1: <T:Ljava/lang/Enum<TT;>;>(Ljava/lang/Class<TT;>;Ljava/lang/String;)TT; +20 -1: unsuspendSomeThreads +29 -1: sun.classloader.findClassTime +11 -1: plusSeconds +3 -1: = +4 -1: Lock +7 -1: regions +38 -1: ()Ljava/lang/reflect/Constructor<TT;>; +25 -1: parseParameterAnnotations +20 -1: getSystemGMTOffsetID +33 -1: [Cc][Oo][Dd][Ee][Bb][Aa][Ss][Ee]= +37 -1: (Ljava/lang/String;I)Ljava/lang/Byte; +10 -1: permission +78 -1: (Ljava/lang/reflect/Constructor;)Lsun/reflect/generics/scope/ConstructorScope; +28 -1: (Lsun/misc/JavaLangAccess;)V +26 -1: GET_CLASSLOADER_PERMISSION +24 -1: (J)Ljava/nio/ByteBuffer; +20 -1: getUnresolvedActions +49 -1: (I[BIILsun/security/util/ManifestEntryVerifier;)V +5 -1: (ZJ)V +14 -1: isClassOnlyJar +35 -1: ()[Ljava/util/HashMap$Node<TK;TV;>; +23 -1: ()Ljava/util/SortedMap; +29 -1: HistoricallyNamedCharset.java +30 -1: no leading reference parameter +8 -1: csCESU-8 +3 -1: > +13 -1: invoke_LLLL_L +109 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;+TV;>;)Ljava/util/NavigableMap<TK;TV;>; +13 -1: invoke_LLLL_V +46 -1: (Ljava/lang/Class;Z)[Ljava/lang/reflect/Field; +5 -1: offer +12 -1: isoLanguages +61 -1: (Ljava/io/OutputStream;Ljava/lang/String;Ljava/lang/String;)V +44 -1: sun/reflect/BootstrapConstructorAccessorImpl +12 -1: BaseIterator +58 -1: (IZ[Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/String; +23 -1: initializeOSEnvironment +62 -1: ([Ljava/security/cert/Certificate;)[Ljava/security/CodeSigner; +3 -1: >> +13 -1: searchEntries +7 -1: setDate +3 -1: red +3 -1: ref +10 -1: EMPTY_LIST +3 -1: rem +78 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/List<TE;>; +9 -1: scanToken +6 -1: greek8 +9 -1: basicType +12 -1: getFromClass +43 -1: averageCharsPerByte exceeds maxCharsPerByte +8 -1: isDaemon +7 -1: nonNull +17 -1: Invalid default: +45 -1: (Lsun/misc/URLClassPath;Ljava/lang/String;Z)V +18 -1: java/lang/String$1 +11 -1: copyMethods +15 -1: sun/misc/Signal +5 -1: float +18 -1: StreamDecoder.java +19 -1: no such constructor +14 -1: bad arity for +14 -1: divideUnsigned +10 -1: CODING_END +3 -1: IST +14 -1: HeaderIterator +9 -1: september +20 -1: makeVarargsCollector +6 -1: L_PATH +45 -1: (Ljava/lang/String;)Ljava/io/File$PathStatus; +24 -1: URI scheme is not "file" +85 -1: (Ljava/lang/Class<TT;>;Ljava/lang/Class<TV;>;Ljava/lang/String;Ljava/lang/Class<*>;)V +27 -1: java/util/function/Supplier +17 -1: checkedCollection +8 -1: MapEntry +81 -1: (Ljava/lang/invoke/MethodHandle;ILjava/util/List;)Ljava/lang/invoke/MethodHandle; +34 -1: java/security/ProtectionDomain$Key +14 -1: List length = +39 -1: ([Ljava/lang/Object;)Ljava/lang/String; +87 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +7 -1: unparse +28 -1: jarFileHasClassPathAttribute +40 -1: (Lsun/misc/JavaIOFileDescriptorAccess;)V +30 -1: (Ljava/lang/reflect/Field;ZZ)V +22 -1: GetPropertyAction.java +8 -1: RUNNABLE +10 -1: exprString +13 -1: getAnnotation +19 -1: class can't be null +22 -1: defaultAssertionStatus +8 -1: getName0 +16 -1: quoteReplacement +15 -1: getMemberVMInfo +42 -1: (Ljava/lang/Class<*>;)Ljava/lang/Class<*>; +16 -1: cachedLambdaForm +16 -1: addFinalRefCount +12 -1: getMethodAt0 +8 -1: ([ZII)[Z +44 -1: (Ljava/lang/Object;TV;Ljava/lang/Object;)TV; +4 -1: [TT; +29 -1: Ljava/lang/invoke/LambdaForm; +28 -1: java/util/DualPivotQuicksort +61 -1: ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>; +17 -1: registerDirectory +8 -1: jarFiles +69 -1: (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String; +54 -1: [Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +15 -1: getDefaultValue +39 -1: sun/util/calendar/ZoneInfoFile$Checksum +20 -1: DISABLE_JAR_CHECKING +12 -1: CONTENT_TYPE +46 -1: array type not assignable to trailing argument +60 -1: <T:Ljava/lang/Object;>([TT;II)Ljava/util/stream/Stream<TT;>; +47 -1: java.lang.invoke.MethodHandle.COMPILE_THRESHOLD +9 -1: toInstant +152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/NavigableMap<TK;TV;>; +7 -1: L_ALPHA +29 -1: java/lang/AbstractMethodError +29 -1: java/util/jar/Attributes$Name +15 -1: LOCALE_SETTINGS +19 -1: (J)Ljava/lang/Long; +4 -1: jar: +17 -1: ensureInitialized +13 -1: LAST_MODIFIED +40 -1: ([Ljava/lang/String;)[Ljava/lang/String; +7 -1: shuffle +70 -1: (Lsun/util/locale/LanguageTag;)Lsun/util/locale/InternalLocaleBuilder; +19 -1: isPackageAccessible +17 -1: compileToBytecode +11 -1: getPackages +237 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V +53 -1: java/util/concurrent/ConcurrentHashMap$KeySpliterator +26 -1: setURLStreamHandlerFactory +47 -1: access print all checkPermission results +22 -1: sun/reflect/MethodInfo +75 -1: (Ljava/lang/String;ZLjava/util/Set<Ljava/lang/String;>;)Lsun/misc/Resource; +6 -1: KOI8-R +14 -1: Character.java +5 -1: (SS)I +8 -1: unshared +73 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class; +19 -1: replacementTreeNode +10 -1: BA_REGULAR +8 -1: UTF-16LE +44 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)V +22 -1: InputStreamReader.java +17 -1: getInvocationType +23 -1: getDeclaredConstructors +48 -1: [Lsun/reflect/generics/tree/FormalTypeParameter; +67 -1: (Ljava/util/Comparator;Ljava/util/Map$Entry;Ljava/util/Map$Entry;)I +6 -1: koi8_r +14 -1: ofTotalSeconds +16 -1: content-encoding +6 -1: koi8_u +10 -1: getEncoded +6 -1: ()[TT; +43 -1: ([ILjava/util/function/IntUnaryOperator;I)V +18 -1: getSecurityContext +13 -1: LF_GEN_LINKER +78 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class;)Ljava/lang/invoke/MemberName; +49 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;II)V +19 -1: LinkedEntryIterator +23 -1: Warning: JIT compiler " +7 -1: static +100 -1: (Ljava/lang/Class;ZLjava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/util/List; +79 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;)Ljava/util/Collection<TT;>; +10 -1: iso-ir-100 +10 -1: iso-ir-101 +13 -1: toUpperString +31 -1: ()Ljava/util/Spliterator$OfInt; +43 -1: Ljava/lang/StringIndexOutOfBoundsException; +19 -1: Lsun/misc/JarIndex; +7 -1: Version +90 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class; +10 -1: getHandler +45 -1: (ILjava/lang/Object;)Ljava/lang/StringBuffer; +28 -1: getDeclaredAnnotationsByType +46 -1: ([Ljava/lang/Class;Ljava/lang/StringBuilder;)V +53 -1: ()Ljava/util/Enumeration<Ljava/security/Permission;>; +15 -1: addAllNonStatic +10 -1: iso-ir-110 +14 -1: Using VM: +17 -1: casReflectionData +26 -1: (Lsun/util/PreHashedMap;)I +24 -1: removeByNameAndSignature +4 -1: OS X +85 -1: (Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Z +16 -1: JarEntryIterator +38 -1: Ljava/lang/Class<Ljava/lang/Integer;>; +37 -1: Ljava/lang/Class<Ljava/lang/Double;>; +28 -1: ([Ljava/util/HashMap$Node;)V +34 -1: java/lang/reflect/GenericArrayType +14 -1: annotationData +26 -1: (Lsun/util/PreHashedMap;)V +22 -1: checkPackageDefinition +30 -1: ACCUMULATED_DAYS_IN_MONTH_LEAP +12 -1: lowestOneBit +64 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;)V +16 -1: asReadOnlyBuffer +11 -1: getRealName +17 -1: StringCoding.java +10 -1: iso-ir-126 +11 -1: isSurrogate +8 -1: setError +138 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>(Ljava/util/function/Function<-TT;+TU;>;Ljava/util/Comparator<-TU;>;)Ljava/util/Comparator<TT;>; +8 -1: (TK;)TK; +4 -1: java +136 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;)TT; +36 -1: ()Lsun/util/locale/LocaleExtensions; +12 -1: doubleStream +28 -1: ()Ljava/util/SimpleTimeZone; +7 -1: :@&=+$, +94 -1: Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<Ljava/lang/StringCoding$StringDecoder;>;>; +64 -1: (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; +19 -1: getSystemTimeZoneID +18 -1: ReentrantLock.java +8 -1: emptyMap +16 -1: getSavedProperty +249 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V +14 -1: KeySpliterator +13 -1: findResources +14 -1: forWrapperType +8 -1: floorMod +12 -1: isoCountries +10 -1: CheckedSet +21 -1: AbstractCalendar.java +12 -1: IS_INVOCABLE +45 -1: (Ljava/lang/Class;)Lsun/reflect/ConstantPool; +19 -1: checkSecurityAccess +13 -1: Invalid index +28 -1: STACK_TRACE_ELEMENT_SENTINEL +88 -1: (ILjava/lang/Object;Ljava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$TreeNode; +130 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V +14 -1: aliases_IBM437 +10 -1: iso-ir-144 +10 -1: iso-ir-148 +35 -1: ()Ljava/nio/charset/CharsetDecoder; +95 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +16 -1: UnmodifiableList +40 -1: ()Ljava/util/concurrent/locks/Condition; +9 -1: Path.java +80 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/util/Map; +36 -1: Ljava/lang/Class<Ljava/lang/Float;>; +124 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;)Ljava/lang/Object; +20 -1: privateGetParameters +24 -1: sun/nio/cs/StreamDecoder +12 -1: getFreeSpace +8 -1: US-ASCII +22 -1: negativeZeroDoubleBits +9 -1: putObject +13 -1: linkToVirtual +35 -1: Ljava/lang/Class<Ljava/lang/Long;>; +15 -1: detectedCharset +26 -1: java/lang/reflect/Modifier +22 -1: JAVAFX_LAUNCH_MODE_JAR +32 -1: java/net/URLStreamHandlerFactory +7 -1: IBM-923 +80 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/String; +13 -1: TRANSFERINDEX +34 -1: (Lsun/nio/cs/StandardCharsets$1;)V +23 -1: ()Ljava/io/InputStream; +16 -1: ()Ljava/io/File; +41 -1: (Ljava/lang/String;)Ljava/nio/ByteBuffer; +22 -1: sun/reflect/Reflection +23 -1: java/lang/AutoCloseable +25 -1: BootstrapMethodError.java +19 -1: EnclosingMethodInfo +13 -1: transferIndex +18 -1: java/lang/Compiler +4 -1: zero +29 -1: java/util/Arrays$NaturalOrder +9 -1: language= +37 -1: Ljava/util/ArrayList<Ljava/net/URL;>; +73 -1: ()Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject; +16 -1: balanceInsertion +82 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V +11 -1: asIntBuffer +27 -1: (Ljava/util/LinkedList;II)V +13 -1: ofEpochSecond +19 -1: sunjce_provider.jar +21 -1: (F)Ljava/lang/String; +32 -1: >> does not contain binding << +21 -1: declaredPublicMethods +5 -1: FIELD +17 -1: getPrimitiveClass +127 -1: (Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;Ljava/lang/Class;Ljava/lang/String;)V +21 -1: replaceParameterTypes +73 -1: Ljava/util/Map<Ljava/lang/Class<*>;Ljava/security/PermissionCollection;>; +21 -1: (Ljava/lang/Double;)I +5 -1: floor +4 -1: halt +14 -1: newConstructor +48 -1: (Ljava/util/function/BiFunction<-TK;-TV;+TV;>;)V +17 -1: packageAccessLock +15 -1: America/Phoenix +19 -1: Incoming arguments: +11 -1: V_Monotonic +14 -1: decrementExact +15 -1: updatePositions +14 -1: toLocaleString +12 -1: appendEscape +7 -1: DISPLAY +27 -1: sun/nio/cs/US_ASCII$Decoder +13 -1: LITTLE_ENDIAN +6 -1: isNull +13 -1: TempDirectory +6 -1: LM_JAR +39 -1: JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +10 -1: isCompiled +36 -1: (Ljava/lang/AbstractStringBuilder;)Z +3 -1: run +17 -1: START_PUNCTUATION +33 -1: Ljava/util/Stack<Ljava/net/URL;>; +49 -1: (Ljava/lang/String;)Ljava/lang/invoke/LambdaForm; +28 -1: java/security/DomainCombiner +53 -1: (Ljava/lang/String;Ljava/util/Map;)Ljava/time/ZoneId; +81 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)[Ljava/lang/reflect/AnnotatedType; +43 -1: java/lang/management/GarbageCollectorMXBean +11 -1: toTitleCase +12 -1: getHoldCount +4 -1: ()[B +4 -1: ()[C +4 -1: ()[J +20 -1: (Ljava/io/File;IZZ)Z +18 -1: fileTimeToUnixTime +23 -1: java/nio/HeapCharBuffer +30 -1: Ljava/lang/ref/ReferenceQueue; +6 -1: rt.jar +69 -1: (Ljava/util/function/ToIntFunction<-TT;>;)Ljava/util/Comparator<TT;>; +21 -1: java/lang/ThreadLocal +25 -1: Ljava/lang/reflect/Field; +44 -1: (Ljava/lang/String;Ljava/lang/Throwable;ZZ)V +93 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/MethodHandle; +22 -1: getDayOfWeekDateBefore +37 -1: [Lsun/reflect/generics/tree/TypeTree; +83 -1: <E:Ljava/lang/Object;>(Ljava/util/Map<TE;Ljava/lang/Boolean;>;)Ljava/util/Set<TE;>; +10 -1: readFields +42 -1: (Ljava/net/URL;)Ljava/security/CodeSource; +44 -1: (Ljava/lang/String;IIJ)Ljava/nio/ByteBuffer; +27 -1: Ljava/util/Collection<TE;>; +13 -1: checkResource +7 -1: rename0 +51 -1: (C)Ljava/lang/invoke/BoundMethodHandle$SpeciesData; +47 -1: sun/reflect/generics/repository/FieldRepository +11 -1: readBoolean +134 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)V +13 -1: getZoneOffset +17 -1: getJdkVersionInfo +21 -1: sun/misc/MessageUtils +23 -1: defaultAllowArraySyntax +31 -1: java/util/concurrent/locks/Lock +24 -1: java/lang/reflect/Method +7 -1: toUpper +17 -1: sun/misc/Signal$1 +51 -1: (JLjava/util/function/BiFunction<-TK;-TK;+TK;>;)TK; +28 -1: (Ljava/lang/ref/Reference;)Z +8 -1: nthreads +26 -1: MapReduceEntriesToLongTask +27 -1: (Ljava/util/NavigableSet;)V +10 -1: savedProps +25 -1: Lsun/security/util/Debug; +12 -1: CR_MALFORMED +13 -1: com.sun.proxy +17 -1: CharSequence.java +24 -1: (ILjava/lang/String;II)Z +13 -1: findNextValue +108 -1: <K::Ljava/lang/Comparable<-TK;>;V:Ljava/lang/Object;>()Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>; +5 -1: (FF)F +9 -1: typeClass +5 -1: (FF)I +11 -1: segmentMask +7 -1: (JJJJ)V +14 -1: aliases_CESU_8 +85 -1: (Ljava/lang/Class;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle; +15 -1: getCalendarDate +21 -1: getDeclaredAnnotation +8 -1: ([BIIB)I +15 -1: stripExtensions +14 -1: getISO3Country +5 -1: short +47 -1: String value %s exceeds range of unsigned long. +34 -1: ()Ljava/security/ProtectionDomain; +8 -1: ([BIIB)V +23 -1: (Ljava/lang/Object;ID)V +47 -1: (Ljava/lang/String;Ljava/nio/charset/Charset;)V +29 -1: RuntimeVisibleTypeAnnotations +24 -1: (Ljava/io/InputStream;)V +39 -1: (Ljava/lang/Class;Ljava/lang/String;Z)V +16 -1: convertPrimitive +8 -1: (TK;)TV; +24 -1: (Ljava/io/InputStream;)Z +6 -1: start +243 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V +9 -1: asSpecial +20 -1: java/text/Normalizer +17 -1: DAYS_0000_TO_1970 +9 -1: toCharset +20 -1: REPLACEALL_THRESHOLD +20 -1: sun/net/util/URLUtil +16 -1: findLoadedClass0 +14 -1: localedata.jar +6 -1: Parser +6 -1: start0 +4 -1: hash +83 -1: (Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +29 -1: Ljava/lang/invoke/MemberName; +8 -1: BOOT_TAG +22 -1: MH_LINKER_ARG_APPENDED +14 -1: comparingByKey +47 -1: ([Ljava/lang/String;)Ljava/lang/ProcessBuilder; +6 -1: start= +18 -1: StringBuilder.java +7 -1: getLong +12 -1: copyElements +16 -1: highResFrequency +11 -1: toGMTString +10 -1: ISO_8859_1 +10 -1: ISO_8859_2 +6 -1: result +10 -1: ISO_8859_4 +10 -1: ISO_8859_5 +10 -1: ISO_8859_7 +15 -1: unmodifiableSet +10 -1: ISO_8859_9 +25 -1: NoClassDefFoundError.java +42 -1: (Ljava/lang/String;)Ljava/util/LinkedList; +14 -1: parallelPrefix +22 -1: ARRAY_LONG_INDEX_SCALE +6 -1: resume +36 -1: Ljava/lang/invoke/LambdaForm$Hidden; +50 -1: java/util/Collections$UnmodifiableRandomAccessList +130 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Consumer;)V +6 -1: getInt +13 -1: getCachedYear +21 -1: CONNECTOR_PUNCTUATION +73 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;ILjava/lang/Class;)V +24 -1: [Lsun/util/calendar/Era; +22 -1: (Ljava/lang/Object;D)V +74 -1: (Ljava/security/AccessControlContext;)Ljava/security/AccessControlContext; +109 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Ljava/lang/Class$AnnotationData;Ljava/lang/Class$AnnotationData;)Z +6 -1: ([CZ)V +74 -1: (Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node; +19 -1: TRADITIONAL_CHINESE +125 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;[Ljava/lang/StackTraceElement;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V +18 -1: unknown protocol: +57 -1: (Ljava/lang/reflect/Method;Lsun/reflect/MethodAccessor;)V +13 -1: writeComments +9 -1: Negotiate +14 -1: Closeable.java +10 -1: asSpreader +122 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;[Ljava/nio/file/WatchEvent$Modifier;)Ljava/nio/file/WatchKey; +17 -1: jvm_minor_version +9 -1: getDouble +25 -1: Ljava/io/File$PathStatus; +19 -1: averageCharsPerByte +22 -1: getConstructorAccessor +61 -1: (Ljava/lang/String;)Ljava/lang/management/MemoryManagerMBean; +45 -1: (Ljava/lang/String;)Ljava/util/regex/Pattern; +25 -1: (JC)Ljava/nio/ByteBuffer; +20 -1: exclusiveOwnerThread +85 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;Ljava/lang/ClassValue$Entry<TT;>;)V +17 -1: getExtensionValue +14 -1: getLoadAverage +17 -1: GET_PD_PERMISSION +6 -1: METHOD +23 -1: sun/nio/cs/ISO_8859_1$1 +23 -1: (I[Ljava/lang/Object;)I +4 1: zzz1 +13 -1: createWrapper +4 1: zzz2 +4 1: zzz3 +33 -1: greater than Character.MAX_RADIX +37 -1: DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE +6 -1: BRIDGE +20 -1: canonicalizeLanguage +13 -1: setPermission +46 -1: (Ljava/io/OutputStream;)Ljava/io/OutputStream; +3 -1: 646 +14 -1: java/lang/Long +55 -1: java/util/concurrent/ConcurrentHashMap$SearchValuesTask +38 -1: (IC)Ljava/lang/invoke/LambdaForm$Name; +37 -1: (Ljava/lang/Class;)Ljava/lang/String; +17 -1: javaUtilJarAccess +23 -1: registerMethodsToFilter +34 -1: (Ljava/nio/charset/Charset;[CII)[B +11 -1: % VERSION 2 +37 -1: ([DI)Ljava/util/Spliterator$OfDouble; +16 -1: Stack Size: +52 -1: (Ljava/lang/Class;)Ljava/lang/annotation/Annotation; +17 -1: putDoubleVolatile +10 -1: access$500 +10 -1: access$502 +28 -1: malformed input around byte +13 -1: LF_INVSPECIAL +32 -1: Non-positive averageCharsPerByte +14 -1: NF_fieldOffset +10 -1: access$508 +48 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/List<TT;>; +17 -1: defaultReadObject +17 -1: java/util/TimSort +13 -1: resolveClass0 +92 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +20 -1: setLangReflectAccess +30 -1: java/lang/reflect/TypeVariable +56 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/Spliterator<TT;>; +8 -1: VOLATILE +10 -1: Big5-HKSCS +23 -1: (Ljava/util/Locale$1;)V +15 -1: ISO_8859-1:1987 +14 -1: no such method +10 -1: null value +8 -1: checkURL +22 -1: ARRAY_BYTE_INDEX_SCALE +27 -1: (Ljava/lang/ClassLoader;Z)V +22 -1: java/lang/reflect/Type +25 -1: java/net/JarURLConnection +36 -1: java/util/WeakHashMap$KeySpliterator +26 -1: ()Lsun/misc/JavaAWTAccess; +50 -1: (I[Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +9 -1: toDegrees +12 -1: lowSurrogate +19 -1: getEnclosingMethod0 +7 -1: bytearr +35 -1: (Ljava/util/List;Ljava/util/List;)I +25 -1: [Ljava/lang/reflect/Type; +34 -1: javaSecurityProtectionDomainAccess +21 -1: java.launcher.X.usage +37 -1: sun/util/locale/InternalLocaleBuilder +33 -1: ()Ljava/lang/invoke/MethodHandle; +3 -1: scl +19 -1: synthesizeAllParams +68 -1: Ljava/util/Map<Ljava/lang/String;[Ljava/security/cert/Certificate;>; +6 -1: vclass +35 -1: (Ljava/util/List;Ljava/util/List;)V +59 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Float;>; +13 -1: CallSite.java +10 1: Bar loaded +21 -1: ARRAY_INT_BASE_OFFSET +49 -1: Ljava/util/concurrent/ConcurrentHashMap$TreeNode; +32 -1: [Ljava/lang/reflect/Constructor; +4 -1: type +34 -1: <T:Ljava/lang/Object;>([TT;II)[TT; +25 -1: BufferedOutputStream.java +23 -1: java/util/zip/ZipFile$1 +24 -1: ()Ljava/lang/ClassValue; +50 -1: (Ljava/util/concurrent/CountedCompleter;[F[FIIII)V +16 -1: getQueuedThreads +26 -1: getJavaNetHttpCookieAccess +8 -1: setExtra +8 -1: implRead +20 -1: linkMethod => throw +76 -1: (Ljava/util/function/ToDoubleFunction;Ljava/lang/Object;Ljava/lang/Object;)I +11 -1: KeyIterator +39 -1: Ljava/util/List<Ljava/lang/Throwable;>; +3 -1: " " +36 -1: Ljava/lang/IllegalArgumentException; +11 -1: checkBounds +30 -1: sun/nio/cs/FastCharsetProvider +11 -1: toLongArray +107 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;IILjava/lang/String;[B)Ljava/lang/reflect/Field; +8 -1: (build +39 -1: (Ljava/nio/Buffer;ILjava/nio/Buffer;I)V +31 -1: [Ljava/lang/reflect/Executable; +3 -1: set +21 -1: PhantomReference.java +41 -1: java/util/Collections$CheckedNavigableMap +53 -1: Can not make a java.lang.Class constructor accessible +12 -1: ([CII[CIII)I +55 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Void;>; +12 -1: MICROSECONDS +11 -1: writeBuffer +52 -1: and domain that didn't have permission +37 -1: java/nio/channels/WritableByteChannel +26 -1: getRawClassTypeAnnotations +15 -1: putCharVolatile +33 -1: java/security/InvalidKeyException +19 -1: Ljava/io/Closeable; +83 -1: Lsun/util/locale/LocaleObjectCache<Ljava/util/Locale$LocaleKey;Ljava/util/Locale;>; +10 -1: SetFromMap +24 -1: JavaFX-Application-Class +13 -1: asShortBuffer +10 -1: getReifier +15 -1: isPositionIndex +18 -1: SignalHandler.java +3 -1: JST +28 -1: (Ljava/io/FileDescriptor;I)I +28 -1: getStackAccessControlContext +16 -1: updateByteBuffer +27 -1: ()Ljava/net/ContentHandler; +25 -1: (JD)Ljava/nio/ByteBuffer; +39 -1: ()Lsun/misc/JavaIOFileDescriptorAccess; +107 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +42 -1: sun/misc/PerfCounter$WindowsClientCounters +8 -1: addExact +28 -1: (Ljava/io/FileDescriptor;I)V +36 -1: ([Ljava/lang/String;)Ljava/util/Map; +21 -1: ()Ljava/lang/Process; +4 -1: UTF8 +5 -1: mkdir +10 -1: transient +3 -1: sgp +15 -1: balanceDeletion +161 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/net/URL;)Ljava/lang/Package; +15 -1: SynchronizedMap +40 -1: sun.misc.URLClassPath.disableJarChecking +92 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/Set<TE;>;Ljava/io/Serializable; +17 -1: removeEldestEntry +35 -1: (I)Ljava/lang/Class$AnnotationData; +24 -1: Ljava/util/Locale$Cache; +13 -1: STANDARD_TIME +17 -1: sun/nio/cs/MS1252 +99 -1: <K:Ljava/lang/Object;>()Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;Ljava/lang/Boolean;>; +23 -1: java/util/LinkedHashSet +9 -1: iso8859_1 +9 -1: iso8859_2 +9 -1: iso8859_4 +66 -1: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA; +9 -1: iso8859_5 +9 -1: iso8859_7 +9 -1: iso8859_9 +21 -1: Ljava/util/Formatter; +6 -1: isPath +21 -1: makeReferenceIdentity +24 -1: sun/net/ApplicationProxy +23 -1: ClassCastException.java +11 -1: MethodArray +12 -1: SingletonMap +41 -1: java/util/ArrayPrefixHelpers$CumulateTask +7 -1: seconds +20 -1: ClassRepository.java +14 -1: allocateMemory +24 -1: java.launcher.jar.error1 +24 -1: java.launcher.jar.error2 +24 -1: java.launcher.jar.error3 +45 -1: (Ljava/lang/String;Ljava/util/jar/Manifest;)Z +3 -1: sin +7 -1: (J[II)I +3 -1: Itr +18 -1: findBootstrapClass +10 -1: getElement +15 -1: ISO_8859-4:1988 +34 -1: newGetLongIllegalArgumentException +7 -1: pending +17 -1: isNotContinuation +6 -1: EXTSIG +13 -1: searchMethods +32 -1: Lsun/misc/JavaUtilZipFileAccess; +33 -1: ([Ljava/lang/reflect/Parameter;)V +31 -1: defaultUncaughtExceptionHandler +89 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind<*>;)Ljava/nio/file/WatchKey; +5 -1: ASCII +28 -1: ()Lsun/reflect/ConstantPool; +20 -1: isJavaIdentifierPart +6 -1: EXTSIZ +34 -1: Lsun/misc/JavaNetHttpCookieAccess; +34 -1: ClassLoader object not initialized +7 -1: CHECKED +16 -1: encodeBufferLoop +37 -1: (Ljava/time/Instant;)Ljava/util/Date; +24 -1: (Ljava/util/Map$Entry;)Z +50 -1: java/util/concurrent/ConcurrentHashMap$KeyIterator +31 -1: ()[Ljava/lang/ClassValue$Entry; +31 -1: java/lang/IllegalStateException +43 -1: (Ljava/lang/Appendable;Ljava/util/Locale;)V +6 -1: CENVEM +53 -1: Ljava/util/ArrayList<Lsun/misc/URLClassPath$Loader;>; +17 -1: getHeaderFieldKey +71 -1: (Ljava/lang/CharSequence;Ljava/text/Normalizer$Form;)Ljava/lang/String; +6 -1: CENVER +17 -1: cleanStaleEntries +9 -1: linkFirst +57 -1: (Ljava/util/Comparator<-TT;>;)Ljava/util/Comparator<TT;>; +8 -1: val$file +27 -1: Invalid parameter modifiers +6 -1: append +57 -1: ()Lsun/reflect/generics/repository/ConstructorRepository; +65 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToIntTask +39 -1: (Ljava/lang/String;)Ljava/lang/Integer; +25 -1: lambda$parallelSetAll$191 +25 -1: lambda$parallelSetAll$192 +25 -1: lambda$parallelSetAll$193 +23 -1: INSERTIONSORT_THRESHOLD +17 -1: java/time/Instant +25 -1: lambda$parallelSetAll$194 +14 -1: dynamicInvoker +9 -1: iso646-us +8 -1: position +29 -1: java/nio/channels/FileChannel +27 -1: java/util/stream/Collectors +64 -1: (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String; +10 -1: INDEX_NAME +15 -1: getCommentBytes +67 -1: (Ljava/io/FileOutputStream;Ljava/lang/String;)Ljava/io/PrintStream; +22 -1: privateGetPublicFields +32 -1: java/util/BitSet$1BitSetIterator +12 -1: PERF_MODE_RO +89 -1: ([Ljava/lang/ClassValue$Entry;ILjava/lang/ClassValue$Entry;Z)Ljava/lang/ClassValue$Entry; +30 -1: java/security/PrivilegedAction +18 -1: host can't be null +26 -1: package name can't be null +12 -1: PERF_MODE_RW +10 -1: isEnqueued +18 -1: argSlotToParameter +37 -1: (II)Ljava/lang/AbstractStringBuilder; +5 -1: tabAt +53 -1: (Ljava/lang/Object;)Ljava/lang/AbstractStringBuilder; +11 -1: PATH_OFFSET +18 -1: unicodebigunmarked +15 -1: ConditionObject +6 -1: KOREAN +13 -1: isNamePresent +24 -1: ()Ljava/lang/Class<TE;>; +14 -1: isStandardTime +8 -1: ([IIII)I +9 -1: WeakEntry +12 -1: javaIOAccess +17 -1: key can't be null +129 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/nio/file/Path;>;Ljava/lang/Iterable<Ljava/nio/file/Path;>;Ljava/nio/file/Watchable; +8 -1: handler +8 -1: ([IIII)V +10 -1: atBugLevel +18 -1: makeGuardWithCatch +18 -1: currentLoadedClass +11 -1: getCodeBase +67 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;)Ljava/util/List<TT;>; +12 -1: JarFile.java +19 -1: (C)Ljava/io/Writer; +22 -1: createURLStreamHandler +23 -1: sun/nio/cs/ArrayDecoder +13 -1: setAccessible +18 -1: stripOffParameters +101 -1: ([Ljava/security/ProtectionDomain;[Ljava/security/ProtectionDomain;)[Ljava/security/ProtectionDomain; +18 -1: Ljava/util/Random; +16 -1: Pacific/Honolulu +13 -1: useOldMapping +65 -1: (Ljava/lang/invoke/LambdaForm$NamedFunction;[Ljava/lang/Object;)V +14 -1: filterArgument +12 -1: LF_MH_LINKER +25 -1: isDirectMemoryPageAligned +49 -1: (Ljava/util/BitSet;)Ljava/util/function/Supplier; +54 -1: (Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;TV;)V +16 -1: java/time/ZoneId +4 -1: zfot +18 -1: isSameClassPackage +6 -1: julian +8 -1: (TT;)TT; +22 -1: java/util/jar/Manifest +7 -1: charOut +16 -1: getOffsetsByWall +19 -1: Illegal replacement +139 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/List<TE;>;Ljava/util/RandomAccess;Ljava/lang/Cloneable;Ljava/io/Serializable; +96 -1: <T:Ljava/lang/Object;>(Ljava/lang/reflect/Constructor<TT;>;)Ljava/lang/reflect/Constructor<TT;>; +15 -1: Annotation.java +24 -1: (Ljava/lang/Class<*>;)[B +6 -1: CODING +34 -1: ([Ljava/lang/ClassValue$Entry;II)V +6 -1: IGNORE +62 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; +29 -1: specificToGenericStringHeader +12 -1: helpTransfer +8 -1: fastTime +62 -1: (Ljava/net/URLConnection;[Ljava/lang/Class;)Ljava/lang/Object; +18 -1: Unhandled signal: +8 -1: isStrict +15 -1: ISO_8859-7:1987 +13 -1: getWeekLength +14 -1: jvmBuildNumber +40 -1: (Ljava/lang/String;)Ljava/util/Iterator; +6 -1: short0 +6 -1: short1 +73 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;)TT; +10 -1: removeNode +8 -1: setFloat +18 -1: cspc862latinhebrew +11 -1: setTimeZone +34 -1: java/lang/reflect/AccessibleObject +25 -1: MapReduceKeysToDoubleTask +25 -1: java/lang/ref/Reference$1 +24 -1: java/nio/HeapByteBufferR +15 -1: jdkMicroVersion +117 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B[B)V +8 -1: (TT;)TV; +16 -1: Permissions.java +22 -1: Ljava/util/Comparator; +17 -1: getDaylightSaving +25 -1: ([BIILjava/lang/String;)V +9 -1: stillborn +11 -1: maxPosition +28 -1: java/util/ArrayPrefixHelpers +73 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/SortedMap<TK;TV;>; +14 -1: useCanonCaches +5 -1: clean +16 -1: checkPermission2 +34 -1: sun.misc.launcher.useSharedArchive +13 -1: shutdownHooks +5 -1: clear +240 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V +67 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;)TU; +6 -1: cp1250 +6 -1: cp1251 +6 -1: cp1252 +13 -1: getZipMessage +6 -1: cp1253 +28 -1: (J)Ljava/lang/ref/Reference; +6 -1: cp1254 +54 -1: (ILjava/lang/String;)Ljava/lang/AbstractStringBuilder; +6 -1: cp1257 +10 -1: deepEquals +17 -1: WRITE_BUFFER_SIZE +13 -1: copyFromArray +40 -1: java/util/Collections$ReverseComparator2 +36 -1: sun/reflect/generics/visitor/Reifier +19 -1: averageBytesPerChar +13 -1: javaAWTAccess +6 -1: cp5346 +61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>; +6 -1: cp5347 +6 -1: cp5348 +6 -1: cp5349 +5 -1: field +23 -1: ()Ljava/nio/LongBuffer; +103 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/WrongMethodTypeException; +37 -1: (I)Ljava/lang/Character$UnicodeBlock; +11 -1: offsetAfter +79 -1: Ljava/util/HashMap<Ljava/security/CodeSource;Ljava/security/ProtectionDomain;>; +27 -1: invocationHandlerReturnType +17 -1: POSITIVE_INFINITY +11 -1: maybeRebind +3 -1: str +18 -1: setSecurityManager +9 -1: signature +18 -1: corrupted jar file +89 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;Ljava/io/Serializable; +87 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +6 -1: CENATT +6 -1: cp5350 +12 -1: AF_GETSTATIC +38 -1: (TE;Ljava/util/LinkedList$Node<TE;>;)V +8 -1: isLoaded +6 -1: CENATX +6 -1: cp5353 +18 -1: Africa/Addis_Ababa +35 -1: sun/usagetracker/UsageTrackerClient +11 -1: toUpperCase +22 -1: java/util/zip/Inflater +10 -1: iso_8859-1 +10 -1: iso_8859-2 +3 -1: sum +7 -1: x-Johab +10 -1: iso_8859-4 +10 -1: iso_8859-5 +85 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class;)Ljava/security/AccessControlContext; +11 -1: activeCount +49 -1: (Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Z +51 -1: (Ljava/util/List;Ljava/lang/Class;)Ljava/util/List; +10 -1: iso_8859-7 +19 -1: appendVmErgoMessage +10 -1: iso_8859-9 +14 -1: getClassLoader +6 -1: (CJJ)Z +15 -1: Lsun/misc/Perf; +7 -1: getTree +27 -1: Ljava/text/Normalizer$Form; +11 -1: ISO-2022-JP +69 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;)Ljava/lang/Object; +50 -1: java/lang/invoke/DirectMethodHandle$StaticAccessor +15 -1: fxLauncherClass +35 -1: (Ljava/net/URL;Ljava/lang/String;)V +16 -1: getAndAccumulate +35 -1: (Ljava/net/URL;Ljava/lang/String;)Z +32 -1: Non-positive averageBytesPerChar +35 -1: Ljava/lang/Class<Ljava/lang/Void;>; +15 -1: CLASS_MODIFIERS +12 -1: checkedQueue +13 -1: enumConstants +10 -1: getFactory +95 -1: Ljava/util/concurrent/ConcurrentMap<TK;Lsun/util/locale/LocaleObjectCache$CacheEntry<TK;TV;>;>; +13 -1: Africa/Harare +57 -1: (JLjava/util/TimeZone;)Lsun/util/calendar/Gregorian$Date; +11 -1: ISO-2022-KR +19 -1: $assertionsDisabled +13 -1: PROXY_PACKAGE +17 -1: copyFromLongArray +81 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/LinkedHashMap$Entry<TK;TV;>; +11 -1: checkDelete +38 -1: sun/management/ManagementFactoryHelper +7 -1: UTC1900 +20 -1: getBootstrapResource +23 -1: ()Ljava/lang/Throwable; +16 -1: CALLER_SENSITIVE +26 -1: checkSystemClipboardAccess +32 -1: Can't set default locale to NULL +16 -1: fxLauncherMethod +4 -1: >= +8 -1: provider +9 -1: Finalizer +78 -1: (Ljava/io/FileDescriptor;ZZZLjava/lang/Object;)Ljava/nio/channels/FileChannel; +13 -1: emptyIterator +15 -1: getZipFileCount +21 -1: isJavaIdentifierStart +9 -1: connected +11 -1: (ITK;TV;I)V +16 -1: America/Honolulu +22 -1: SynchronizedCollection +28 -1: java/util/zip/ZipConstants64 +29 -1: inheritedAccessControlContext +29 -1: ()[Ljava/security/CodeSigner; +85 -1: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/sun/management/GcInfo;)V +25 -1: (Ljava/nio/CharBuffer;Z)V +15 -1: java/io/Console +101 -1: (Ljava/lang/Class<*>;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z +9 -1: | resolve +81 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Ljava/lang/invoke/MemberName; +33 -1: (Ljava/nio/charset/Charset;FF[B)V +49 -1: (Ljava/lang/Class;Z)Ljava/lang/invoke/MethodType; +66 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File; +29 -1: ([Ljava/util/HashMap$Node;I)V +13 -1: filterMethods +4 -1: jcal +61 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)[Ljava/lang/Class<*>; +6 -1: which= +46 -1: (Ljava/math/BigInteger;)Ljava/math/BigInteger; +4 -1: date +18 -1: internalMemberName +6 -1: (JJI)Z +30 -1: [Ljava/lang/invoke/LambdaForm; +60 -1: <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object; +15 -1: ReservationNode +42 -1: java/lang/ThreadLocal$ThreadLocalMap$Entry +6 -1: setIn0 +4 -1: sort +8 -1: ibm00858 +110 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class; +28 -1: Ljava/lang/ClassValue$Entry; +22 -1: ensureExplicitCapacity +6 -1: rotate +14 -1: closeRequested +30 -1: ([CII)Ljava/lang/StringBuffer; +10 -1: LM_UNKNOWN +15 -1: Comparable.java +13 -1: getByteBuffer +9 -1: getScheme +15 -1: done with meta! +17 -1: checkForTypeAlias +7 -1: getKeys +7 -1: SIG_DFL +30 -1: Ljava/nio/charset/CoderResult; +16 -1: returnTypesMatch +19 -1: getClassAccessFlags +18 -1: JavaNioAccess.java +9 -1: setDouble +23 -1: Ljava/util/zip/ZipFile; +83 -1: (JLjava/util/function/ToIntFunction<-TK;>;ILjava/util/function/IntBinaryOperator;)I +11 -1: ACCESS_READ +15 -1: nativeByteOrder +5 -1: hours +7 -1: toArray +7 -1: Encoder +12 -1: resolveClass +29 -1: (Ljava/io/FileDescriptor;JJ)V +14 -1: redefinedCount +8 -1: getTotal +11 -1: iso_8859-13 +11 -1: iso_8859-15 +9 -1: expected +18 -1: getDeclaredMethods +11 -1: elementData +6 -1: intern +10 -1: countryKey +6 -1: setInt +39 -1: Could not create extension class loader +24 -1: SELF_SUPPRESSION_MESSAGE +14 -1: argToSlotTable +42 -1: Ljava/util/HashMap<TE;Ljava/lang/Object;>; +5 -1: \t\n\r\x0c +4 -1: read +12 -1: Objects.java +7 -1: aliases +29 -1: sun/reflect/LangReflectAccess +6 -1: prefix +15 -1: superInterfaces +10 -1: getDoInput +30 -1: java/nio/CharBufferSpliterator +6 -1: KOI8_R +12 -1: Asia/Kolkata +6 -1: KOI8_U +6 -1: LOCSIG +15 -1: UA-Java-Version +92 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap<TK;TV;>;Ljava/util/Map<TK;TV;>; +14 -1: CertificateRep +17 -1: getSystemResource +85 -1: (JLjava/util/function/ToLongFunction<-TV;>;JLjava/util/function/LongBinaryOperator;)J +27 -1: java/lang/reflect/Parameter +5 -1: quote +8 -1: not MH: +46 -1: java/util/Collections$UnmodifiableCollection$1 +6 -1: putVal +6 -1: LOCSIZ +6 -1: Atomic +3 -1: 737 +38 -1: java/lang/UnsupportedClassVersionError +27 -1: ()Ljava/lang/StringBuilder; +41 -1: sun/net/www/protocol/jar/JarURLConnection +60 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter; +59 -1: <T:Ljava/lang/Object;>(TT;TT;Ljava/util/Comparator<-TT;>;)I +54 -1: java/util/concurrent/locks/AbstractOwnableSynchronizer +7 -1: getHost +36 -1: (F)Ljava/lang/AbstractStringBuilder; +69 -1: <U:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;)Ljava/lang/Class<+TU;>; +4 -1: Form +103 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;+TV;>;)Ljava/util/SortedMap<TK;TV;>; +6 -1: spread +8 -1: addHours +13 -1: contentEquals +47 -1: (Ljava/lang/String;Ljava/security/Permission;)V +12 -1: newCondition +23 -1: (Ljava/lang/Object;IZ)V +26 -1: (Ljava/util/LinkedList;I)V +13 -1: ConstantValue +18 -1: URLConnection.java +12 -1: Boolean.java +75 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;Ljava/net/URLStreamHandlerFactory;)V +21 -1: EMPTY_THROWABLE_ARRAY +153 -1: (Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>;Ljava/lang/Class<*>;[Ljava/lang/String;)Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>; +18 -1: getUnresolvedCerts +13 -1: Negative time +28 -1: java/util/WeakHashMap$KeySet +8 -1: slashify +16 -1: isOtherLowercase +17 -1: putObjectVolatile +5 -1: ERASE +12 -1: filterFields +40 -1: Ljava/lang/ReflectiveOperationException; +12 -1: VM settings: +57 -1: (ILjava/lang/Object;)Ljava/util/HashMap$TreeNode<TK;TV;>; +10 -1: access$600 +11 -1: ] return => +13 -1: user.timezone +23 -1: USER_AGENT_JAVA_VERSION +27 -1: (Ljava/util/HashMap$Node;)V +19 -1: filterNTLMResponses +28 -1: (Lsun/misc/VMNotification;)V +37 -1: ()[[Ljava/lang/annotation/Annotation; +45 -1: ()Lcom/sun/management/DiagnosticCommandMBean; +3 -1: tan +31 -1: getDirectlyAndIndirectlyPresent +7 -1: prepend +35 -1: (I)Lsun/util/calendar/CalendarDate; +8 -1: val$dirs +4 -1: test +28 -1: Non-positive maxCharsPerByte +83 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<TK;TV;>; +12 -1: JAVA_VERSION +24 -1: ([Ljava/lang/Thread;IZ)I +70 -1: (Ljava/lang/invoke/LambdaForm$Name;)Ljava/lang/invoke/LambdaForm$Name; +57 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/Comparator<-TT;>;)V +42 -1: (Ljava/lang/Class<*>;[I)Ljava/lang/Object; +27 -1: java/lang/SecurityManager$1 +32 -1: java/security/SignatureException +27 -1: java/lang/SecurityManager$2 +4 -1: .jar +20 -1: parameterAnnotations +31 -1: DIRECTIONALITY_BOUNDARY_NEUTRAL +21 -1: hasClassPathAttribute +17 -1: checkParentAccess +35 -1: java/security/PermissionsEnumerator +6 -1: FORMAT +92 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;)TU; +3 -1: 775 +11 -1: PROBE_LIMIT +111 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater; +11 -1: AF_PUTFIELD +22 -1: (Ljava/lang/Object;Z)V +20 -1: getGenericReturnType +9 -1: val$extcl +13 -1: inClassLoader +37 -1: sun.urlClassLoader.readClassBytesTime +35 -1: (JLjava/util/function/BiConsumer;)V +28 -1: getContentHandlerPkgPrefixes +10 -1: getChannel +78 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;TT;Ljava/util/Comparator<-TT;>;)I +16 -1: parseMemberValue +4 -1: regn +47 -1: ([Ljava/lang/Object;III)Ljava/util/Spliterator; +11 -1: but found +6 -1: adjust +11 -1: isLowerCase +29 -1: sun/reflect/ReflectionFactory +98 -1: <E:Ljava/lang/Object;>(Ljava/util/SortedSet<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/SortedSet<TE;>; +18 -1: [Ljava/lang/Error; +5 -1: entry +14 -1: refreshVersion +8 -1: (IIIII)V +22 -1: unmodifiableCollection +6 -1: putAll +22 -1: offsetByCodePointsImpl +26 -1: (Ljava/lang/String;[BII)[C +46 -1: (Ljava/net/URLClassLoader;Ljava/lang/String;)V +4 -1: /LF= +9 -1: Bits.java +30 -1: [Ljava/util/WeakHashMap$Entry; +19 -1: getLastModifiedTime +44 -1: [Ljava/lang/Thread$UncaughtExceptionHandler; +11 -1: getZoneInfo +6 -1: lookup +19 -1: MapReduceValuesTask +18 -1: isVarargsCollector +38 -1: java/util/jar/JarFile$JarEntryIterator +11 -1: getJarIndex +9 -1: getByName +42 -1: (Ljava/lang/Object;JLjava/lang/Object;JJ)V +71 -1: (Ljava/lang/invoke/LambdaForm$Name;I)Ljava/lang/invoke/LambdaForm$Name; +30 -1: java/net/ContentHandlerFactory +54 -1: (Ljava/lang/Class<*>;I)Ljava/lang/invoke/MethodHandle; +12 -1: getSignature +9 -1: parseLong +25 -1: DEBUG_METHOD_HANDLE_NAMES +15 -1: runFinalization +13 -1: 0000000000000 +28 -1: ()[Ljava/lang/reflect/Field; +37 -1: ([Ljava/lang/ClassValue$Entry<*>;II)V +13 -1: gcInfoBuilder +64 -1: (JLjava/util/function/BiFunction;Ljava/util/function/Consumer;)V +5 -1: cnfe1 +8 -1: setShort +28 -1: (C)Ljava/lang/StringBuilder; +44 -1: (Ljava/nio/LongBuffer;)Ljava/nio/LongBuffer; +70 -1: (Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class<*>; +33 -1: java/lang/TypeNotPresentException +5 -1: \n +20 -1: acquireInterruptibly +21 -1: (I)Ljava/lang/String; +24 -1: (Ljava/io/PrintWriter;)V +16 -1: convertArguments +32 -1: Ljava/net/MalformedURLException; +15 -1: linkToInterface +39 -1: java/lang/Throwable$PrintStreamOrWriter +10 -1: iso8859_13 +13 -1: hasPrimitives +10 -1: iso8859_15 +145 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Class<*>;)Ljava/lang/invoke/CallSite; +8 -1: equalPDs +28 -1: (Ljava/io/FileDescriptor;J)V +7 -1: newLine +43 -1: (Ljava/lang/Class<*>;I)Ljava/lang/Class<*>; +8 -1: addEntry +30 -1: java/util/WeakHashMap$EntrySet +14 -1: USE_OLDMAPPING +39 -1: (Ljava/io/DataInput;)Ljava/lang/String; +12 -1: LF_EX_LINKER +27 -1: java/lang/invoke/MethodType +23 -1: JavaSecurityAccess.java +23 -1: isLocalOrAnonymousClass +19 -1: Expanded arguments: +18 -1: sun/nio/cs/Unicode +40 -1: ()Ljava/nio/charset/spi/CharsetProvider; +23 -1: ([BLjava/lang/String;)V +7 -1: default +13 -1: highestOneBit +9 -1: isDefault +28 -1: (IF)Ljava/lang/StringBuffer; +31 -1: ()Ljava/util/ListIterator<TE;>; +4 -1: base +23 -1: newPermissionCollection +7 -1: version +15 -1: Permission.java +41 -1: java/lang/invoke/LambdaForm$NamedFunction +8 -1: isQueued +24 -1: ([Ljava/lang/Class<*>;)I +64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToIntTask +16 -1: checkInitialized +37 -1: java/lang/ClassLoader$ParallelLoaders +5 -1: ([B)I +23 -1: Lsun/misc/URLClassPath; +9 -1: usr_paths +10 -1: Queue.java +43 -1: (Ljava/io/File;Ljava/nio/charset/Charset;)V +64 -1: (Ljava/lang/invoke/MethodTypeForm;)Ljava/lang/invoke/MemberName; +3 -1: tid +23 -1: JarIndex-Version: 1.0\n\n +33 -1: (I)Ljava/nio/charset/CoderResult; +5 -1: ([B)V +10 -1: isInstance +25 -1: unmappableCharacterAction +11 -1: queueLength +5 -1: ([B)Z +10 -1: freeMemory +47 -1: java/util/ArrayPrefixHelpers$DoubleCumulateTask +52 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)V +41 -1: Ljava/util/Collections$ReverseComparator; +16 -1: copyToShortArray +206 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;ILjava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)Z +38 -1: sun/launcher/LauncherHelper$SizePrefix +17 -1: ACCESS_PERMISSION +17 -1: ReverseComparator +30 -1: ()Ljava/lang/ClassValue$Entry; +17 -1: AF_PUTSTATIC_INIT +6 -1: (IIB)I +19 -1: java/nio/file/Files +35 -1: (Z)[Ljava/lang/reflect/Constructor; +20 -1: INVALID_FIELD_OFFSET +17 -1: initializeHeaders +10 -1: management +10 -1: targetType +61 -1: (Ljava/util/SortedSet;Ljava/lang/Class;)Ljava/util/SortedSet; +5 -1: ascii +8 -1: validate +78 -1: Ljava/util/concurrent/ConcurrentHashMap<Ljava/lang/String;Ljava/lang/Object;>; +25 -1: sun/nio/cs/UTF_16$Decoder +36 -1: sun/management/DiagnosticCommandImpl +24 -1: unmodifiableNavigableMap +18 -1: canonicalizeScript +29 -1: Lsun/misc/JavaSecurityAccess; +74 -1: ([JLjava/util/function/IntToLongFunction;)Ljava/util/function/IntConsumer; +38 -1: DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING +11 -1: languageTag +33 -1: java/lang/invoke/VolatileCallSite +18 -1: setRequestProperty +6 -1: 0x%02X +20 -1: (Ljava/lang/Float;)I +35 -1: (Ljava/nio/charset/CoderResult$1;)V +24 -1: (Ljava/lang/Object;JJB)V +21 -1: synchronizedSortedSet +59 -1: (Ljava/util/function/ToLongFunction;)Ljava/util/Comparator; +30 -1: av.length == arity: av.length= +7 -1: $VALUES +27 -1: RandomNumberGeneratorHolder +3 -1: tlr +43 -1: java/util/ArraysParallelSortHelpers$FJFloat +28 -1: ()[Ljava/io/File$PathStatus; +26 -1: invalid extra field length +13 -1: getExtensions +7 -1: PARAMS0 +7 -1: PARAMS1 +30 -1: [Ljava/lang/invoke/MemberName; +7 -1: PARAMS2 +31 -1: java/lang/AbstractStringBuilder +161 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiFunction;Ljava/util/function/Consumer;)V +4 -1: repl +19 -1: ()Ljava/lang/Class; +24 -1: BufferedInputStream.java +9 -1: sizeCache +8 -1: READLINK +9 -1: metaIndex +18 -1: getLocalizedObject +6 -1: filter +58 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V +140 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName; +24 -1: Ljava/util/HashMap$Node; +35 -1: (Lsun/nio/cs/FastCharsetProvider;)V +8 -1: dispatch +19 -1: sun.stderr.encoding +130 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;)Ljava/util/List<Ljava/lang/String;>; +51 -1: Ljava/util/concurrent/ConcurrentHashMap$ValuesView; +30 -1: sun.reflect.inflationThreshold +20 -1: MutableCallSite.java +26 -1: invokeWithArgumentsTracing +7 -1: getters +38 -1: ()[Ljava/lang/reflect/TypeVariable<*>; +46 -1: ([DLjava/util/function/DoubleBinaryOperator;)V +5 -1: klass +13 -1: publicMethods +37 -1: ()[Ljava/lang/reflect/Constructor<*>; +126 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle; +6 -1: FJLong +20 -1: canBeStaticallyBound +17 -1: getTimezoneOffset +9 -1: ALL_TYPES +3 -1: toV +8 -1: packages +15 -1: codePointBefore +10 -1: getCountry +13 -1: getDSTSavings +100 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetDecoder;)Lsun/nio/cs/StreamDecoder; +50 -1: java/util/ArraysParallelSortHelpers$FJFloat$Sorter +20 -1: hasNonVoidPrimitives +7 -1: syncAll +41 -1: domain dump all domains in context +59 -1: Ljava/util/Hashtable<Ljava/lang/Integer;Lsun/misc/Signal;>; +16 -1: ForEachEntryTask +18 -1: vminfoIsConsistent +26 -1: (ZLjava/util/Comparator;)V +9 -1: Lock.java +31 -1: Lsun/reflect/ReflectionFactory; +8 -1: (I[BII)I +23 -1: doesExtendFXApplication +87 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl +11 -1: windows-31j +28 -1: sun/misc/URLClassPath$Loader +17 -1: getEntryAfterMiss +47 -1: ([Ljava/lang/reflect/Field;Ljava/lang/String;)J +44 -1: Ljava/util/List<Ljava/security/Permission;>; +10 -1: openStream +31 -1: Ljava/net/UnknownHostException; +19 -1: bad reference kind +29 -1: ()Ljava/nio/MappedByteBuffer; +9 -1: H_UPALPHA +37 -1: (Ljava/util/function/UnaryOperator;)V +25 -1: FAKE_METHOD_HANDLE_INVOKE +4 -1: peek +25 -1: java/util/Hashtable$Entry +18 -1: getMemberRefInfoAt +37 -1: Ljava/util/WeakHashMap$Entry<TK;TV;>; +14 -1: resolvedHandle +27 -1: ()Ljava/util/Iterator<TV;>; +61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>; +6 -1: static +50 -1: (Ljava/net/URLClassLoader;)Lsun/misc/URLClassPath; +38 -1: (Ljava/lang/Class;)[Ljava/lang/Object; +41 -1: (Ljava/util/SortedSet;Ljava/lang/Class;)V +41 -1: java/lang/invoke/WrongMethodTypeException +5 -1: group +10 -1: readObject +13 -1: getParentFile +14 -1: daylightSaving +15 -1: eagerValidation +36 -1: (Ljava/io/File;)Lsun/misc/MetaIndex; +18 -1: reduceEntriesToInt +15 -1: INITIAL_ENTRIES +18 -1: AtomicInteger.java +7 -1: .length +128 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/CharBuffer;>;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/Readable; +6 -1: asList +21 -1: unmodifiableSortedSet +22 -1: ([B)Ljava/util/BitSet; +5 -1: check +64 -1: (Ljava/util/jar/JarFile;Lsun/misc/MetaIndex;)Lsun/misc/JarIndex; +35 -1: ()Ljava/nio/charset/CharsetEncoder; +4 -1: oome +25 -1: ()Lsun/misc/URLClassPath; +27 -1: (Ljava/io/FilePermission;)V +29 -1: IllegalArgumentException.java +17 -1: jvmSpecialVersion +21 -1: Ljava/util/ArrayList; +13 -1: packagePrefix +27 -1: (Ljava/io/FilePermission;)Z +11 -1: canonicalID +82 -1: Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;Ljava/io/Serializable; +24 -1: java.launcher.opt.footer +13 -1: NativeLibrary +37 -1: (Ljava/lang/String;J)Ljava/lang/Long; +16 -1: longBitsToDouble +6 -1: getKey +22 -1: (JLjava/lang/String;)V +14 -1: ensureCapacity +69 -1: (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +22 -1: java/lang/Thread$State +50 -1: ()Ljava/util/Iterator<Ljava/nio/charset/Charset;>; +4 -1: 7bit +46 -1: (Ljava/lang/Class<+Ljava/lang/ClassLoader;>;)Z +76 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)[Ljava/lang/invoke/LambdaForm$Name; +3 -1: ttb +12 -1: UTF_16LE_BOM +9 -1: remainder +40 -1: ()Lsun/reflect/generics/visitor/Reifier; +22 -1: [Ljava/lang/Throwable; +17 -1: EMPTY_ENUMERATION +13 -1: erasedInvoker +46 -1: Ljava/nio/charset/IllegalCharsetNameException; +10 -1: UnicodeBig +53 -1: ()Ljava/util/Map<Ljava/io/File;Lsun/misc/MetaIndex;>; +12 -1: MAX_EXPONENT +10 -1: Enumerator +15 -1: charset decoder +11 -1: AF_GETFIELD +12 -1: | getInvoker +74 -1: (Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult; +14 -1: toUnsignedLong +46 -1: java/lang/invoke/MethodHandleNatives$Constants +29 -1: java version "1.8.0-internal" +21 -1: ([Ljava/lang/Class;)I +16 -1: UPPERCASE_LETTER +22 -1: newConstantPerfCounter +6 -1: signum +9 -1: getField0 +38 -1: java/nio/charset/CoderMalfunctionError +21 -1: [Ljava/lang/Runnable; +11 -1: putIfAbsent +30 -1: java/util/Collections$EmptySet +22 -1: (I)[Ljava/lang/String; +34 -1: Ljava/security/SecurityPermission; +49 -1: ([Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +190 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceEntriesTask;Ljava/util/function/BiFunction;)V +22 -1: Not an annotation type +34 -1: java/io/ObjectInputStream$GetField +50 -1: (Lsun/reflect/FieldInfo;)Ljava/lang/reflect/Field; +32 -1: Ljava/lang/NoSuchFieldException; +70 -1: (Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object; +9 -1: mergeSort +77 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>; +21 -1: sun/nio/cs/ISO_8859_1 +8 -1: DST_MASK +21 -1: Ljava/lang/Throwable; +108 -1: (Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;I)Ljava/lang/Class$ReflectionData<TT;>; +32 -1: java/util/Arrays$LegacyMergeSort +59 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/Class<TT;>;>; +16 -1: parseUnsignedInt +36 -1: ([D)Ljava/util/Spliterator$OfDouble; +26 -1: SPECIFY_HANDLER_PERMISSION +13 -1: primitiveType +17 -1: threadStartFailed +21 -1: (J)Ljava/lang/String; +23 -1: setClassAssertionStatus +28 -1: java/util/Hashtable$EntrySet +28 -1: Non-positive maxBytesPerChar +19 -1: getApplicationClass +14 -1: SentinelHolder +15 -1: staticFieldBase +25 -1: setDefaultRequestProperty +66 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedValueTask +8 -1: threadID +9 -1: getFields +15 -1: LineNumberTable +38 -1: java/util/Collections$CheckedSortedSet +14 -1: jdkBuildNumber +6 -1: divide +46 -1: (Ljava/io/BufferedWriter;Ljava/lang/String;Z)V +20 -1: java/lang/Terminator +92 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/lang/String;Ljava/net/URL;Ljava/util/jar/JarEntry;)V +6 -1: force0 +10 -1: getThreads +34 -1: java/util/IllformedLocaleException +115 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/FieldRepository; +9 -1: testFlags +11 -1: getLanguage +36 -1: java/util/function/IntBinaryOperator +12 -1: Suppressed: +10 -1: isMandated +23 -1: MethodAccessorImpl.java +28 -1: (Ljava/util/zip/ZipEntry;)[B +27 -1: Ljava/util/Hashtable$Entry; +5 -1: table +10 -1: Short.java +19 -1: ReferenceQueue.java +8 -1: setTabAt +26 -1: ()Lsun/invoke/empty/Empty; +53 -1: (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; +74 -1: (ILjava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +19 -1: changeParameterType +61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/security/Permission;>; +23 -1: (Ljava/lang/Object;IF)V +32 -1: (I)Ljava/util/ListIterator<TE;>; +6 -1: unread +12 -1: isSubclassOf +6 -1: (JJJ)V +3 -1: Key +105 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;ITK;TV;)Ljava/util/HashMap$TreeNode<TK;TV;>; +14 -1: x-utf-16le-bom +22 -1: ()Ljava/nio/IntBuffer; +104 -1: (Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +21 -1: removeFirstOccurrence +21 -1: sun/misc/DoubleConsts +23 -1: ()Ljava/util/SortedSet; +11 -1: getManEntry +23 -1: URI is not hierarchical +7 -1: replace +16 -1: getDisplayScript +11 -1: ISO_8859-15 +16 -1: permuteArguments +5 -1: (JI)C +41 -1: Error decoding percent encoded characters +22 -1: ([I)Ljava/lang/String; +31 -1: java/lang/management/ThreadInfo +9 -1: useCaches +22 -1: withInternalMemberName +5 -1: (JI)I +5 -1: (JI)J +67 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>; +95 -1: (Ljava/util/jar/JarFile;[Ljava/security/CodeSource;)Ljava/util/Enumeration<Ljava/lang/String;>; +7 -1: release +56 -1: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; +5 -1: (JI)V +46 -1: (Ljava/util/Iterator;I)Ljava/util/Spliterator; +18 -1: verifyMemberAccess +9 -1: warning: +23 -1: java/lang/reflect/Field +67 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)V +10 -1: SizePrefix +15 -1: setJavaIOAccess +6 -1: (JI)[B +10 -1: ST_FLUSHED +10 -1: resolution +9 -1: ST_CODING +16 -1: sun/misc/Cleaner +21 -1: (Ljava/lang/Class;)[B +18 -1: WeakReference.java +41 -1: (Ljava/util/List;Ljava/util/Comparator;)V +18 -1: printPropertyValue +3 -1: 813 +22 -1: setRunFinalizersOnExit +4 -1: init +44 -1: ()Ljava/util/Iterator<Ljava/nio/file/Path;>; +3 -1: 819 +12 -1: listIterator +8 -1: , arity= +24 -1: (Ljava/util/ArrayList;)I +49 -1: (IJLjava/io/FileDescriptor;Ljava/lang/Runnable;)V +10 -1: principals +17 -1: x-ISO-2022-CN-CNS +22 -1: (Ljava/lang/Object;F)V +14 -1: setReadTimeout +19 -1: getProtectionDomain +13 -1: pathSeparator +12 -1: getAndSetInt +58 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/util/Locale; +11 -1: setWritable +4 -1: perf +50 -1: ()Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>; +6 -1: LOCTIM +6 -1: status +11 -1: replaceName +52 -1: (Ljava/lang/CharSequence;II)Ljava/lang/StringBuffer; +9 -1: nextToken +13 -1: dropArguments +20 -1: RECOGNIZED_MODIFIERS +14 -1: getInputStream +9 -1: readFully +25 -1: (CLjava/nio/CharBuffer;)I +24 -1: sun/misc/PathPermissions +10 -1: malformedN +9 -1: n is null +19 -1: instanceof Double: +13 -1: markSupported +26 -1: fromMethodDescriptorString +43 -1: [Ljava/util/concurrent/locks/ReentrantLock; +17 -1: parseUnsignedLong +33 -1: Lsun/misc/URLClassPath$JarLoader; +26 -1: (Ljava/io/OutputStream;I)V +6 -1: L_DASH +17 -1: EmptyNavigableMap +19 -1: CharsetDecoder.java +18 -1: makeDynamicInvoker +12 -1: URLUtil.java +27 -1: ()Ljava/util/Iterator<TT;>; +9 -1: initTable +11 -1: getFragment +7 -1: isLower +20 -1: getMethodOrFieldType +29 -1: java/util/function/BiFunction +10 -1: access$700 +3 -1: 850 +7 -1: UTC2037 +43 -1: java/util/ArraysParallelSortHelpers$FJShort +9 -1: toSTZTime +10 -1: access$702 +3 -1: 852 +8 -1: hashCode +3 -1: 855 +44 -1: (Ljava/lang/ThreadLocal;Ljava/lang/Object;)V +3 -1: 857 +3 -1: 858 +5 -1: erase +55 -1: ()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>; +47 -1: (Ljava/util/Iterator;JI)Ljava/util/Spliterator; +38 -1: Ljava/nio/charset/spi/CharsetProvider; +22 -1: can't deserialize enum +13 -1: LF_EX_INVOKER +18 -1: java/text/Collator +18 -1: Zero length string +49 -1: <T:Ljava/lang/Object;>()Ljava/util/Iterator<TT;>; +5 -1: amd64 +12 -1: getNameCount +7 -1: inCheck +52 -1: (Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)V +53 -1: (ILjava/lang/CharSequence;II)Ljava/lang/StringBuffer; +21 -1: java/util/WeakHashMap +8 -1: throws +52 -1: (Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)Z +55 -1: Ljava/lang/ref/SoftReference<Ljava/util/jar/Manifest;>; +16 -1: emptyEnumeration +3 -1: 862 +89 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Class;)Ljava/util/List; +3 -1: 866 +55 -1: Lsun/reflect/generics/repository/ConstructorRepository; +35 -1: (Ljava/lang/ref/ReferenceQueue$1;)V +33 -1: java/util/Collections$CheckedList +18 -1: prefetchReadStatic +7 -1: (JI[I)I +78 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;)Ljava/lang/ThreadLocal$ThreadLocalMap; +7 -1: ordinal +22 -1: FilterInputStream.java +26 -1: java/util/zip/ZipConstants +28 -1: JVM cannot find invoker for +43 -1: sun/reflect/generics/parser/SignatureParser +51 -1: (Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)V +73 -1: (Ljava/util/function/ToIntFunction;Ljava/lang/Object;Ljava/lang/Object;)I +17 -1: StringBuffer.java +62 -1: ([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set;)V +6 -1: equals +9 -1: formatter +3 -1: 874 +35 -1: newGetShortIllegalArgumentException +74 -1: (Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult; +3 -1: ucp +60 -1: ([Ljava/lang/ClassValue$Entry;I)Ljava/lang/ClassValue$Entry; +6 -1: create +17 -1: makeReinvokerForm +18 -1: csisolatincyrillic +15 -1: incrementAndGet +24 -1: maybeInstantiateVerifier +33 -1: ()Ljava/nio/channels/FileChannel; +6 -1: class +16 -1: getAnnotatedType +43 -1: (Ljava/lang/reflect/Type;)Ljava/lang/Class; +9 -1: HASH_BITS +12 -1: placeInCache +38 -1: java/util/Collections$SynchronizedList +89 -1: (Ljava/net/URL;Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)Ljava/security/CodeSource; +22 -1: (Ljava/lang/String;I)B +20 -1: Ljava/util/TimeZone; +16 -1: sun.java.command +28 -1: java/util/WeakHashMap$Values +10 -1: X-UTF-16BE +22 -1: (Ljava/lang/String;I)I +26 -1: java/nio/DirectCharBufferS +99 -1: (Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class<*>; +22 -1: (Ljava/lang/String;I)J +26 -1: java/nio/DirectCharBufferU +54 -1: (Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal; +21 -1: java/util/AbstractSet +37 -1: (Ljava/lang/String;)Ljava/lang/Short; +36 -1: Ljava/nio/charset/CoderResult$Cache; +22 -1: (Ljava/lang/String;I)S +15 -1: Ljava/util/Map; +59 -1: (Ljava/lang/reflect/Type;)Ljava/lang/reflect/AnnotatedType; +22 -1: (Ljava/lang/String;I)V +10 -1: getAddress +25 -1: java/nio/DirectIntBufferS +11 -1: IMPL_LOOKUP +3 -1: uee +7 -1: addTime +37 -1: sun/security/action/GetPropertyAction +25 -1: java/nio/DirectIntBufferU +21 -1: javaUtilZipFileAccess +34 -1: java/util/Collections$CheckedQueue +11 -1: readResolve +22 -1: (Ljava/lang/String;I)Z +11 -1: findVirtual +63 -1: (Ljava/lang/String;Ljava/lang/String;)Lsun/security/util/Debug; +22 -1: getDeclaredAnnotations +16 -1: Collections.java +18 -1: invalid entry size +49 -1: java/util/concurrent/ConcurrentHashMap$TableStack +32 -1: java/util/AbstractSequentialList +4 -1: int0 +16 -1: MAXIMUM_CAPACITY +53 -1: ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; +4 -1: int1 +4 -1: int2 +4 -1: int3 +119 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;I)Lsun/reflect/MethodAccessor; +7 -1: variant +39 -1: Lsun/reflect/annotation/AnnotationType; +11 -1: arrayOffset +24 -1: ()Ljava/util/LinkedList; +31 -1: java/lang/ClassCircularityError +17 -1: java/lang/Package +10 -1: ccsid00858 +27 -1: java/io/ExpiringCache$Entry +16 -1: newFieldAccessor +67 -1: (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; +99 -1: Lsun/reflect/generics/repository/GenericDeclRepository<Lsun/reflect/generics/tree/ClassSignature;>; +53 -1: (Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)V +53 -1: Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +58 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/stream/Stream<TT;>; +54 -1: (Ljava/lang/CharSequence;Ljava/text/Normalizer$Form;)Z +8 -1: Kerberos +29 -1: ()Ljava/nio/channels/Channel; +12 -1: java_version +45 -1: (Lsun/reflect/DelegatingMethodAccessorImpl;)V +10 -1: canConvert +136 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V +26 -1: getDayOfWeekDateOnOrBefore +7 -1: INVALID +10 -1: TERMINATED +41 -1: Ljava/security/cert/CertificateException; +74 -1: (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/String; +30 -1: [Ljava/lang/invoke/MethodType; +13 -1: getMethodName +7 -1: Factory +34 -1: (Ljava/util/function/BiConsumer;)V +11 -1: unlinkFirst +37 -1: lambda$getDeclaredAnnotationsByType$0 +77 -1: (Ljava/nio/ByteBuffer;ILjava/nio/CharBuffer;II)Ljava/nio/charset/CoderResult; +20 -1: java/util/ArrayDeque +65 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;J)V +49 -1: (Ljava/util/ArrayDeque;Ljava/util/ArrayDeque$1;)V +22 -1: (J)Ljava/time/Instant; +17 -1: URLClassPath.java +6 -1: 8859_1 +25 -1: stopRemoteManagementAgent +6 -1: 8859_2 +17 -1: containsNullValue +6 -1: 8859_4 +6 -1: 8859_5 +26 -1: (Ljava/nio/ByteBuffer;IC)V +76 -1: (Ljava/lang/Runnable;Ljava/security/AccessControlContext;)Ljava/lang/Thread; +6 -1: 8859_7 +6 -1: 8859_9 +8 -1: setHours +9 -1: File.java +21 -1: isIdentifierIgnorable +5 -1: ([C)I +4 -1: (B)I +10 -1: codesource +77 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;Ljava/security/AccessControlContext;)V +4 -1: (B)J +6 -1: isFair +30 -1: java/lang/NullPointerException +10 -1: IMPL_NAMES +13 -1: Runnable.java +26 -1: Ill-formed extension key: +17 -1: ()[Ljava/io/File; +18 -1: javaSecurityAccess +16 -1: equalsIgnoreCase +4 -1: (B)V +5 -1: ([C)V +25 -1: Ljava/io/FileInputStream; +14 -1: trackJavaUsage +20 -1: Ljava/io/FileSystem; +10 -1: iso_8859_1 +4 -1: (B)Z +30 -1: java/lang/NoClassDefFoundError +152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/HashMap$TreeNode<TK;TV;>;Ljava/util/HashMap$TreeNode<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>; +19 -1: java/lang/Cloneable +55 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;I)V +27 -1: (Ljava/nio/ByteBuffer;IJZ)V +21 -1: ()Lsun/misc/JarIndex; +72 -1: ([Ljava/security/CodeSource;)Ljava/util/Enumeration<Ljava/lang/String;>; +64 -1: (Ljava/util/Collection;Ljava/util/Comparator;)Ljava/lang/Object; +20 -1: invalid entry crc-32 +9 -1: BASECOUNT +29 -1: java/security/BasicPermission +52 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/io/File; +7 -1: ([B[B)Z +17 -1: EMPTY_ELEMENTDATA +61 -1: (Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object; +49 -1: (ILjava/lang/Class;)Ljava/lang/invoke/MethodType; +29 -1: sun/reflect/MagicAccessorImpl +121 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/ConstructorRepository; +6 -1: ([II)I +40 -1: (Ljava/lang/String;I)Ljava/lang/Integer; +25 -1: java.content.handler.pkgs +63 -1: ()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>; +13 -1: parameterList +6 -1: rebind +16 -1: isSuperInterface +6 -1: ([II)V +14 -1: currentRuntime +9 -1: BA_EXISTS +15 -1: END_PUNCTUATION +15 -1: no such method +19 -1: getAndVerifyPackage +4 -1: wrap +24 -1: checkAwtEventQueueAccess +7 -1: ibm-437 +4 -1: open +47 -1: (Ljava/nio/ByteBuffer;[BI)Ljava/nio/ByteBuffer; +21 -1: ADDRESS_BITS_PER_WORD +13 -1: isConstructor +12 -1: getUseCaches +27 -1: sun/util/locale/LocaleUtils +4 -1: koi8 +23 -1: getParameterAnnotations +9 -1: providers +57 -1: ([Ljava/lang/Object;Ljava/util/function/BinaryOperator;)V +24 -1: Lsun/misc/JavaNetAccess; +22 -1: ([J)Ljava/lang/String; +7 -1: p-1022$ +6 -1: isType +63 -1: Ljava/util/Map<Ljava/lang/String;Lsun/util/calendar/ZoneInfo;>; +21 -1: pageAlignDirectMemory +18 -1: getManifestDigests +37 -1: [Ljava/lang/reflect/AccessibleObject; +7 -1: decrypt +54 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/util/HashMap; +32 -1: lambda$comparingByKey$bbdbfea9$1 +21 -1: hasGenericInformation +31 -1: java/nio/charset/CharsetEncoder +15 -1: setTargetNormal +3 -1: ulp +18 -1: argumentTypesMatch +12 -1: getDayOfYear +8 -1: closeAll +76 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/ref/SoftReference<TV;>; +6 -1: concat +9 -1: getLongAt +16 -1: hasBeenFinalized +32 -1: [Ljava/util/Hashtable$Entry<**>; +23 -1: CheckedRandomAccessList +106 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/net/URI; +21 -1: defineClassInPackage. +11 -1: ([III[III)V +8 -1: toZoneId +34 -1: SUBCLASS_IMPLEMENTATION_PERMISSION +55 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater +8 -1: isDirect +10 -1: ALL_ACCESS +12 -1: isRegistered +29 -1: ForEachTransformedMappingTask +5 -1: LIJFD +23 -1: (Ljava/util/TimeZone;)V +23 -1: (Ljava/util/TimeZone;)Z +20 -1: java/lang/Appendable +29 -1: Lsun/util/calendar/Gregorian; +9 -1: charValue +8 -1: ONE_HOUR +38 -1: (Ljava/util/Locale;)Ljava/lang/String; +7 -1: script= +10 -1: X-UTF-16LE +7 -1: ENTRIES +6 -1: detach +38 -1: certpath PKIX CertPathBuilder and +14 -1: setLanguageTag +13 -1: isAlphaString +13 -1: interpretName +9 -1: dayOfWeek +88 -1: (Ljava/lang/Class;ZLjava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/List; +91 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)Ljava/lang/invoke/MethodHandle; +13 -1: addTypeString +17 -1: VectorSpliterator +12 -1: MILLISECONDS +6 -1: CENCOM +10 -1: KeySetView +18 -1: getTargetException +7 -1: H_ALPHA +32 -1: sun/util/locale/BaseLocale$Cache +25 -1: [Ljava/lang/CharSequence; +7 -1: Builder +4 -1: left +19 -1: BootClassPathHolder +12 -1: publicFields +11 -1: windows-437 +9 -1: EMPTY_SET +26 -1: GET_STACK_TRACE_PERMISSION +6 -1: copyOf +14 -1: aliases_IBM737 +9 -1: writeLong +35 -1: (JLjava/util/concurrent/TimeUnit;)Z +70 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/String;>; +22 -1: getAnnotatedReturnType +41 -1: (Ljava/lang/String;[BII)Ljava/lang/Class; +8 -1: ,maxpri= +29 -1: handleParameterNumberMismatch +26 -1: ts timestamping +11 -1: checkListen +10 -1: SourceFile +44 -1: (Ljava/lang/String;)Ljava/util/jar/JarEntry; +17 -1: weakCompareAndSet +9 -1: timestamp +21 -1: (Z)Ljava/lang/String; +12 -1: doneWithMeta +20 -1: makeCollectArguments +52 -1: (JLjava/util/function/BiFunction;)Ljava/lang/Object; +10 -1: searchKeys +48 -1: sun/reflect/SerializationConstructorAccessorImpl +69 -1: (Ljava/lang/reflect/Constructor<*>;)Lsun/reflect/ConstructorAccessor; +19 -1: ()Lsun/misc/Unsafe; +11 -1: deepEquals0 +7 -1: GB18030 +13 -1: ValueIterator +75 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V +28 -1: java/util/PropertyPermission +6 -1: CENCRC +3 -1: url +3 -1: L_L +19 -1: Certificate too big +33 -1: sun/reflect/DelegatingClassLoader +16 -1: lambda$stream$57 +11 -1: BitSet.java +13 -1: sun/misc/Perf +26 -1: Ljava/util/SimpleTimeZone; +7 -1: (BBBB)I +24 -1: sun/misc/FloatingDecimal +21 -1: sun/util/PreHashedMap +8 -1: utf-16be +11 -1: isInherited +83 -1: (Ljava/util/Properties;Ljava/io/OutputStream;Ljava/lang/String;Ljava/lang/String;)V +52 -1: (Ljava/lang/Class;Ljava/security/ProtectionDomain;)V +11 -1: getDoOutput +18 -1: asVarargsCollector +22 -1: NoSuchMethodError.java +18 -1: getStackTraceDepth +30 -1: (Ljava/io/File;)Ljava/net/URL; +15 -1: CURRENCY_SYMBOL +24 -1: Lsun/misc/JavaNioAccess; +6 -1: NATIVE +22 -1: Lsun/misc/PerfCounter; +63 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToIntTask +30 -1: less than Character.MIN_RADIX +17 -1: isCallerSensitive +8 -1: shutdown +11 -1: STATE_GREEN +4 -1: next +10 -1: tryPresize +16 -1: CONSTRUCTOR_NAME +3 -1: MAY +21 -1: java.security.manager +20 -1: Lsun/misc/Contended; +16 -1: DISPLAY_LANGUAGE +6 -1: KeySet +9 -1: getScript +3 -1: utc +17 -1: TRACE_INTERPRETER +39 -1: (Ljava/lang/Object;I)Ljava/lang/String; +19 -1: getFieldAtIfLoaded0 +15 -1: methodFilterMap +54 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Boolean;>; +45 -1: java/security/cert/Certificate$CertificateRep +43 -1: (Ljava/lang/String;)Lsun/util/calendar/Era; +34 -1: java/security/cert/X509Certificate +19 -1: versionsInitialized +11 -1: isDirectory +14 -1: aliases_IBM775 +38 -1: (Ljava/util/function/Consumer<-TE;>;)V +38 -1: (Ljava/lang/String;)Ljava/util/Locale; +40 -1: (Ljava/lang/String;Z)Lsun/misc/Resource; +14 -1: setDefaultZone +14 -1: highResCounter +78 -1: (Ljava/lang/ClassValue$Version;Ljava/lang/Object;)Ljava/lang/ClassValue$Entry; +16 -1: defaultUseCaches +40 -1: (Ljava/util/zip/ZipFile;)Ljava/util/Map; +24 -1: isSupplementaryCodePoint +15 -1: ISO_8859_1.java +13 -1: multiplyExact +126 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale; +8 -1: classMap +8 -1: wildcard +19 -1: getSimpleBinaryName +17 -1: illegal signature +14 -1: == basicType( +17 -1: ZipConstants.java +32 -1: [Ljava/lang/VirtualMachineError; +27 -1: ()Ljava/util/stream/Stream; +24 -1: (Ljava/lang/Exception;)V +21 -1: java/util/Enumeration +13 -1: newSetFromMap +8 -1: getenv.* +20 -1: sun/management/Agent +21 -1: sun/nio/cs/US_ASCII$1 +7 -1: comment +15 -1: appendAuthority +11 -1: hasWrappers +10 -1: dstOffset +24 -1: sun/reflect/ConstantPool +75 -1: (Ljava/util/jar/JarFile;[Ljava/security/CodeSource;)Ljava/util/Enumeration; +52 -1: (Ljava/util/jar/JarFile;)Ljava/util/jar/JarVerifier; +70 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/Object;>; +14 -1: previousSetBit +17 -1: AF_GETSTATIC_INIT +15 -1: ArrayDeque.java +7 -1: boolean +25 -1: (I)Ljava/math/BigInteger; +146 -1: (Ljava/lang/ref/ReferenceQueue<Ljava/lang/Class<*>;>;Ljava/util/concurrent/ConcurrentMap<+Ljava/lang/ref/WeakReference<Ljava/lang/Class<*>;>;*>;)V +8 -1: getClass +8 -1: user.dir +6 -1: VALUES +5 -1: raise +39 -1: (JLjava/util/function/Consumer<-TV;>;)V +107 -1: <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TT;TV;>; +5 -1: print +8 -1: readChar +55 -1: (JLjava/util/TimeZone;)Lsun/util/calendar/CalendarDate; +56 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysTask +60 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Double;>; +102 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;)Ljava/util/SortedMap<TK;TV;>; +23 -1: printModifiersIfNonzero +14 -1: getterFunction +15 -1: ISO-10646-UCS-2 +14 -1: canonizeString +13 -1: getTotalSpace +24 -1: synchronizedNavigableSet +100 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;)TT; +4 -1: ioex +24 -1: ()Ljava/nio/FloatBuffer; +14 -1: toBinaryString +7 -1: Segment +34 -1: ()Lsun/misc/JavaUtilZipFileAccess; +17 -1: setTargetVolatile +22 -1: (Ljava/util/List<*>;)V +51 -1: (ILjava/lang/CharSequence;)Ljava/lang/StringBuffer; +22 -1: (Ljava/util/List<*>;)Z +5 -1: (FI)F +11 -1: parseMethod +8 -1: Compiled +19 -1: java/util/SortedMap +7 -1: setByte +12 -1: getFieldType +8 -1: pageSize +14 -1: getCallerClass +9 -1: ensureObj +18 -1: refKindHasReceiver +10 -1: getZoneIds +24 -1: (Ljava/nio/CharBuffer;)I +47 -1: ()Lsun/reflect/generics/parser/SignatureParser; +8 -1: getIndex +24 -1: (Ljava/lang/Thread;TT;)V +18 -1: (Ljava/util/Map;)V +18 -1: (Ljava/util/Map;)Z +6 -1: random +10 -1: putAddress +64 -1: (Ljava/util/function/Consumer<-Ljava/util/Map$Entry<TK;TV;>;>;)V +24 -1: (Ljava/nio/CharBuffer;)V +8 -1: canCache +24 -1: (Ljava/nio/CharBuffer;)Z +9 -1: getIntAt0 +4 -1: sqrt +8 -1: makeLong +54 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;)V +5 -1: (JJ)I +26 -1: javaIOFileDescriptorAccess +5 -1: (JJ)J +15 -1: != basicType: +36 -1: Ljava/lang/ref/ReferenceQueue<-TT;>; +5 -1: words +16 -1: sun.jnu.encoding +32 -1: (Ljava/lang/invoke/LambdaForm;)V +22 -1: ensureClassInitialized +16 -1: Ljava/util/List; +14 -1: varargsInvoker +5 -1: (JJ)V +20 -1: java/util/Properties +12 -1: getImplClass +21 -1: argumentTypesToString +5 -1: (JJ)Z +50 -1: java/util/Collections$SynchronizedRandomAccessList +17 -1: makeWrappedMember +21 -1: UnmodifiableSortedSet +22 -1: (ILjava/lang/Class;Z)V +3 -1: MIT +13 -1: bootClassPath +50 -1: (JLjava/util/function/Function;)Ljava/lang/Object; +74 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap$Node<TK;TV;>; +45 -1: (Ljava/util/Map$Entry;Ljava/util/Map$Entry;)I +12 -1: advanceProbe +10 -1: encoderFor +14 -1: DataInput.java +16 -1: java/lang/Double +3 -1: 912 +3 -1: 914 +3 -1: abs +3 -1: 915 +13 -1: currentThread +17 -1: ClassDefiner.java +32 -1: sun/misc/Launcher$ExtClassLoader +16 -1: Current state = +12 -1: elementCount +10 -1: unmaskNull +8 -1: csibm857 +32 -1: java/net/UnknownServiceException +10 -1: x-utf-16be +3 -1: acc +37 -1: Ljava/util/List<Ljava/io/Closeable;>; +23 -1: ([Ljava/lang/Thread;Z)I +17 -1: impliesIgnoreMask +23 -1: getGenericComponentType +51 -1: ()Lsun/reflect/generics/repository/ClassRepository; +34 -1: " not found. Will use interpreter. +38 -1: ()Ljava/security/AccessControlContext; +6 -1: final +8 -1: utf-16le +3 -1: 920 +3 -1: 923 +5 -1: (I)[C +43 -1: (Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer; +8 -1: csibm862 +34 -1: (Ljava/lang/ref/Reference<+TT;>;)Z +8 -1: csibm866 +20 -1: getParameterizedType +7 -1: (II[C)V +20 -1: [Ljava/lang/Package; +84 -1: (Ljava/lang/String;Ljava/security/ProtectionDomain;)Ljava/security/ProtectionDomain; +28 -1: SynchronizedRandomAccessList +18 -1: sun/misc/VMSupport +61 -1: java/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry +3 -1: add +16 -1: ZipEntryIterator +11 -1: next_target +87 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class<*>; +4 -1: amod +12 -1: markedSkipLF +55 -1: java/util/concurrent/ConcurrentHashMap$EntrySpliterator +5 -1: lim= +29 -1: java/security/PermissionsHash +50 -1: (Ljava/lang/CharSequence;II)Ljava/lang/Appendable; +19 -1: makePairwiseConvert +58 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue$Entry<TT;>;)V +8 -1: contents +11 -1: user.region +17 -1: RandomAccess.java +13 -1: singletonList +13 -1: policy,access +64 -1: Ljava/util/Map<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>; +25 -1: (Ljava/lang/Appendable;)V +19 -1: (Ljava/util/List;)V +43 -1: (Ljava/lang/ClassLoader;Ljava/lang/Class;)V +19 -1: (Ljava/util/List;)Z +15 -1: America/Chicago +25 -1: (II)Ljava/nio/CharBuffer; +12 -1: getDayOfWeek +8 -1: ([BIIZ)V +28 -1: (Ljava/lang/reflect/Field;)I +28 -1: (Ljava/lang/reflect/Field;)J +18 -1: getDeclaringClass0 +11 -1: counterTime +30 -1: (Ljava/util/Collection<TE;>;)V +28 -1: (Ljava/lang/reflect/Field;)V +31 -1: (IIIILjava/io/FileDescriptor;)V +25 -1: java/net/SocketPermission +20 -1: bad parameter count +18 -1: getHeaderFieldLong +26 -1: GetReflectionFactoryAction +19 -1: MIN_TRANSFER_STRIDE +17 -1: java/nio/Bits$1$1 +7 -1: getSize +33 -1: java/util/function/ToLongFunction +46 -1: (IILjava/lang/String;)Ljava/lang/StringBuffer; +10 -1: access$800 +65 -1: sun/misc/JavaSecurityProtectionDomainAccess$ProtectionDomainCache +26 -1: (Ljava/lang/ClassLoader;)V +25 -1: java/util/IdentityHashMap +26 -1: (Ljava/lang/ClassLoader;)Z +91 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToIntFunction<-TT;>;)Ljava/util/Comparator<TT;>; +26 -1: ([CIILjava/lang/String;I)I +12 -1: canonicalize +3 -1: val +8 -1: putCharB +12 -1: UTF_32LE_BOM +60 -1: (Ljava/security/CodeSource;)Ljava/security/ProtectionDomain; +44 -1: (Lsun/misc/SignalHandler;Lsun/misc/Signal;)V +26 -1: (Ljava/util/Enumeration;)V +11 -1: INVALIDATED +8 -1: putCharL +22 -1: (II)Ljava/lang/String; +7 -1: hasNext +5 -1: WRITE +20 -1: MIN_INITIAL_CAPACITY +13 -1: propertyNames +9 -1: Gregorian +13 -1: getExpiration +7 -1: minutes +7 -1: ostream +9 -1: java.lang +17 -1: forceStandardTime +9 -1: initWords +41 -1: java/lang/Thread$UncaughtExceptionHandler +9 -1: theUnsafe +27 -1: ForEachTransformedEntryTask +10 -1: forEncoder +31 -1: needsClassLoaderPermissionCheck +5 -1: ctime +25 -1: ()Ljava/nio/DoubleBuffer; +8 -1: getValue +66 -1: (Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale$Key; +42 -1: (Ljava/io/InputStream;Ljava/lang/String;)V +6 -1: august +14 -1: compileClasses +13 -1: javaNetAccess +22 -1: interpretWithArguments +4 -1: url: +14 -1: EMPTY_ITERATOR +60 -1: Ljava/util/WeakHashMap<Ljava/io/Closeable;Ljava/lang/Void;>; +24 -1: java/util/jar/Attributes +12 -1: getOrDefault +19 -1: Pacific/Guadalcanal +33 -1: ()Ljava/lang/reflect/Constructor; +38 -1: java/util/Collections$UnmodifiableList +13 -1: basicTypeChar +22 -1: (Ljava/lang/String;J)J +14 -1: memberDefaults +42 -1: (Ljava/lang/Class<*>;[Ljava/lang/String;)V +38 -1: (Ljava/util/function/Consumer<-TK;>;)V +16 -1: LF_NEWINVSPECIAL +10 -1: classDepth +28 -1: [Ljava/io/ObjectStreamField; +46 -1: (Ljava/util/Collection;)Ljava/util/Collection; +91 -1: ([Ljava/lang/reflect/Method;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; +11 -1: getLocation +39 -1: (Ljava/lang/Class;[Ljava/lang/Class;Z)V +9 -1: loadTable +55 -1: Directory separator should not appear in library name: +7 -1: setTime +14 -1: getConstructor +4 -1: urls +25 -1: dispatchUncaughtException +77 -1: (ILjava/lang/Object;Ljava/lang/Class<*>;)Ljava/util/HashMap$TreeNode<TK;TV;>; +8 -1: modCount +8 -1: Opening +6 -1: ENDHDR +4 -1: cnfe +34 -1: DIRECTIONALITY_PARAGRAPH_SEPARATOR +10 -1: Asia/Amman +3 -1: MST +28 -1: DIRECTIONALITY_ARABIC_NUMBER +3 -1: all +4 -1: enum +8 -1: copyWith +12 -1: ([JI[IIJII)I +29 -1: Ljava/lang/annotation/Target; +7 -1: Thread- +14 -1: x-utf-32le-bom +38 -1: (ILjava/lang/management/MemoryUsage;)V +33 -1: Signal already used by VM or OS: +27 -1: (I)Ljava/lang/StringBuffer; +25 -1: java/text/Normalizer$Form +10 -1: x-utf-16le +34 -1: can not access a member of class +27 -1: (Ljava/nio/ByteBuffer;IFZ)V +28 -1: (Ljava/lang/ClassValue<*>;)V +16 -1: INITIAL_CAPACITY +23 -1: DirectMethodHandle.java +18 -1: reduceValuesToLong +41 -1: (Ljava/lang/String;Ljava/lang/Class<*>;)V +6 -1: unwrap +12 -1: threadStatus +5 -1: (DI)D +11 -1: fieldOffset +52 -1: java/util/concurrent/ConcurrentHashMap$EntryIterator +14 -1: ACCESS_EXECUTE +44 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer; +29 -1: ([Ljava/lang/ThreadGroup;IZ)I +18 -1: LocalVariableTable +17 -1: ConstantPool.java +26 -1: (Ljava/nio/ByteBuffer;ID)V +4 -1: (C)B +3 -1: and +4 -1: head +126 -1: (Ljava/lang/reflect/GenericDeclaration;Lsun/reflect/generics/scope/Scope;)Lsun/reflect/generics/factory/CoreReflectionFactory; +4 -1: (C)C +16 -1: Pacific/Auckland +7 -1: Thread[ +5 -1: ([D)I +4 -1: (C)I +98 -1: <U:Ljava/lang/Object;>([Ljava/lang/reflect/Constructor<TU;>;)[Ljava/lang/reflect/Constructor<TU;>; +11 -1: fileHandler +30 -1: DIRECTIONALITY_NONSPACING_MARK +10 -1: (this Map) +14 -1: malformedCache +5 -1: ([D)V +4 -1: (C)V +26 -1: getUnicodeLocaleAttributes +4 -1: (C)Z +81 -1: (JLjava/util/function/ToLongBiFunction;JLjava/util/function/LongBinaryOperator;)J +46 -1: [Ljava/util/concurrent/ConcurrentHashMap$Node; +20 -1: Max. Heap Size: +24 -1: Ljava/lang/reflect/Type; +13 -1: EmptyIterator +8 -1: allocate +7 -1: FLUSHED +8 -1: exitVM.* +59 -1: (Ljava/lang/String;)Lsun/util/locale/InternalLocaleBuilder; +19 -1: reduceEntriesToLong +15 -1: getISOLanguages +13 -1: CONSTANT_ZERO +23 -1: (I)Ljava/util/Iterator; +96 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>; +11 -1: isSynthetic +7 -1: lineBuf +30 -1: java/lang/annotation/Inherited +65 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Iterable<TE;>; +35 -1: (Ljava/lang/String;)[Ljava/io/File; +27 -1: java/security/cert/CertPath +26 -1: startRemoteManagementAgent +9 -1: shiftLeft +5 -1: stack +42 -1: (Ljava/lang/Class<*>;[Ljava/lang/Object;)V +11 -1: CheckedList +10 -1: replaceAll +86 -1: (Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;)Ljava/util/HashMap$TreeNode; +24 -1: (I)Ljava/nio/CharBuffer; +13 -1: image/vnd.fpx +15 -1: iso_8859-1:1987 +19 -1: (Ljava/lang/Long;)I +22 -1: sun/misc/SignalHandler +15 -1: ifModifiedSince +42 -1: (Ljava/lang/Class;)Ljava/lang/ClassLoader; +105 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Class;I[Ljava/lang/invoke/MemberName;)I +22 -1: Negative timeout value +38 -1: Ljava/io/UnsupportedEncodingException; +11 -1: removeRange +13 -1: Compiler.java +6 -1: Sorter +8 -1: aliasSet +10 -1: UNASSIGNED +34 -1: lambda$comparingByValue$1065357e$1 +34 -1: ()Ljava/lang/Class$AnnotationData; +25 -1: sun/misc/Launcher$Factory +15 -1: getLongVolatile +8 -1: vmloader +10 -1: unicodebig +10 -1: closeables +31 -1: JavaIOFileDescriptorAccess.java +7 -1: static +3 -1: arg +21 -1: library can't be null +42 -1: java/util/ArraysParallelSortHelpers$FJByte +22 -1: getDeclaringExecutable +19 -1: runFinalizersOnExit +20 -1: simpleTimeZoneParams +13 -1: Modifier.java +14 -1: pkcs11keystore +6 -1: shared +30 -1: java/net/MalformedURLException +26 -1: ()[Lsun/util/calendar/Era; +13 -1: x-MS950-HKSCS +10 -1: relativize +40 -1: (Ljava/lang/String;JJ)Ljava/lang/String; +31 -1: java/util/HashMap$ValueIterator +29 -1: MODIFY_THREADGROUP_PERMISSION +38 -1: (Ljava/lang/String;)Ljava/lang/Object; +7 -1: destroy +37 -1: (Ljava/util/List;)[Ljava/lang/String; +18 -1: (Ljava/io/File;Z)V +32 -1: Ljava/util/HashMap$Node<TK;TV;>; +18 -1: interfaceModifiers +34 -1: java/util/LinkedList$LLSpliterator +7 -1: REF_??? +23 -1: java/net/ContentHandler +20 -1: <compiledLambdaForm> +17 -1: [Ljava/lang/Byte; +6 -1: exitVM +3 -1: att +27 -1: sun/nio/cs/UTF_16BE$Encoder +6 -1: exists +28 -1: Ljava/util/Collection<+TE;>; +48 -1: (Ljava/lang/CharSequence;)Ljava/lang/Appendable; +6 -1: getMap +52 -1: ([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor; +11 -1: stackTrace[ +21 -1: slowCheckMemberAccess +33 -1: ReflectiveOperationException.java +9 -1: versionId +56 -1: Wrong number of parameters in MethodParameters attribute +14 -1: isLowSurrogate +8 -1: csPCp852 +16 -1: copyConstructors +25 -1: ()Ljava/util/Spliterator; +9 -1: closeLock +17 -1: readUnsignedShort +7 -1: 8859_13 +7 -1: 8859_15 +13 -1: TARGET_OFFSET +26 -1: (Ljava/util/Hashtable;IZ)V +44 -1: (Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer; +46 -1: (Ljava/lang/reflect/Type;)Ljava/lang/Class<*>; +25 -1: DEFAULT_CONCURRENCY_LEVEL +36 -1: Ljava/util/List<Ljava/lang/String;>; +29 -1: sun.nio.PageAlignDirectMemory +37 -1: (Ljava/lang/Class;I)Ljava/lang/Class; +12 -1: encryptBlock +8 -1: parentOf +5 -1: H_HEX +11 -1: getFloatAt0 +24 -1: VirtualMachineError.java +18 -1: getDeclaredClasses +59 -1: (Ljava/lang/AbstractStringBuilder;)Ljava/lang/StringBuffer; +42 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)[C +37 -1: (Ljava/lang/String;)Ljava/lang/Float; +13 -1: Iterator.java +25 -1: (Ljava/io/PrintStream;I)V +9 -1: getObject +19 -1: [Ljava/lang/String; +7 -1: SIZECTL +11 -1: isUnderflow +27 -1: sun.nio.MaxDirectMemorySize +21 -1: isNonPublicProxyClass +13 -1: toCalendarDOW +9 -1: Type.java +14 -1: aliases_IBM850 +17 -1: emptyNavigableMap +14 -1: aliases_IBM852 +14 -1: aliases_IBM855 +14 -1: aliases_IBM857 +14 -1: aliases_IBM858 +15 -1: iso_8859-4:1988 +13 -1: UnicodeScript +8 -1: getCharB +17 -1: constructorMethod +27 -1: java/util/function/Function +20 -1: getProtectionDomain0 +8 -1: getCharL +32 -1: ([Ljava/io/File;)[Ljava/net/URL; +51 -1: (Ljava/lang/Class;Z)Ljava/lang/invoke/MethodHandle; +7 -1: getenv. +5 -1: stale +14 -1: aliases_IBM862 +32 -1: java/util/spi/LocaleNameProvider +14 -1: aliases_IBM866 +51 -1: ([Ljava/lang/Class;)Ljava/lang/reflect/Constructor; +6 -1: CENDSK +36 -1: java/util/Comparators$NullComparator +34 -1: UnsafeStaticFieldAccessorImpl.java +17 -1: staticFieldOffset +12 -1: prefetchRead +4 -1: help +34 -1: (Ljava/util/concurrent/TimeUnit;)J +8 -1: getChars +19 -1: java/lang/Throwable +34 -1: Annotation Type:\n Member types: +55 -1: (Ljava/lang/Class<*>;Ljava/security/ProtectionDomain;)V +14 -1: aliases_IBM874 +17 -1: getDisplayVariant +24 -1: Ljava/net/NetPermission; +15 -1: jvmMinorVersion +11 -1: subSequence +3 -1: x86 +6 -1: double +14 -1: checkSlotCount +20 -1: java/net/InetAddress +14 -1: Principal.java +8 -1: $,;:@&=+ +17 -1: ByteBuffered.java +21 -1: sun.misc.Perf.getPerf +11 -1: finishEntry +27 -1: sun.timezone.ids.oldmapping +26 -1: (ZLjava/io/OutputStream;)V +41 -1: (Ljava/lang/String;Z)Ljava/lang/Class<*>; +32 -1: generateSerializationConstructor +6 -1: Value +40 -1: (Ljava/lang/String;Ljava/lang/String;Z)V +16 -1: previousClearBit +7 -1: theProp +51 -1: (Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V +39 -1: com.sun.javafx.application.LauncherImpl +21 -1: URLStreamHandler.java +4 -1: in +14 -1: BIT_INDEX_MASK +125 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Comparator<-TK;>;)Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>; +49 -1: (Ljava/util/Set;Ljava/lang/Class;)Ljava/util/Set; +10 -1: scaleValue +27 -1: (Ljava/nio/ByteBuffer;IDZ)V +78 -1: (Ljava/util/Collection<Ljava/lang/reflect/Field;>;[Ljava/lang/reflect/Field;)V +53 -1: <T:Ljava/lang/Object;>(I)Ljava/util/Enumeration<TT;>; +38 -1: java/lang/IncompatibleClassChangeError +60 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsTask +23 -1: not a method or field: +35 -1: Ljava/nio/BufferUnderflowException; +4 -1: i386 +13 -1: US_ASCII.java +80 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/lang/reflect/AnnotatedType; +21 -1: initializeSystemClass +6 -1: ([CI)I +18 -1: getBooleanProperty +35 -1: java/util/function/ToDoubleFunction +37 -1: (Ljava/lang/String;)Ljava/lang/Class; +28 -1: MapReduceEntriesToDoubleTask +38 -1: java/util/LinkedHashMap$LinkedEntrySet +8 -1: copySign +6 -1: ([CI)V +55 -1: sun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule +12 -1: parseBoolean +3 -1: NET +3 -1: NEW +6 -1: Values +17 -1: getJavaLangAccess +9 -1: LocaleKey +3 -1: :-1 +30 -1: (Ljava/io/ObjectInputStream;)V +3 -1: NFC +3 -1: NFD +18 -1: SIMPLIFIED_CHINESE +28 -1: ()Ljava/lang/reflect/Method; +9 -1: localInit +37 -1: sun/util/locale/LocaleSyntaxException +15 -1: LambdaForm.java +5 -1: rtype +8 -1: field " +50 -1: (Ljava/lang/Class;Ljava/lang/ref/ReferenceQueue;)V +80 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)V +36 -1: java/lang/invoke/MethodHandleNatives +22 -1: (Ljava/util/Map<**>;)Z +22 -1: Ljava/lang/Class<TV;>; +7 -1: SubList +14 -1: connect,accept +19 -1: declaredAnnotations +38 -1: Ljava/lang/ThreadLocal$ThreadLocalMap; +11 -1: isSpaceChar +10 -1: offerFirst +20 -1: sun/nio/ByteBuffered +17 -1: packageDefinition +7 -1: trigger +35 -1: [Ljava/lang/invoke/LambdaForm$Name; +19 -1: sun.stdout.encoding +5 -1: start +26 -1: (Ljava/util/HashMap;IIII)V +65 -1: (JLjava/util/function/Consumer<-Ljava/util/Map$Entry<TK;TV;>;>;)V +6 -1: LOCVER +3 -1: :// +42 -1: (CLjava/lang/Class<*>;Ljava/lang/Object;)Z +6 -1: friday +45 -1: sun/reflect/DelegatingConstructorAccessorImpl +15 -1: iso_8859-7:1987 +15 -1: newCalendarDate +24 -1: getAnnotatedReceiverType +32 -1: java/util/NoSuchElementException +50 -1: (Ljava/util/NavigableSet;)Ljava/util/NavigableSet; +26 -1: java/text/SimpleDateFormat +16 -1: threadInitNumber +55 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/Spliterator<TT;>; +23 -1: (Ljava/lang/Object;)TT; +3 1: bar +37 -1: getFunctionalInterfaceMethodSignature +5 -1: state +39 -1: [Ljava/lang/reflect/GenericDeclaration; +22 -1: sun/net/ProgressSource +13 -1: LLSpliterator +93 -1: <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Enumeration<TT;>;Ljava/util/Iterator<TT;>; +5 -1: JAPAN +11 -1: SPACE_TOTAL +20 -1: CharsetProvider.java +12 -1: CR_UNDERFLOW +7 -1: ITALIAN +11 -1: getCallerPD +13 -1: isMemberClass +38 -1: (Ljava/util/function/Consumer<-TT;>;)V +18 -1: malformedForLength +14 -1: ReflectionData +34 -1: (BZI)Ljava/lang/invoke/LambdaForm; +16 -1: forPrimitiveType +31 -1: field found in java.lang.Class +60 -1: (Ljava/lang/Void;Ljava/lang/ThreadGroup;Ljava/lang/String;)V +18 -1: DescendingIterator +25 -1: (IZLjava/lang/Runnable;)V +35 -1: ()[Ljava/lang/reflect/TypeVariable; +3 -1: bcp +23 -1: (Ljava/lang/Object;)TV; +25 -1: setDefaultAssertionStatus +10 -1: ([BIIIII)V +150 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>; +14 -1: Inherited: +17 -1: fileTimeToWinTime +7 -1: ([BI)[B +26 -1: java/io/OutputStreamWriter +58 -1: <T:Ljava/lang/Object;>(Ljava/util/ListIterator<+TT;>;I)TT; +39 -1: sun/reflect/annotation/AnnotationParser +71 -1: (Ljava/nio/file/Path;Ljava/lang/String;)Ljava/nio/file/DirectoryStream; +47 -1: Ljava/util/concurrent/locks/ReentrantLock$Sync; +44 -1: (Ljava/util/Collection;[Ljava/lang/Object;)Z +43 -1: ([ILjava/util/function/IntBinaryOperator;)V +29 -1: reverseAllValidSurrogatePairs +20 -1: Ljava/nio/file/Path; +20 -1: getGenericSignature0 +42 -1: (Ljava/lang/String;Ljava/lang/Class<*>;Z)V +8 -1: manEntry +64 -1: (Ljava/lang/String;)Ljava/util/Enumeration<Lsun/misc/Resource;>; +36 -1: newGetDoubleIllegalArgumentException +63 -1: (Ljava/util/Collection;Ljava/lang/Class;)Ljava/util/Collection; +30 -1: Ergonomics Machine Class: +40 -1: ()Ljava/util/Map<Ljava/lang/String;TT;>; +50 -1: (Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer; +30 -1: CHECK_MEMBER_ACCESS_PERMISSION +22 -1: (Ljava/lang/Boolean;)I +10 -1: V_Variable +68 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedMappingTask +61 -1: (Ljava/lang/String;IILjava/lang/String;)Ljava/nio/ByteBuffer; +21 -1: accessClassInPackage. +44 -1: java/util/WeakHashMap$WeakHashMapSpliterator +11 -1: nextElement +9 -1: separator +48 -1: java/util/zip/ZipFile$ZipFileInflaterInputStream +44 -1: java/security/UnresolvedPermissionCollection +10 -1: access$900 +44 -1: java/util/ArraysParallelSortHelpers$FJDouble +84 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodHandle; +52 -1: (Ljava/lang/ref/Finalizer;)Ljava/lang/ref/Finalizer; +19 -1: getDeclaredMethods0 +29 -1: (IJ)Ljava/lang/StringBuilder; +11 -1: Member.java +61 -1: (Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMBean; +35 -1: java/lang/AssertionStatusDirectives +23 -1: Declaring class is null +56 -1: (Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/Object;)I +12 -1: java/io/File +41 -1: java/util/ConcurrentModificationException +47 -1: (Ljava/lang/CharSequence;)Ljava/nio/CharBuffer; +19 -1: parameterClassCache +8 -1: US_ASCII +15 -1: tryMonitorEnter +22 -1: Ljava/util/Collection; +67 -1: (Lsun/misc/Signal;Lsun/misc/SignalHandler;)Lsun/misc/SignalHandler; +27 -1: (Lsun/misc/JavaNioAccess;)V +9 -1: DigitOnes +5 -1: write +31 -1: NF_internalMemberNameEnsureInit +18 -1: comparableClassFor +20 -1: defineAnonymousClass +49 -1: (Ljava/io/InputStream;Lsun/net/ProgressSource;J)V +31 -1: java/lang/NumberFormatException +17 -1: remainderUnsigned +62 -1: <T::Ljava/lang/Comparable<-TT;>;>()Ljava/util/Comparator<TT;>; +4 -1: Kind +20 -1: (Ljava/lang/Short;)I +8 -1: OfDouble +51 -1: ([Ljava/lang/Object;Ljava/lang/invoke/MethodType;)Z +22 -1: [Ljava/util/Map$Entry; +13 -1: instanceClass +29 -1: ()Ljava/lang/SecurityManager; +12 -1: isUnmappable +17 -1: getAllStackTraces +19 -1: LinkedValueIterator +7 -1: isDigit +10 -1: rangeCheck +89 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>; +13 -1: getMethodType +21 -1: FileOutputStream.java +22 -1: ()Ljava/text/Collator; +64 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;)TT; +15 -1: defaultTimeZone +14 -1: emptySortedMap +35 -1: (Ljava/util/function/IntConsumer;)V +45 -1: java/util/Collections$CheckedRandomAccessList +11 -1: getPriority +7 -1: signals +6 -1: Subset +15 -1: invokeInterface +16 -1: nameRefsAreLegal +38 -1: (Ljava/lang/Object;)Ljava/lang/Object; +37 -1: Ljava/util/Collections$EmptyIterator; +9 -1: Perf.java +20 -1: java/math/BigInteger +38 -1: java/util/WeakHashMap$ValueSpliterator +71 -1: (Ljava/nio/charset/CodingErrorAction;)Ljava/nio/charset/CharsetEncoder; +50 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Object;)V +21 -1: [Ljava/lang/Class<*>; +13 -1: getProperties +63 -1: (Ljava/lang/Class<*>;[B[Ljava/lang/Object;)Ljava/lang/Class<*>; +65 -1: (Ljava/util/Set<Ljava/lang/Class<*>;>;)[Ljava/lang/reflect/Field; +21 -1: sun/util/calendar/Era +21 -1: Ljava/nio/CharBuffer; +23 -1: (Ljava/lang/Object;JS)V +24 -1: oracle/jrockit/jfr/VMJFR +15 -1: access allowed +34 -1: ()Lsun/util/calendar/CalendarDate; +97 -1: (Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object; +44 -1: ([Ljava/lang/Object;[Ljava/lang/Object;III)V +48 -1: (Ljava/util/Collection;I)Ljava/util/Spliterator; +11 -1: SimpleEntry +63 -1: (Ljava/net/URL;Ljava/net/URLStreamHandler;Ljava/util/HashMap;)V +8 -1: putFloat +20 -1: linkToCallSiteMethod +8 -1: invokers +35 -1: (J)Lsun/util/calendar/BaseCalendar; +6 -1: FRENCH +26 -1: getRawParameterAnnotations +9 -1: wordIndex +29 -1: JNI_COPY_FROM_ARRAY_THRESHOLD +20 -1: sun/nio/cs/Surrogate +48 -1: (Lsun/misc/JavaSecurityProtectionDomainAccess;)V +45 -1: (Ljava/lang/ThreadLocal;Ljava/lang/Object;I)V +3 -1: NST +14 -1: getHeaderField +27 -1: (Ljava/util/jar/JarFile;Z)V +10 -1: findNative +38 -1: The following can be used with access: +29 -1: convertCertArrayToSignerArray +4 -1: .DSA +12 -1: dependencies +12 -1: SYNCHRONIZED +14 -1: x-euc-jp-linux +28 -1: URLStreamHandlerFactory.java +11 -1: checkPtypes +68 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)V +13 -1: addDayOfMonth +10 -1: isAncestor +16 -1: entryDislocation +12 -1: tableSizeFor +29 -1: (ID)Ljava/lang/StringBuilder; +19 -1: getLastRuleInstance +24 -1: getGenericParameterTypes +8 -1: ,offset= +37 -1: (Ljava/net/URL;Ljava/lang/String;II)V +13 -1: UTF_16LE.java +12 -1: setTimestamp +31 -1: AtomicReferenceFieldUpdaterImpl +6 -1: L_URIC +4 -1: (D)D +14 -1: DeqSpliterator +13 -1: LF_INVVIRTUAL +171 -1: <U:Ljava/lang/Object;W:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;Ljava/lang/Class<TW;>;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TU;TW;>; +4 -1: (D)I +22 -1: Ljava/lang/Class<TT;>; +4 -1: (D)J +196 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +13 -1: ,transitions= +30 -1: (Lsun/net/www/MessageHeader;)I +4 -1: (D)V +12 -1: segmentShift +4 -1: (D)Z +14 -1: Reference.java +26 -1: [Ljava/lang/reflect/Field; +26 -1: java/nio/DirectByteBufferR +31 -1: lambda$thenComparing$36697e65$1 +30 -1: (Lsun/net/www/MessageHeader;)V +55 -1: No java.nio.charset.StandardCharsets instances for you! +59 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;Ljava/lang/Object;)I +19 -1: thenComparingDouble +31 -1: ()Ljava/lang/invoke/LambdaForm; +9 -1: getMillis +21 -1: StandardCharsets.java +15 -1: postDefineClass +8 -1: getExtra +3 -1: box +10 -1: nextSetBit +27 -1: java/util/ArrayList$SubList +43 -1: (Ljava/util/concurrent/ConcurrentHashMap;)V +52 -1: (Lsun/misc/URLClassPath;)Ljava/net/URLStreamHandler; +7 -1: putLong +40 -1: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V +8 -1: ([DII)[D +8 -1: ([SIIS)I +8 -1: argument +12 -1: linkCallSite +56 -1: <T:Ljava/lang/Object;>Ljava/lang/ref/WeakReference<TT;>; +15 -1: returnSlotCount +68 -1: (Ljava/lang/String;[Ljava/lang/Class<*>;Z)Ljava/lang/reflect/Method; +20 -1: defaultDisplayLocale +21 -1: (Ljava/util/Vector;)V +49 -1: (Lsun/reflect/generics/visitor/TypeTreeVisitor;)V +12 -1: isAlphabetic +5 -1: perms +13 -1: dosToJavaTime +8 -1: ([SIIS)V +7 -1: valueOf +27 -1: Ljava/util/LinkedList$Node; +41 -1: (Ljava/lang/String;I)Ljava/lang/Class<*>; +38 -1: ()Ljava/nio/charset/CodingErrorAction; +4 -1: MASK +5 -1: Field +101 -1: (Ljava/lang/Class;Ljava/nio/ByteBuffer;Lsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/lang/Object; +56 -1: (Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class; +9 -1: SURROGATE +72 -1: (Ljava/lang/invoke/DirectMethodHandle$StaticAccessor;)Ljava/lang/Object; +19 -1: PARAMETER_MODIFIERS +48 -1: ([Ljava/lang/Object;II)Ljava/util/stream/Stream; +56 -1: (TK;TV;Ljava/util/function/BiFunction<-TV;-TV;+TV;>;)TV; +118 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU; +29 -1: (Lsun/nio/ch/Interruptible;)V +45 -1: Ljava/lang/ref/Reference<Ljava/lang/Object;>; +17 -1: isHeldExclusively +3 -1: NYI +15 -1: getByteVolatile +20 -1: compareAndSwapObject +29 -1: (Z)[Ljava/lang/reflect/Field; +7 -1: ([SII)V +13 -1: toLanguageTag +18 -1: StreamEncoder.java +25 -1: (IF)Ljava/nio/ByteBuffer; +18 -1: afterNodeInsertion +11 -1: accessOrder +60 -1: Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget; +20 -1: META-INF/MANIFEST.MF +18 -1: getSuperInterfaces +26 -1: (Ljava/nio/ByteBuffer;IZ)C +26 -1: (Ljava/nio/ByteBuffer;IZ)D +9 -1: PROTECTED +18 -1: not visible from +26 -1: java/lang/RuntimeException +26 -1: (Ljava/nio/ByteBuffer;IZ)F +20 -1: java/net/FileNameMap +15 -1: insertArguments +8 -1: diagprop +26 -1: (Ljava/nio/ByteBuffer;IZ)I +26 -1: (Ljava/nio/ByteBuffer;IZ)J +20 -1: createContentHandler +11 -1: getProtocol +26 -1: (Ljava/nio/ByteBuffer;IZ)S +33 -1: Ljava/lang/NoSuchMethodException; +19 -1: entry name too long +40 -1: java/util/jar/JarVerifier$VerifierStream +6 -1: server +7 -1: OCTOBER +26 -1: sun/invoke/util/VerifyType +6 -1: domain +9 -1: getUnsafe +5 -1: ([Z)I +4 -1: cons +42 -1: ()Ljava/lang/ReflectiveOperationException; +4 -1: byte +29 -1: java/util/function/BiConsumer +24 -1: getJavaUtilZipFileAccess +37 -1: ([Ljava/lang/Object;)Ljava/util/List; +27 -1: timeout can not be negative +62 -1: Ljava/util/Map<Ljava/io/InputStream;Ljava/util/zip/Inflater;>; +16 -1: getOurStackTrace +34 -1: (J)Ljava/lang/ref/Reference<+TT;>; +50 -1: Lsun/reflect/generics/repository/MethodRepository; +20 -1: MIN_BYTE_BUFFER_SIZE +3 -1: buf +13 -1: getAttributes +6 -1: GERMAN +38 -1: java/security/NoSuchAlgorithmException +20 -1: Direct buffer memory +6 -1: (IIZ)V +27 -1: JVMTI_THREAD_STATE_RUNNABLE +26 -1: [Ljava/io/File$PathStatus; +36 -1: (Ljava/util/List;Ljava/lang/Class;)V +13 -1: JarIndex.java +34 -1: java/lang/Character$CharacterCache +8 -1: csIBM857 +10 -1: LineReader +28 -1: Ljava/lang/RuntimeException; +30 -1: ()Ljava/util/Enumeration<TV;>; +12 -1: getSeparator +124 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;)Ljava/lang/Object; +97 -1: Ljava/util/Map<Ljava/lang/Integer;Ljava/lang/ref/WeakReference<Ljava/nio/charset/CoderResult;>;>; +21 -1: ConstantCallSite.java +40 -1: sun/util/locale/provider/LocaleResources +101 -1: <U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;)Ljava/util/Comparator<TT;>; +4 -1: copy +23 -1: sun/security/util/Debug +10 -1: isAbsolute +34 -1: (Ljava/lang/invoke/MethodHandle;)V +34 -1: (Ljava/lang/invoke/MethodHandle;)Z +48 -1: ()[Ljava/util/concurrent/ConcurrentHashMap$Node; +34 -1: (Lsun/reflect/LangReflectAccess;)V +8 -1: csIBM862 +8 -1: csIBM866 +64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToDoubleTask +39 -1: No java.util.Objects instances for you! +68 -1: (Ljava/util/PrimitiveIterator$OfInt;JI)Ljava/util/Spliterator$OfInt; +19 -1: Illegal class name +24 -1: uncaughtExceptionHandler +12 -1: runFinalizer +23 -1: java/io/FileInputStream +41 -1: (Ljava/lang/Class<*>;Ljava/lang/String;)V +98 -1: (Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>; +30 -1: java/nio/charset/CoderResult$1 +20 -1: SourceDebugExtension +30 -1: java/nio/charset/CoderResult$2 +69 -1: ([Ljava/security/ProtectionDomain;[Ljava/security/ProtectionDomain;)Z +14 -1: 1.8.0-internal +16 -1: ReduceValuesTask +13 -1: toLowerString +14 -1: newPrintStream +13 -1: unicodelittle +19 -1: getClassLoadingLock +9 -1: DigitTens +80 -1: (Ljava/lang/Class<*>;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType; +12 -1: sun_eu_greek +21 -1: getBootstrapClassPath +9 -1: volatile +15 -1: UnmodifiableMap +21 -1: enumConstantDirectory +10 -1: getContent +4 -1: cosh +74 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; +11 -1: ([JII[JII)V +28 -1: java/lang/reflect/Executable +17 -1: MapReduceKeysTask +6 -1: isOpen +17 -1: AnnotationDefault +17 -1: Already connected +27 -1: (Lsun/misc/JavaNetAccess;)V +34 -1: ([BILjava/nio/charset/Charset;Z)[B +55 -1: Ljava/lang/invoke/DirectMethodHandle$EnsureInitialized; +8 -1: Set.java +10 -1: DEAD_ENTRY +7 -1: nextInt +3 -1: wtb +23 -1: java/util/Collections$1 +86 -1: (Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/visitor/Reifier; +23 -1: java/util/Collections$2 +23 -1: java/util/Collections$3 +18 -1: DoubleCumulateTask +22 -1: skip value is negative +85 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamDecoder; +5 -1: \\[\\]$ +34 -1: Ljava/util/NoSuchElementException; +3 -1: NaN +30 -1: sun/util/calendar/CalendarDate +10 -1: V_Constant +20 -1: java/lang/Comparable +9 -1: text/html +20 -1: -9223372036854775808 +20 -1: DataInputStream.java +20 -1: Member defaults: +35 -1: Ljava/lang/ref/ReferenceQueue$Lock; +8 -1: getBytes +17 -1: CodePointIterator +11 -1: Signal.java +19 -1: javaHomePrefixCache +12 -1: replaceFirst +37 -1: Ljava/lang/IndexOutOfBoundsException; +39 -1: (Ljava/lang/String;ZI)Ljava/lang/Class; +21 -1: initSystemClassLoader +28 -1: America/Indiana/Indianapolis +47 -1: (Ljava/security/Permission;Ljava/lang/Object;)V +27 -1: java/net/URISyntaxException +21 -1: createSecurityManager +7 -1: suspend +12 -1: L_UNRESERVED +17 -1: checkMemberAccess +12 -1: CoreCounters +19 -1: java/net/HttpCookie +8 -1: isGetter +17 -1: setDaylightSaving +27 -1: java.launcher.javafx.error1 +53 -1: java/util/concurrent/ConcurrentHashMap$CollectionView +16 -1: readUnsignedByte +47 -1: (Ljava/lang/String;)Ljava/net/URLStreamHandler; +40 -1: (Z)[Ljava/lang/reflect/Constructor<TT;>; +14 -1: javaLangAccess +5 -1: apply +8 -1: getFloat +15 -1: getD3DAvailable +25 -1: startLocalManagementAgent +7 -1: RUNTIME +22 -1: LocalVariableTypeTable +8 -1: doOutput +16 -1: CheckedSortedSet +10 -1: zoneOffset +64 -1: (Ljava/security/Permission;)Ljava/security/PermissionCollection; +13 -1: hasUnresolved +13 -1: user.language +11 -1: getDoubleAt +14 -1: getQueueLength +37 -1: java/nio/DirectByteBuffer$Deallocator +6 -1: ASHIFT +9 -1: checkPath +80 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)V +6 -1: CENEXT +16 -1: doubleToLongBits +11 -1: copyToArray +9 -1: copyField +36 -1: ()Lsun/util/calendar/Gregorian$Date; +53 -1: (ILjava/lang/Object;)Ljava/util/HashMap$Node<TK;TV;>; +44 -1: (TK;TV;Ljava/lang/ref/ReferenceQueue<TV;>;)V +35 -1: java/util/Collections$EmptyIterator +38 -1: sealing violation: can't seal package +37 -1: (Ljava/util/Queue;Ljava/lang/Class;)V +129 -1: (Ljava/lang/Class<*>;Ljava/lang/String;[Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B[B)V +18 -1: HashMapSpliterator +8 -1: putShort +23 -1: (Ljava/lang/Object;II)V +7 -1: GERMANY +13 -1: toTitleString +102 -1: (Ljava/util/ArrayPrefixHelpers$CumulateTask;Ljava/util/function/BinaryOperator;[Ljava/lang/Object;II)V +44 -1: ([Ljava/lang/Object;)Ljava/util/Spliterator; +6 -1: unused +25 -1: java/net/URLClassLoader$1 +25 -1: java/net/URLClassLoader$2 +25 -1: java/net/URLClassLoader$3 +25 -1: java/net/URLClassLoader$4 +12 -1: getFixedDate +25 -1: java/net/URLClassLoader$5 +4 -1: time +25 -1: java/net/URLClassLoader$6 +25 -1: java/net/URLClassLoader$7 +14 -1: historicalName +12 -1: normalizeKey +13 -1: MIN_SURROGATE +41 -1: java/nio/charset/CharacterCodingException +18 -1: java/lang/Iterable +8 -1: ([JII)[J +103 -1: Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>; +18 -1: putBooleanVolatile +5 -1: Entry +11 -1: CounterCell +12 -1: monitorEnter +10 -1: skipBuffer +15 -1: hasMoreElements +24 -1: newIllegalStateException +24 -1: Ljava/lang/LinkageError; +12 -1: getEntryFlag +44 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;)V +18 -1: java/nio/IntBuffer +17 -1: jdk_minor_version +28 -1: DIRECTIONALITY_RIGHT_TO_LEFT +15 -1: getAndSetObject +7 -1: sizeCtl +15 -1: Ljava/util/Set; +32 -1: (I)Ljava/lang/invoke/LambdaForm; +16 -1: runFinalization0 +30 -1: Ljava/lang/reflect/Executable; +15 -1: compareUnsigned +5 -1: nkeys +31 -1: Ljava/nio/channels/FileChannel; +40 -1: sun/reflect/generics/tree/ClassSignature +22 -1: (Ljava/lang/Object;I)B +22 -1: (Ljava/lang/Object;I)C +22 -1: (Ljava/lang/Object;I)D +7 -1: JANUARY +30 -1: newGetIllegalArgumentException +22 -1: (Ljava/lang/Object;I)F +22 -1: (Ljava/lang/Object;I)I +22 -1: (Ljava/lang/Object;I)J +28 -1: generateNamedFunctionInvoker +38 -1: (Ljava/lang/String;)Ljava/time/ZoneId; +19 -1: lambda$codePoints$2 +4 -1: Null +7 -1: setEras +15 -1: lambda$chars$15 +14 -1: CollectionView +24 -1: (C)Ljava/io/PrintStream; +22 -1: (Ljava/lang/Object;I)S +96 -1: (Ljava/security/AccessControlContext;Lsun/security/util/Debug;Ljava/security/ProtectionDomain;)V +17 -1: getJulianCalendar +22 -1: (Ljava/lang/Object;I)V +17 -1: getMethodAccessor +9 -1: not owner +35 -1: java/lang/reflect/ParameterizedType +15 -1: ValueCollection +22 -1: (Ljava/lang/Object;I)Z +4 -1: utf8 +5 -1: CHINA +9 -1: sprintf0d +18 -1: java/nio/file/Path +35 -1: DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC +30 -1: URI has an authority component +43 -1: (Ljava/lang/Object;)Ljava/util/Spliterator; +15 -1: <no principals> +62 -1: (Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale; +6 -1: ([ZZ)V +10 -1: getContext +12 -1: content-type +99 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;ILjava/util/WeakHashMap$Entry;)V +51 -1: (Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;)V +27 -1: Invalid constant pool index +20 -1: getUnicodeLocaleType +43 -1: Ljava/nio/charset/CharacterCodingException; +113 -1: (Ljava/net/URL;Ljava/net/URLStreamHandler;Ljava/util/HashMap<Ljava/lang/String;Lsun/misc/URLClassPath$Loader;>;)V +56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; +24 -1: Lsun/misc/SignalHandler; +8 -1: readlink +125 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind<*>;[Ljava/nio/file/WatchEvent$Modifier;)Ljava/nio/file/WatchKey; +11 -1: initStatics +15 -1: unmappableCache +13 -1: fixMethodType +25 -1: sun/net/www/MessageHeader +3 -1: cdt +27 -1: Ljava/util/Locale$Category; +59 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object; +8 -1: Accessor +14 -1: mapLibraryName +54 -1: (Lsun/misc/URLClassPath$JarLoader;)Lsun/misc/JarIndex; +24 -1: ZoneOffsetTransitionRule +7 -1: getChar +3 -1: cee +25 -1: MapReduceValuesToLongTask +20 -1: declaredPublicFields +44 -1: (Ljava/lang/ClassLoader;Ljava/lang/String;)J +13 -1: removeElement +20 -1: Ljava/nio/ByteOrder; +8 -1: parallel +67 -1: (Ljava/lang/reflect/Constructor;Lsun/reflect/ConstructorAccessor;)V +22 -1: java/util/zip/ZipCoder +8 -1: ([ZIIZ)V +45 -1: (Ljava/lang/String;Ljava/lang/CharSequence;)Z +8 -1: february +40 -1: java/util/zip/ZipFile$ZipFileInputStream +47 -1: java/nio/charset/Charset$ExtendedProviderHolder +13 -1: IEEEremainder +15 -1: sun/misc/Perf$1 +9 -1: abstract +20 -1: ()Ljava/time/ZoneId; +7 -1: ONE_DAY +92 -1: (Ljava/util/concurrent/ConcurrentHashMap$Node;)Ljava/util/concurrent/ConcurrentHashMap$Node; +45 -1: (Ljava/util/ListIterator;I)Ljava/lang/Object; +16 -1: Buffer size <= 0 +9 -1: checkName +11 -1: getJarEntry +20 -1: Replacement too long +53 -1: (Ljava/lang/String;)Ljava/nio/charset/CharsetDecoder; +12 -1: isWhitespace +15 -1: csISOLatinGreek +70 -1: (Ljava/lang/reflect/Constructor<*>;Lsun/reflect/ConstructorAccessor;)V +20 -1: TypeAnnotationTarget +3 -1: No +27 -1: Lsun/reflect/FieldAccessor; +12 -1: doPrivileged +83 -1: (JLjava/util/function/ToIntFunction<-TV;>;ILjava/util/function/IntBinaryOperator;)I +60 -1: (Ljava/nio/file/attribute/FileTime;)Ljava/util/zip/ZipEntry; +11 -1: changeEntry +18 -1: MessageHeader.java +51 -1: ([Ljava/lang/String;Ljava/util/Map;)Ljava/util/Map; +9 -1: castEntry +16 -1: ACCESS_MODIFIERS +11 -1: newInstance +24 -1: lastIndexOfSupplementary +13 -1: JZENTRY_EXTRA +5 -1: LONGS +7 -1: domain +16 -1: protocolPathProp +21 -1: java/util/Hashtable$1 +10 -1: isSameDate +23 -1: (I)Ljava/nio/file/Path; +33 -1: java/util/PrimitiveIterator$OfInt +7 -1: ([II)[I +8 -1: variant= +29 -1: (Ljava/util/Collection<*>;Z)Z +38 -1: already loaded in another classloader +10 -1: setSeconds +9 -1: makeShort +59 -1: ClassLoader.findLibrary failed to return an absolute path: +26 -1: java/util/jar/JarException +9 -1: setCharAt +13 -1: initCauseFrom +13 -1: val$fieldName +18 -1: MIN_HIGH_SURROGATE +60 -1: (Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)B +25 -1: getDayOfWeekFromFixedDate +10 -1: maybeReBox +9 -1: getHandle +60 -1: (Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)I +59 -1: ([Ljava/lang/Class<*>;)Ljava/lang/reflect/Constructor<TT;>; +3 -1: cis +11 -1: getIssuerDN +9 -1: codebase= +80 -1: (ILjava/lang/invoke/BoundMethodHandle$SpeciesData;)Ljava/lang/invoke/LambdaForm; +29 -1: addThreadDumpForSynchronizers +6 -1: FRANCE +77 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>([TU;ILjava/lang/Class<+[TT;>;)[TT; +45 -1: <T:Ljava/lang/Object;>()Ljava/util/List<TT;>; +16 -1: putFloatVolatile +34 -1: (Ljava/lang/Class;)Ljava/util/Map; +28 -1: ()Ljava/security/Permission; +28 -1: (Ljava/lang/CharSequence;I)I +51 -1: ()Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>; +25 -1: (II)Ljava/nio/ByteBuffer; +20 -1: BasicPermission.java +5 -1: isNaN +48 -1: (Ljava/lang/Throwable;)Ljava/lang/InternalError; +10 -1: ONE_MINUTE +55 -1: (Ljava/lang/invoke/DirectMethodHandle$StaticAccessor;)J +13 -1: DAYS_IN_MONTH +7 -1: domains +10 -1: isUnshared +58 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Long;>; +47 -1: (Ljava/util/List;)Ljava/security/cert/CertPath; +28 -1: ()Ljava/util/Enumeration<*>; +34 -1: sun/misc/Launcher$ExtClassLoader$1 +22 -1: (CLjava/lang/Object;)Z +64 -1: Ljava/lang/ref/WeakReference<Ljava/nio/charset/CharsetDecoder;>; +7 -1: ENGLISH +27 -1: (Ljava/util/zip/Inflater;)V +24 -1: makeArrayElementAccessor +72 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType; +48 -1: (Ljava/util/jar/JarFile;)Ljava/util/Enumeration; +48 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodType; +15 -1: getNthDayOfWeek +16 -1: printHelpMessage +15 -1: getAbsoluteFile +9 -1: OPEN_READ +19 -1: willGMTOffsetChange +28 -1: (Ljava/util/LinkedHashMap;)V +37 -1: java/security/AllPermissionCollection +5 -1: [id=" +34 -1: java/lang/invoke/BoundMethodHandle +31 -1: ()Ljava/security/cert/CertPath; +50 -1: java/util/ArraysParallelSortHelpers$FJShort$Sorter +14 -1: StaticAccessor +22 -1: synchronizedCollection +53 -1: ([Ljava/io/File;)Ljava/security/AccessControlContext; +37 -1: (Ljava/lang/Class;Ljava/lang/Class;)Z +14 -1: codePointCount +13 -1: is param at +37 -1: Ljava/lang/invoke/MemberName$Factory; +20 -1: annotationDataOffset +31 -1: protocol doesn't support output +11 -1: hostAddress +12 -1: ,dstSavings= +35 -1: java.lang.Integer.IntegerCache.high +15 -1: ParallelLoaders +48 -1: (Ljava/util/Locale;)Lsun/util/locale/BaseLocale; +8 -1: getSize0 +22 -1: checkCreateClassLoader +8 -1: transfer +32 -1: (Lsun/misc/JavaSecurityAccess;)V +26 -1: ()Ljava/net/URLConnection; +3 -1: cmp +9 -1: setMillis +34 -1: sun/util/calendar/AbstractCalendar +19 -1: getDirectBufferPool +7 -1: ([FII)V +28 -1: ([C)Ljava/lang/StringBuffer; +54 -1: (Ljava/lang/Class<*>;)Ljava/security/ProtectionDomain; +26 -1: (Ljava/nio/ByteBuffer;IF)V +11 -1: discardMark +71 -1: (Ljava/lang/Class;)Lsun/util/locale/provider/LocaleServiceProviderPool; +30 -1: java/io/UTFDataFormatException +53 -1: (Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult; +48 -1: java/util/concurrent/ConcurrentHashMap$Traverser +5 -1: ([F)I +37 -1: (IC)Ljava/lang/AbstractStringBuilder; +27 -1: Ljava/util/jar/JarVerifier; +22 -1: java/util/Spliterators +32 -1: java/lang/invoke/MutableCallSite +20 -1: java/io/Serializable +5 -1: ([F)V +35 -1: (Ljava/security/ProtectionDomain;)V +33 -1: ()Ljava/lang/ref/Reference<+TT;>; +10 -1: unlinkLast +6 -1: (JSZ)V +8 -1: isStatic +14 -1: subclassAudits +23 -1: (Ljava/lang/String;)TT; +17 -1: java.awt.headless +9 -1: <Unknown> +39 -1: Lsun/util/locale/LocaleSyntaxException; +8 -1: location +3 -1: cos +27 -1: createGarbageCollectorMBean +20 -1: MAX_MH_INVOKER_ARITY +75 -1: (Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;Z)Ljava/nio/charset/CoderResult; +14 -1: Cloneable.java +50 -1: (Lsun/reflect/DelegatingConstructorAccessorImpl;)V +26 -1: sun/nio/ch/FileChannelImpl +51 -1: (Ljava/lang/Class;Ljava/lang/reflect/Constructor;)V +18 -1: Unknown byte order +28 -1: ()[Lsun/invoke/util/Wrapper; +21 -1: getReadClassBytesTime +64 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodTypeForm; +11 -1: setDelegate +20 -1: (Ljava/util/List;)[C +7 -1: usemmap +22 -1: (CC)Ljava/lang/String; +34 -1: sun/invoke/util/BytecodeDescriptor +21 -1: getJavaSecurityAccess +53 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult; +7 -1: ibm-737 +19 -1: (Ljava/lang/Enum;)I +4 -1: rint +11 -1: Constructor +9 -1: arraycopy +35 -1: ([D)Ljava/util/stream/DoubleStream; +13 -1: comparingLong +40 -1: <T:Ljava/lang/Object;>Ljava/lang/Object; +23 -1: OutputStreamWriter.java +8 -1: getShort +17 -1: CLASSPATH_LASTOCC +13 -1: createNewFile +14 -1: internalValues +34 -1: Ljava/lang/IllegalAccessException; +23 -1: (JILjava/lang/Object;)V +27 -1: sun.misc.URLClassPath.debug +45 -1: [Ljava/lang/ThreadLocal$ThreadLocalMap$Entry; +35 -1: ()Lsun/reflect/ConstructorAccessor; +12 -1: classEnabled +23 -1: cachedFixedDateNextJan1 +48 -1: (Ljava/util/Locale$LocaleKey;)Ljava/util/Locale; +26 -1: (Ljava/lang/ThreadGroup;)V +7 -1: setErr0 +6 -1: CENFLG +26 -1: (Ljava/lang/ThreadGroup;)Z +18 -1: sun/misc/MetaIndex +3 -1: crc +34 -1: (Z)Ljava/lang/invoke/MethodHandle; +12 -1: replaceNames +15 -1: java/util/Stack +57 -1: Ljava/util/Vector<Ljava/lang/ClassLoader$NativeLibrary;>; +89 -1: (JLjava/util/function/ToDoubleFunction<-TV;>;DLjava/util/function/DoubleBinaryOperator;)D +24 -1: permission can't be null +22 -1: Unable to connect to: +44 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer; +14 -1: floatToIntBits +11 -1: getLastRule +6 -1: EXTCRC +26 -1: java/net/InetSocketAddress +36 -1: (Lsun/misc/URLClassPath$JarLoader;)V +27 3: sun/launcher/LauncherHelper +8 -1: ecma-118 +49 -1: (Ljava/net/URL;Ljava/lang/String;)[Ljava/net/URL; +13 -1: hashCodeValue +5 -1: CESU8 +21 -1: appendVmSelectMessage +13 -1: bindImmediate +12 -1: closeLoaders +16 -1: emptySpliterator +28 -1: (J)Ljava/time/LocalDateTime; +22 -1: [Ljava/lang/Character; +5 -1: certs +6 -1: (null) +25 -1: java/io/ObjectInputStream +27 -1: ([Ljava/lang/ThreadGroup;)I +3 -1: cst +3 -1: csu +20 -1: java/nio/ShortBuffer +53 -1: (Ljava/lang/ClassValue;Ljava/lang/ClassValue$Entry;)V +4 -1: (Z)I +24 -1: Ljava/io/FileDescriptor; +6 -1: cclass +10 -1: , profile +39 -1: (Ljava/lang/String;)Ljava/lang/Process; +4 -1: (Z)V +5 -1: toURI +24 -1: ConstructorAccessor.java +5 -1: toURL +18 -1: addAllIfNotPresent +4 -1: (Z)Z +5 -1: parse +11 -1: isPrimitive +42 -1: (Ljava/io/File;)Ljava/lang/ProcessBuilder; +36 -1: (Ljava/util/Date;)Ljava/lang/String; +23 -1: getFormalTypeParameters +13 -1: Resource.java +7 -1: ibm-775 +6 -1: isEnum +24 -1: setJavaUtilZipFileAccess +21 -1: \t[CIRCULAR REFERENCE: +51 -1: (Ljava/lang/CharSequence;)Ljava/util/regex/Matcher; +24 -1: (I)Ljava/nio/ByteBuffer; +53 -1: sun/reflect/generics/repository/GenericDeclRepository +3 -1: xor +17 -1: invokeBasicMethod +27 -1: java/lang/invoke/LambdaForm +99 -1: (Ljava/util/jar/Manifest;Ljava/util/jar/JarEntry;Ljava/io/InputStream;Ljava/util/jar/JarVerifier;)V +46 -1: Lsun/reflect/generics/factory/GenericsFactory; +37 -1: sun/misc/Launcher$BootClassPathHolder +10 -1: BindCaller +24 -1: java/lang/reflect/Member +32 -1: java/lang/management/ThreadState +5 -1: (IB)V +21 -1: RuntimeException.java +5 -1: ended +17 -1: java/util/TreeSet +7 -1: : no !/ +16 -1: java/util/Vector +9 -1: nextAfter +22 -1: Ljava/lang/Deprecated; +20 -1: requestedCharsetName +37 -1: ([JIII)Ljava/util/Spliterator$OfLong; +14 -1: internArgument +11 -1: getTimeZone +10 -1: isValidKey +11 -1: LAST_RESULT +43 -1: sun/reflect/annotation/TypeAnnotationParser +13 -1: encodedInPath +52 -1: (Ljava/security/PrivilegedAction;)Ljava/lang/Object; +4 -1: LL_L +34 -1: java/nio/charset/CodingErrorAction +10 -1: copyFields +11 -1: getConstant +9 -1: threshold +13 -1: aliases_UTF_8 +27 -1: (Ljava/util/ArrayDeque;II)V +29 -1: Ljava/lang/ref/SoftReference; +19 -1: indexedBinarySearch +11 -1: containsKey +81 -1: ([Ljava/lang/ClassValue$Entry;Ljava/lang/ClassValue;)Ljava/lang/ClassValue$Entry; +86 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<*>;II)Ljava/lang/invoke/MethodHandle; +15 -1: cannot convert +25 -1: getSystemResourceAsStream +46 -1: (Ljava/util/Properties;)Ljava/util/Properties; +12 -1: reverseOrder +16 -1: getSystemPackage +8 -1: ([SII)[S +19 -1: makeAccessException +100 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;Ljava/util/function/Consumer<-TU;>;)V +39 -1: ([Ljava/lang/Class;I)[Ljava/lang/Class; +19 -1: Ljava/lang/Boolean; +6 -1: Hidden +47 -1: java/lang/invoke/MethodHandleImpl$ArrayAccessor +5 -1: APRIL +8 -1: emptySet +11 -1: getCombiner +58 -1: (Ljava/lang/String;I[Ljava/lang/invoke/LambdaForm$Name;I)V +24 -1: java.system.class.loader +14 -1: Can't handle: +16 -1: isNullConversion +38 -1: ()Ljava/util/HashMap$TreeNode<TK;TV;>; +29 -1: referenceKindIsConsistentWith +11 -1: flushBuffer +8 -1: putField +27 -1: ()Ljava/security/PublicKey; +53 -1: ()Ljava/util/stream/Stream<Ljava/util/jar/JarEntry;>; +10 -1: pathToURLs +26 -1: throwIllegalStateException +10 -1: markedChar +14 -1: isNativeMethod +36 -1: (I)Ljava/lang/AbstractStringBuilder; +54 -1: java/util/concurrent/ConcurrentHashMap$ReservationNode +45 -1: Lsun/misc/JavaSecurityProtectionDomainAccess; +62 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;I)V +7 -1: subList +8 -1: UTF_32BE +6 -1: U_None +50 -1: sun/reflect/generics/repository/AbstractRepository +62 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;I)Z +43 -1: sun/net/www/protocol/file/FileURLConnection +13 -1: setZoneOffset +43 -1: Underlying input stream returned zero bytes +54 -1: [a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)* +13 -1: containsValue +44 -1: (Ljava/nio/CharBuffer;)Ljava/nio/ByteBuffer; +25 -1: isNullReferenceConversion +38 -1: Ljava/util/Vector<Ljava/lang/String;>; +6 -1: toFile +7 -1: getSlot +17 -1: (Ljava/net/URI;)V +32 -1: java.security.cert.Certificate: +24 -1: ()Lsun/misc/PerfCounter; +11 -1: Asia/Hebron +16 -1: createMemoryPool +10 -1: addToCache +29 -1: Ljava/lang/invoke/DontInline; +39 -1: java/lang/ref/Finalizer$FinalizerThread +58 -1: (Ljava/lang/Class;)Lsun/reflect/generics/scope/ClassScope; +20 -1: getBooleanAttributes +15 -1: parallelLockMap +34 -1: java/util/Vector$VectorSpliterator +21 -1: createMemoryPoolMBean +15 -1: no content-type +41 -1: Couldn't find 3-letter language code for +5 -1: slash +34 -1: Ljava/lang/annotation/ElementType; +8 -1: isSetter +26 -1: (ZLjava/lang/String;JJJZ)V +6 -1: GMT_ID +73 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Constructor<TT;>;>; +56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object; +32 -1: (Ljava/util/Set;)Ljava/util/Set; +4 -1: stop +62 -1: (Ljava/lang/String;IILjava/lang/String;I)Ljava/nio/ByteBuffer; +11 -1: genericInfo +11 -1: listToArray +26 -1: ()Ljava/util/jar/Manifest; +13 -1: putOrderedInt +5 -1: flush +13 -1: ArrayAccessor +4 -1: Name +95 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;[Ljava/util/concurrent/ConcurrentHashMap$Node;)V +68 -1: (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +23 -1: java/lang/ref/Reference +37 -1: (Ljava/nio/file/attribute/FileTime;)J +14 -1: MIN_CODE_POINT +9 -1: ISO8859-1 +9 -1: ISO8859-2 +9 -1: ISO8859-5 +34 -1: ([CILjava/nio/charset/Charset;Z)[C +9 -1: ISO8859-9 +9 -1: getUTF8At +12 -1: java/net/URI +67 -1: (Ljava/io/DataInput;Ljava/lang/String;)Lsun/util/calendar/ZoneInfo; +22 -1: ListCompositionPattern +67 -1: (Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class; +3 1: xxx +12 -1: java/net/URL +27 -1: Can't overwrite cause with +30 -1: sun/util/locale/BaseLocale$Key +22 -1: forkSecondaryFinalizer +23 -1: java/security/Principal +8 -1: makeSite +17 -1: NEGATIVE_INFINITY +12 -1: addUnstarted +12 -1: internalForm +9 -1: cellsBusy +23 -1: (Ljava/lang/Object;IJ)V +13 -1: getParameters +6 -1: H_PATH +10 -1: L_REG_NAME +139 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable; +6 -1: latin0 +10 -1: addSeconds +6 -1: latin1 +6 -1: latin2 +6 -1: latin4 +6 -1: latin5 +17 -1: getWaitingThreads +6 -1: latin9 +21 -1: java/util/Comparators +10 -1: trimToSize +96 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;Ljava/lang/Object;)Ljava/util/Collection<TT;>; +43 -1: ([JLjava/util/function/IntToLongFunction;)V +23 -1: GET_COMBINER_PERMISSION +20 -1: lambda$replaceAll$14 +5 -1: KOREA +20 -1: getJvmSpecialVersion +9 -1: dumpStack +16 -1: CACHE_LOAD_LIMIT +43 -1: GSS LoginConfigImpl debugging +26 -1: (DLjava/lang/Appendable;)V +8 -1: forName0 +35 -1: java/lang/ClassLoader$NativeLibrary +46 -1: java/util/concurrent/ConcurrentHashMap$Segment +24 -1: getDeclaredAnnotationMap +89 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1 +20 -1: java_runtime_version +26 -1: java/util/stream/IntStream +16 -1: Ljava/io/Writer; +69 -1: ()Ljava/util/SortedMap<Ljava/lang/String;Ljava/nio/charset/Charset;>; +15 -1: getClassLoader0 +6 -1: x86_64 +11 -1: isInterface +15 -1: MODIFIER_SYMBOL +44 -1: Note: Separate multiple options with a comma +9 -1: recursive +19 -1: java/nio/CharBuffer +8 -1: capacity +17 -1: validateMainClass +50 -1: (Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set; +22 -1: (Ljava/lang/Object;J)B +22 -1: (Ljava/lang/Object;J)C +22 -1: (Ljava/lang/Object;J)D +17 -1: not a method type +22 -1: (Ljava/lang/Object;J)F +5 -1: count +32 -1: AtomicReferenceFieldUpdater.java +22 -1: (Ljava/lang/Object;J)I +14 -1: methodAccessor +22 -1: (Ljava/lang/Object;J)J +7 -1: isError +51 -1: (Ljava/nio/Buffer;II)Ljava/nio/charset/CoderResult; +53 -1: (BZLjava/lang/Class<*>;)Ljava/lang/invoke/LambdaForm; +25 -1: <no signer certificates> +5 -1: [pos= +14 -1: lambda$chars$1 +9 -1: CELLVALUE +16 -1: haveLeftoverChar +22 -1: ([Ljava/lang/String;)V +32 -1: java/lang/InstantiationException +7 -1: SIG_IGN +13 -1: ZipUtils.java +50 -1: Ljava/lang/ref/ReferenceQueue<Ljava/lang/Object;>; +22 -1: (Ljava/lang/Object;J)S +69 -1: Ljava/util/HashMap<Ljava/lang/String;Lsun/misc/URLClassPath$Loader;>; +16 -1: newInternalError +22 -1: (Ljava/lang/Object;J)V +9 -1: ABBR_MASK +5 -1: array +22 -1: (Ljava/lang/Object;J)Z +13 -1: FilteringMode +30 -1: java/util/stream/StreamSupport +19 -1: retrieveDisplayName +56 -1: (Ljava/util/TimeZone;)Lsun/util/calendar/Gregorian$Date; +10 -1: val$values +9 -1: normalize +28 -1: (II)Ljava/lang/CharSequence; +16 -1: serialVersionUID +7 -1: getPath +25 -1: (ILjava/lang/Class<*>;Z)V +13 -1: thenComparing +51 -1: (Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle; +36 -1: ()[Ljava/lang/annotation/Annotation; +14 -1: MetaIndex.java +8 -1: identity +15 -1: findSystemClass +24 -1: privateGetDeclaredFields +27 -1: java/lang/ref/SoftReference +19 -1: useCanonPrefixCache +3 -1: dec +3 -1: PLT +8 -1: UTF_32LE +17 -1: java/util/HashMap +12 -1: toEpochMilli +9 -1: intStream +11 -1: Caused by: +31 -1: java/nio/charset/CharsetDecoder +30 -1: is being checked +11 -1: parseHeader +25 -1: ACCUMULATED_DAYS_IN_MONTH +34 -1: newGetByteIllegalArgumentException +21 -1: checkPropertiesAccess +13 -1: StackMapTable +8 -1: addCount +51 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/String; +9 -1: authority +15 -1: iso-10646-ucs-2 +6 -1: SUNDAY +22 -1: LocalGregorianCalendar +9 -1: listRoots +32 -1: Lsun/reflect/generics/tree/Tree; +38 -1: [Ljava/util/WeakHashMap$Entry<TK;TV;>; +11 -1: nativeOrder +5 -1: long0 +5 -1: long1 +5 -1: long2 +5 -1: long3 +17 -1: capacityIncrement +5 -1: long4 +97 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName; +31 -1: Ljava/lang/CharacterDataLatin1; +5 -1: long5 +5 -1: long6 +5 -1: long7 +17 -1: reduceValuesToInt +13 -1: package2certs +13 -1: isTypeVisible +30 -1: java/lang/ref/PhantomReference +47 -1: ()Ljava/util/stream/Stream<Ljava/lang/String;>; +9 -1: longValue +3 -1: PNT +10 -1: storeToXML +10 -1: getMethod0 +12 -1: constantZero +7 -1: promise +116 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/MethodRepository; +32 -1: DIRECTIONALITY_SEGMENT_SEPARATOR +9 -1: byteOrder +9 -1: isPromise +4 -1: isOn +6 -1: LOCCRC +10 -1: setDefault +9 -1: setHandle +15 -1: java/nio/Buffer +37 -1: (Ljava/lang/String;I)Ljava/lang/Long; +10 -1: Float.java +12 -1: showSettings +27 -1: (Ljava/io/FileDescriptor;)I +27 -1: (Ljava/io/FileDescriptor;)J +21 -1: java/util/Spliterator +22 -1: CodingErrorAction.java +11 -1: isMalformed +27 -1: java/util/PrimitiveIterator +15 -1: THROW_EXCEPTION +15 -1: copyToCharArray +26 -1: ()Ljava/util/jar/JarEntry; +27 -1: (Ljava/io/FileDescriptor;)V +11 -1: getEncoding +48 -1: (Ljava/lang/ThreadLocal<*>;Ljava/lang/Object;I)V +17 -1: java.runtime.name +28 -1: (Lsun/invoke/util/Wrapper;)Z +20 -1: annotationTypeOffset +27 -1: (J)Ljava/lang/StringBuffer; +15 -1: METHOD_RECEIVER +10 -1: startEntry +29 -1: (I)Ljava/lang/reflect/Member; +7 -1: setOut0 +10 -1: getMethods +26 -1: ()Lsun/misc/JavaNioAccess; +18 -1: linkToTargetMethod +8 -1: INSTANCE +3 -1: dir +41 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;)V +9 -1: unboxCast +58 -1: <T:Ljava/lang/Throwable;>(TT;)Lsun/invoke/empty/Empty;^TT; +12 -1: java.version +50 -1: (Ljava/io/InputStream;Ljava/nio/charset/Charset;)V +32 2: sun/net/www/protocol/jar/Handler +20 -1: java/lang/Compiler$1 +9 -1: LongCache +14 -1: FILL_THRESHOLD +22 -1: getRawClassAnnotations +9 -1: (JI[CII)I +13 -1: hasMoreTokens +13 -1: getSuperclass +3 -1: PRC +12 -1: MAX_PRIORITY +14 -1: checkCacheLoad +7 -1: lowMask +8 -1: LM_CLASS +7 -1: initIDs +27 -1: Ljava/util/Collection<TV;>; +3 -1: yes +3 -1: PRT +91 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/LambdaForm; +27 -1: ()Lsun/security/util/Debug; +8 -1: VM start +9 -1: setMemory +7 -1: getName +10 -1: findSignal +19 -1: startsWithLocHeader +37 -1: java/util/Collections$SynchronizedMap +51 -1: (ICLjava/lang/Object;)Ljava/lang/invoke/LambdaForm; +62 -1: (Ljava/util/Locale;)Lsun/util/locale/provider/LocaleResources; +17 -1: lastParameterType +9 -1: NO_PTYPES +116 -1: <T:Ljava/lang/Object;>([Ljava/lang/ClassValue$Entry<*>;Ljava/lang/ClassValue<TT;>;)Ljava/lang/ClassValue$Entry<TT;>; +18 -1: DisplayNamePattern +8 -1: getField +5 -1: flags +3 -1: PST +17 -1: annotationDefault +18 -1: java/nio/ByteOrder +8 -1: highMask +6 -1: ascii7 +29 -1: getGregorianYearFromFixedDate +125 -1: (Ljava/lang/String;[Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)V +8 -1: =Lambda( +29 -1: Ljava/util/WeakHashMap$Entry; +18 -1: multiValueIterator +74 -1: Ljava/util/LinkedHashMap<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>; +13 -1: CONV_OP_LIMIT +6 -1: sclSet +81 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/util/jar/JarFile;)Ljava/util/jar/JarFile; +14 -1: appendFragment +46 -1: java/util/Collections$SynchronizedNavigableMap +36 -1: application/x-java-serialized-object +13 -1: setNativeName +53 -1: java/util/concurrent/ConcurrentHashMap$ForwardingNode +16 -1: setJavaAWTAccess +30 -1: methodHandleInvokeLinkerMethod +15 -1: reduceKeysToInt +20 -1: ensureCapacityHelper +23 -1: createFileURLConnection +12 -1: d3dAvailable +69 -1: (Ljava/lang/Object;Ljava/lang/invoke/MethodHandle;)Ljava/lang/String; +49 -1: java/util/ArraysParallelSortHelpers$FJLong$Sorter +21 -1: explicitCastArguments +24 -1: JAVAFX_LAUNCH_MODE_CLASS +9 -1: invoke_MT +18 -1: ensureMemberAccess +74 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)I +36 -1: java/nio/charset/spi/CharsetProvider +23 -1: (Ljava/lang/String;II)V +14 -1: initProperties +4 -1: (F)F +35 -1: [[Ljava/lang/annotation/Annotation; +4 -1: (F)I +106 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence; +46 -1: java/lang/invoke/BoundMethodHandle$SpeciesData +74 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z +24 -1: java.launcher.opt.header +37 -1: ([Ljava/security/ProtectionDomain;Z)V +10 -1: lineBuffer +7 -1: ibm-813 +9 -1: isBuiltin +4 -1: (F)V +7 -1: ibm-819 +9 -1: H_ESCAPED +4 -1: (F)Z +20 -1: suppressedExceptions +12 -1: UTF-32LE-BOM +19 -1: CalendarSystem.java +8 -1: readOnly +81 -1: (JLjava/util/function/Function;Ljava/util/function/BiFunction;)Ljava/lang/Object; +32 -1: sun/misc/Launcher$AppClassLoader +78 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/io/File;>; +17 -1: [Ljava/lang/Enum; +41 -1: java/util/Collections$CheckedNavigableSet +8 -1: asSetter +3 -1: dom +41 -1: (I)[Ljava/util/WeakHashMap$Entry<TK;TV;>; +16 -1: localeExtensions +21 -1: sun/net/www/ParseUtil +21 -1: Ljava/nio/ByteBuffer; +29 -1: java/util/concurrent/TimeUnit +25 -1: java/lang/CharacterData00 +15 -1: sun/misc/Unsafe +28 -1: java/io/ByteArrayInputStream +3 -1: dow +25 -1: java/lang/CharacterData01 +25 -1: java/lang/CharacterData02 +6 -1: STORED +11 -1: isTransient +8 -1: function +16 -1: getCanonicalFile +13 -1: ,useDaylight= +24 -1: domain (context is null) +12 -1: Cleaner.java +17 -1: CalendarDate.java +49 -1: Lsun/reflect/generics/repository/FieldRepository; +14 -1: forInputString +25 -1: java/lang/CharacterData0E +67 -1: (Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object; +19 -1: codePointBeforeImpl +6 -1: script +21 -1: systemNativeLibraries +38 -1: ([Ljava/lang/Class;)Ljava/lang/String; +14 -1: CONTENT_LENGTH +19 -1: HeapCharBuffer.java +22 -1: ExtendedProviderHolder +41 -1: java/lang/invoke/InvokerBytecodeGenerator +12 -1: basicInvoker +26 -1: ([Ljava/lang/Comparable;)V +10 -1: val$tclass +47 -1: (Ljava/lang/Throwable;)Lsun/invoke/empty/Empty; +18 -1: isLegalReplacement +22 -1: spliteratorUnknownSize +15 -1: SynchronizedSet +16 -1: MethodParameters +22 -1: desiredAssertionStatus +29 -1: ()Ljava/util/ArrayDeque<TE;>; +36 -1: ()Ljava/lang/reflect/Constructor<*>; +66 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/invoke/LambdaForm;>; +58 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V +50 -1: (Ljava/util/concurrent/CountedCompleter;[J[JIIII)V +14 -1: requireNonNull +21 -1: java/lang/ThreadGroup +95 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;)V +76 -1: (Ljava/nio/channels/WritableByteChannel;Ljava/nio/charset/CharsetEncoder;I)V +14 -1: reflectionData +33 -1: Ljava/lang/invoke/MethodTypeForm; +7 -1: tuesday +31 -1: ()Lsun/misc/JavaSecurityAccess; +14 -1: fieldFilterMap +28 -1: ([Ljava/lang/ThreadGroup;Z)I +7 -1: ibm-850 +83 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMXBean; +7 -1: ibm-852 +5 -1: cesu8 +14 -1: ForwardingNode +29 -1: (Ljava/nio/ByteBuffer;IIIII)V +7 -1: ibm-855 +12 -1: SingletonSet +16 -1: isOtherUppercase +15 -1: FIELD_UNDEFINED +9 -1: makeEntry +7 -1: ibm-857 +10 -1: extensions +10 -1: longStream +19 -1: getGenericSignature +7 -1: newNode +8 -1: jarNames +25 -1: java/util/jar/JarVerifier +49 -1: ()[Lsun/reflect/generics/tree/ClassTypeSignature; +4 -1: wait +115 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/ClassRepository; +56 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +65 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)V +7 -1: ibm-862 +9 -1: ISO646-US +7 -1: ibm-866 +16 -1: extendedProvider +7 -1: ([C[C)Z +93 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; +8 -1: getHours +21 -1: ()[Ljava/lang/String; +187 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceKeysTask;Ljava/util/function/BiFunction;)V +106 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/locks/ReentrantLock;Ljava/io/Serializable; +24 -1: java/nio/file/FileSystem +14 -1: ForEachKeyTask +13 -1: defaultLocale +20 -1: constructorModifiers +13 -1: asWrapperType +42 -1: (Ljava/lang/String;ZI)Ljava/lang/Class<*>; +17 -1: BaseCalendar.java +76 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)[Ljava/security/CodeSigner; +14 -1: isSynchronized +27 -1: java/nio/DirectByteBuffer$1 +3 -1: ()B +7 -1: ibm-874 +12 -1: exactInvoker +3 -1: ()C +39 -1: (Ljava/lang/Thread;Ljava/lang/Object;)V +3 -1: ()D +3 -1: ()F +27 -1: [Ljava/lang/reflect/Method; +10 -1: floatValue +3 -1: ()I +3 -1: ()J +18 -1: getLocaleResources +59 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesTask +67 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/Hashtable$Entry;)V +11 -1: maxPriority +11 -1: getStringAt +60 -1: (Ljava/lang/ClassValue$Version;)Ljava/lang/ClassValue$Entry; +3 -1: ()S +49 -1: (Ljava/lang/String;)Ljava/io/ExpiringCache$Entry; +42 -1: (III)Lsun/util/calendar/BaseCalendar$Date; +82 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;Ljava/lang/Object;)Ljava/util/Set<TT;>; +62 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/util/Map; +3 -1: ()V +24 -1: ()Ljava/nio/ShortBuffer; +29 -1: file descriptor can't be null +3 -1: ()Z +7 -1: matcher +66 -1: (Ljava/lang/reflect/Constructor;)Lsun/reflect/ConstructorAccessor; +7 -1: matches +12 -1: getAuthority +16 -1: java/lang/Object +5 -1: (IC)V +17 -1: EmptyListIterator +8 -1: charsets +8 -1: sameFile +47 -1: (TT;Ljava/util/function/UnaryOperator<TV;>;)TV; +16 -1: overwrittenEntry +15 -1: reinvokerTarget +11 -1: isUpperCase +5 -1: toUri +9 -1: GMT+00:00 +33 -1: java/util/concurrent/ForkJoinPool +10 -1: parseFloat +53 -1: java/util/concurrent/ConcurrentHashMap$SearchKeysTask +48 -1: (Ljava/lang/Object;Ljava/util/LinkedList$Node;)V +82 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;ILjava/lang/Class<*>;)V +15 -1: reduceCacheLoad +76 -1: (Ljava/lang/Class<*>;[Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method; +20 -1: singletonSpliterator +24 -1: getTransitionEpochSecond +24 -1: MapReduceValuesToIntTask +13 -1: ALLOWED_FLAGS +55 -1: (Ljava/lang/reflect/Field;Z)Lsun/reflect/FieldAccessor; +34 -1: [Ljava/lang/annotation/Annotation; +10 -1: readBuffer +21 -1: Illegal month value: +31 -1: java/security/SecureClassLoader +12 -1: reinitialize +5 -1: limit +4 -1: grow +15 -1: getCreationTime +7 -1: , from +25 -1: (Ljava/lang/ClassValue;)V +13 -1: java.compiler +37 -1: ()Ljava/util/Set<Ljava/lang/String;>; +6 -1: FJChar +16 -1: getFieldAccessor +4 -1: eras +11 -1: isSupported +24 -1: ()Ljava/text/DateFormat; +32 -1: java/util/Collections$CopiesList +32 -1: java/io/NotSerializableException +15 -1: typeAnnotations +27 -1: defaultAllowUserInteraction +57 -1: (Ljava/lang/String;I[Ljava/lang/invoke/LambdaForm$Name;)V +18 -1: checkArgumentTypes +71 -1: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar; +10 -1: isMirrored +27 -1: (I)Ljava/lang/Thread$State; +26 -1: (Ljava/util/Collection;Z)Z +6 -1: ibm367 +16 -1: isAssignableFrom +7 -1: readUTF +35 -1: Ljava/lang/ref/ReferenceQueue<TV;>; +56 -1: ([Ljava/util/HashMap$Node;Ljava/util/HashMap$TreeNode;)V +8 -1: MANDATED +18 -1: canonicalizeRegion +11 -1: checkAccept +44 -1: (Ljava/net/Proxy;)Lsun/net/ApplicationProxy; +8 -1: ECMA-118 +22 -1: ReflectPermission.java +4 -1: _put +41 -1: java.lang.invoke.MethodHandle.DEBUG_NAMES +47 -1: java/util/concurrent/ConcurrentHashMap$TreeNode +44 -1: (Ljava/net/URL;[Ljava/security/CodeSigner;)V +5 -1: april +44 -1: ([Ljava/lang/Class<*>;Ljava/lang/Class<*>;)V +150 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;TK;>;Ljava/util/Set<TK;>;Ljava/io/Serializable; +91 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode; +26 -1: sun/net/util/IPAddressUtil +8 -1: Modifier +9 -1: isVarargs +24 -1: -- listing properties -- +16 -1: hasAllPermission +27 -1: MapReduceMappingsToLongTask +29 -1: sharedGetParameterAnnotations +9 -1: argCounts +11 -1: toLocalTime +89 -1: (Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName; +4 -1: ROOT +20 -1: sun.reflect.noCaches +18 -1: UnicodeBigUnmarked +4 -1: Lazy +35 -1: java/lang/invoke/SimpleMethodHandle +20 -1: (I)Ljava/nio/Buffer; +48 -1: ()Lsun/reflect/generics/tree/ClassTypeSignature; +31 -1: (I[CII)Ljava/lang/StringBuffer; +17 -1: America/Anchorage +7 -1: markpos +9 -1: enumerate +11 -1: parseLocale +24 -1: java.launcher.cls.error1 +46 -1: (ILjava/lang/Object;)Ljava/lang/StringBuilder; +24 -1: java.launcher.cls.error2 +24 -1: java.launcher.cls.error3 +24 -1: java.launcher.cls.error4 +21 -1: java/util/ArrayList$1 +17 -1: getExceptionTypes +24 -1: java.launcher.cls.error5 +30 -1: java/util/Spliterator$OfDouble +10 -1: forDecoder +8 -1: getEntry +10 -1: checkGuard +12 -1: checkInitted +34 -1: Lsun/util/locale/LocaleExtensions; +41 -1: java/util/ArraysParallelSortHelpers$FJInt +10 -1: findStatic +22 -1: setConstructorAccessor +34 -1: Lsun/misc/URLClassPath$FileLoader; +20 -1: not a reinvoker MH: +16 -1: LongCumulateTask +11 -1: checkAccess +14 -1: SearchKeysTask +36 -1: ()[Ljava/lang/reflect/AnnotatedType; +11 -1: initDefault diff --git a/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java b/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java new file mode 100644 index 00000000000..88a346ff850 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, 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 + * @summary Test for field annotations. + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/FieldAnnotationsApp.java test-classes/MyAnnotation.java + * @run main FieldAnnotationsTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +// This is a test for the handling of multi-dimensional Arrays in MetaspaceClosure. +// +// We choose FieldAnnotations because they happen to be implemented as a multi-dimension +// Array (Annotations::_fields_annotations, which is of type Array<Array<unsigned char>*>*, +// and is handled by the template class PointerArrayRef<T> in metaspaceClosure.hpp). +// +// Specifically, we are testing the following C code, where _fields_annotations is non-NULL: +// +// void Annotations::metaspace_pointers_do(MetaspaceClosure* it) { +// ... +// it->push(&_fields_annotations); +// +// which will be matched with the function +// +// template <typename T> void MetaspaceClosure::push(Array<T*>** mpp, Writability w = _default) +// +public class FieldAnnotationsTest { + public static void main(String[] args) throws Exception { + String[] ARCHIVE_CLASSES = {"FieldAnnotationsApp", "MyAnnotation"}; + String appJar = JarBuilder.build("FieldAnnotationsTest", ARCHIVE_CLASSES); + + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, ARCHIVE_CLASSES); + TestCommon.checkDump(dumpOutput); + + OutputAnalyzer execOutput = TestCommon.exec(appJar, "FieldAnnotationsApp"); + TestCommon.checkExec(execOutput, "Field annotations are OK."); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java b/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java new file mode 100644 index 00000000000..30f167cdf20 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017, 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 + * @summary Unused metadata created during dump time should be freed from the CDS archive. + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules jdk.jartool/sun.tools.jar + * @compile test-classes/MethodNoReturn.jasm test-classes/Hello.java + * @run main FreeUnusedMetadata + */ + +import java.nio.file.Files; +import java.nio.file.Paths; +import jdk.test.lib.process.OutputAnalyzer; + +public class FreeUnusedMetadata { + static byte iconst_1 = 4; + static byte pop = 87; + static byte[] pattern = { // This has the same sequence as in test-classes/MethodNoReturn.jasm + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + pop, + iconst_1, + iconst_1, + iconst_1, + iconst_1, + iconst_1, + iconst_1, + iconst_1, + iconst_1, + pop, + pop, + pop, + pop, + pop, + pop, + pop, + pop + }; + + public static void main(String[] args) throws Exception { + String[] ARCHIVE_CLASSES = {"Hello", "MethodNoReturn"}; + String appJar = JarBuilder.build("FreeUnusedMetadata", ARCHIVE_CLASSES); + + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, ARCHIVE_CLASSES); + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + + OutputAnalyzer execOutput = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(execOutput, "Hello World"); + + + String archive = TestCommon.getCurrentArchiveName(); + System.out.println("Checking for pattern inside " + archive + "..."); + + byte[] data = Files.readAllBytes(Paths.get(archive)); + int max = data.length - pattern.length; + for (int i=0; i<max; i++) { + if (data[i+0] == iconst_1 && data[i+1] == pop && + data[i+2] == iconst_1 && data[i+3] == pop) { + boolean match = true; + for (int x=4; x<pattern.length; x++) { + if (data[i+x] != pattern[x]) { + match = false; + break; + } + } + + if (match) { + throw new RuntimeException("method of unverifiable class should have been " + + "removed from the archive " + archive + + " , but was found at offset " + i); + } + } + } + System.out.println("Not found: method from unverifiable class has been removed"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java b/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java new file mode 100644 index 00000000000..3dba32bfbd0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary a simple test for loading a class using the ext class loader in AppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile test-classes/HelloExt.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main HelloExtTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class HelloExtTest { + + public static void main(String[] args) throws Exception { + JarBuilder.build("helloExt", "HelloExt"); + + String appJar = TestCommon.getTestJar("helloExt.jar"); + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar; + + TestCommon.dump(appJar, + TestCommon.list("org/omg/CORBA/ORB", "[Ljava/lang/Comparable;"), + bootClassPath, "-verbose:class", "--add-modules", "java.corba"); + + OutputAnalyzer output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, bootClassPath, "-verbose:class", "--add-modules", "java.corba", "HelloExt"); + + String prefix = ".class.load. "; + String class_pattern = ".*LambdaForm[$]MH[/][0123456789].*"; + String suffix = ".*source: shared objects file.*"; + String pattern = prefix + class_pattern + suffix; + output.shouldNotMatch(pattern); + + output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, bootClassPath, "-verbose:class", + "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary", + "--add-modules", "java.corba", "HelloExt"); + output.shouldNotMatch(class_pattern); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/HelloTest.java b/test/hotspot/jtreg/runtime/appcds/HelloTest.java new file mode 100644 index 00000000000..eee29f575ad --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/HelloTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Hello World test for AppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main HelloTest + */ + +public class HelloTest { + + public static void main(String[] args) throws Exception { + TestCommon.test(JarBuilder.getOrCreateHelloJar(), + TestCommon.list("Hello"), "Hello"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java b/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java new file mode 100644 index 00000000000..8bf4dec9326 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Test the -XX:+IgnoreEmptyClassPaths flag + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @compile test-classes/HelloMore.java + * @run main IgnoreEmptyClassPaths + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class IgnoreEmptyClassPaths { + + public static void main(String[] args) throws Exception { + String jar1 = JarBuilder.getOrCreateHelloJar(); + String jar2 = JarBuilder.build("IgnoreEmptyClassPaths_more", "HelloMore"); + + String sep = File.pathSeparator; + String cp_dump = jar1 + sep + jar2 + sep; + String cp_exec = sep + jar1 + sep + sep + jar2 + sep; + + TestCommon.testDump(cp_dump, TestCommon.list("Hello", "HelloMore"), + "-XX:+TraceClassPaths", "-XX:+IgnoreEmptyClassPaths"); + + OutputAnalyzer output = TestCommon.execCommon( + "-verbose:class", + "-cp", cp_exec, + "-XX:+IgnoreEmptyClassPaths", // should affect classpath even if placed after the "-cp" argument + "-XX:+TraceClassPaths", + "HelloMore"); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/JarBuilder.java b/test/hotspot/jtreg/runtime/appcds/JarBuilder.java new file mode 100644 index 00000000000..bb3921e7281 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/JarBuilder.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +/* + * @summary Simple jar builder + * Input: jarName className1 className2 ... + * do not specify extensions, just the names + * E.g. prot_domain ProtDomainA ProtDomainB + * Output: A jar containing compiled classes, placed in a test classes folder + * @library /open/test/lib + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import java.io.File; +import java.util.ArrayList; +import sun.tools.jar.Main; + +public class JarBuilder { + // to turn DEBUG on via command line: -DJarBuilder.DEBUG=[true, TRUE] + private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("JarBuilder.DEBUG", "false")); + private static final String classDir = System.getProperty("test.classes"); + + public static String getJarFilePath(String jarName) { + return classDir + File.separator + jarName + ".jar"; + } + + // jar all files under dir, with manifest file man, with an optional versionArgs + // for generating a multi-release jar. + // The jar command is as follows: + // jar cmf \ + // <path to output jar> <path to the manifest file>\ + // -C <path to the base classes> .\ + // --release 9 -C <path to the versioned classes> . + // the last line begins with "--release" corresponds to the optional versionArgs. + public static void build(String jarName, File dir, String man, String ...versionArgs) + throws Exception { + ArrayList<String> args = new ArrayList<String>(); + if (man != null) { + args.add("cfm"); + } else { + args.add("cf"); + } + args.add(classDir + File.separator + jarName + ".jar"); + if (man != null) { + args.add(man); + } + args.add("-C"); + args.add(dir.getAbsolutePath()); + args.add("."); + for (String verArg : versionArgs) { + args.add(verArg); + } + createJar(args); + } + + public static String build(String jarName, String ...classNames) + throws Exception { + + return createSimpleJar(classDir, getJarFilePath(jarName), classNames); + } + + public static String build(boolean classesInWorkDir, String jarName, String ...classNames) + throws Exception { + if (classesInWorkDir) { + return createSimpleJar(".", getJarFilePath(jarName), classNames); + } else { + return build(jarName, classNames); + } + } + + + public static String buildWithManifest(String jarName, String manifest, + String jarClassesDir, String ...classNames) throws Exception { + String jarPath = getJarFilePath(jarName); + ArrayList<String> args = new ArrayList<String>(); + args.add("cvfm"); + args.add(jarPath); + args.add(System.getProperty("test.src") + File.separator + "test-classes" + + File.separator + manifest); + addClassArgs(args, jarClassesDir, classNames); + createJar(args); + + return jarPath; + } + + + // Execute: jar uvf $jarFile -C $dir . + static void update(String jarFile, String dir) throws Exception { + String jarExe = JDKToolFinder.getJDKTool("jar"); + + ArrayList<String> args = new ArrayList<>(); + args.add(jarExe); + args.add("uvf"); + args.add(jarFile); + args.add("-C"); + args.add(dir); + args.add("."); + + executeProcess(args.toArray(new String[1])); + } + + + private static String createSimpleJar(String jarclassDir, String jarName, + String[] classNames) throws Exception { + + ArrayList<String> args = new ArrayList<String>(); + args.add("cf"); + args.add(jarName); + addClassArgs(args, jarclassDir, classNames); + createJar(args); + + return jarName; + } + + private static void addClassArgs(ArrayList<String> args, String jarclassDir, + String[] classNames) { + + for (String name : classNames) { + args.add("-C"); + args.add(jarclassDir); + args.add(name + ".class"); + } + } + + private static void createJar(ArrayList<String> args) { + if (DEBUG) printIterable("createJar args: ", args); + + Main jarTool = new Main(System.out, System.err, "jar"); + if (!jarTool.run(args.toArray(new String[1]))) { + throw new RuntimeException("jar operation failed"); + } + } + + // Many AppCDS tests use the same simple "Hello.jar" which contains + // simple Hello.class and does not specify additional attributes. + // For this common use case, use this method to get the jar path. + // The method will check if the jar already exists + // (created by another test or test run), and will create the jar + // if it does not exist + public static String getOrCreateHelloJar() throws Exception { + String jarPath = getJarFilePath("hello"); + + File jarFile = new File(jarPath); + if (jarFile.exists()) { + return jarPath; + } else { + return build("hello", "Hello"); + } + } + + public static void compile(String dstPath, String source, String... extraArgs) throws Exception { + ArrayList<String> args = new ArrayList<String>(); + args.add(JDKToolFinder.getCompileJDKTool("javac")); + args.add("-d"); + args.add(dstPath); + if (extraArgs != null) { + for (String s : extraArgs) { + args.add(s); + } + } + args.add(source); + + if (DEBUG) printIterable("compile args: ", args); + + ProcessBuilder pb = new ProcessBuilder(args); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + } + + public static void signJar() throws Exception { + String keyTool = JDKToolFinder.getJDKTool("keytool"); + String jarSigner = JDKToolFinder.getJDKTool("jarsigner"); + String classDir = System.getProperty("test.classes"); + String FS = File.separator; + + executeProcess(keyTool, + "-genkey", "-keystore", "./keystore", "-alias", "mykey", + "-storepass", "abc123", "-keypass", "abc123", + "-dname", "CN=jvmtest") + .shouldHaveExitValue(0); + + executeProcess(jarSigner, + "-keystore", "./keystore", "-storepass", "abc123", "-keypass", + "abc123", "-signedjar", classDir + FS + "signed_hello.jar", + classDir + FS + "hello.jar", "mykey") + .shouldHaveExitValue(0); + } + + private static OutputAnalyzer executeProcess(String... cmds) + throws Exception { + + JarBuilder.printArray("executeProcess: ", cmds); + return ProcessTools.executeProcess(new ProcessBuilder(cmds)); + } + + // diagnostic + public static void printIterable(String msg, Iterable<String> l) { + StringBuilder sum = new StringBuilder(); + for (String s : l) { + sum.append(s).append(' '); + } + System.out.println(msg + sum.toString()); + } + + public static void printArray(String msg, String[] l) { + StringBuilder sum = new StringBuilder(); + for (String s : l) { + sum.append(s).append(' '); + } + System.out.println(msg + sum.toString()); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java b/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java new file mode 100644 index 00000000000..12d41a2c066 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary JvmtiEnv::AddToBootstrapClassLoaderSearch and JvmtiEnv::AddToSystemClassLoaderSearch should disable AppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @bug 8060592 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @compile test-classes/Hello.java + * @compile test-classes/JvmtiApp.java + * @run main JvmtiAddPath + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +public class JvmtiAddPath { + static String use_whitebox_jar; + static String[] no_extra_matches = {}; + static String[] check_appcds_enabled = { + "[class,load] ExtraClass source: shared object" + }; + static String[] check_appcds_disabled = { + "[class,load] ExtraClass source: file:" + }; + + static void run(String cp, String... args) throws Exception { + run(no_extra_matches, cp, args); + } + + static void run(String[] extra_matches, String cp, String... args) throws Exception { + String[] opts = {"-cp", cp, "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", use_whitebox_jar}; + opts = TestCommon.concat(opts, args); + OutputAnalyzer output = TestCommon.execCommon(opts); + TestCommon.checkExec(output, extra_matches); + } + + public static void main(String[] args) throws Exception { + JarBuilder.build("jvmti_addboot", "Hello"); + JarBuilder.build("jvmti_addapp", "Hello"); + JarBuilder.build("jvmti_app", "JvmtiApp", "ExtraClass"); + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + + // In all the test cases below, appJar does not contain Hello.class. Instead, we + // append JAR file(s) that contain Hello.class to the boot classpath, the app + // classpath, or both, and verify that Hello.class is loaded by the expected ClassLoader. + String appJar = TestCommon.getTestJar("jvmti_app.jar"); // contains JvmtiApp.class + String addappJar = TestCommon.getTestJar("jvmti_addapp.jar"); // contains Hello.class + String addbootJar = TestCommon.getTestJar("jvmti_addboot.jar"); // contains Hello.class + String twoAppJars = appJar + File.pathSeparator + addappJar; + String wbJar = TestCommon.getTestJar("WhiteBox.jar"); + use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + TestCommon.testDump(appJar, TestCommon.list("JvmtiApp", "ExtraClass"), use_whitebox_jar); + + System.out.println("Test case 1: not adding any paths - Hello.class should not be found"); + run(check_appcds_enabled, appJar, "-Xlog:class+load", "JvmtiApp", "noadd"); // appcds should be enabled + + System.out.println("Test case 2: add to boot classpath only - should find Hello.class in boot loader"); + run(check_appcds_disabled, appJar, "-Xlog:class+load", "JvmtiApp", "bootonly", addbootJar); // appcds should be disabled + + System.out.println("Test case 3: add to app classpath only - should find Hello.class in app loader"); + run(appJar, "JvmtiApp", "apponly", addappJar); + + System.out.println("Test case 4: add to boot and app paths - should find Hello.class in boot loader"); + run(appJar, "JvmtiApp", "appandboot", addbootJar, addappJar); + + System.out.println("Test case 5: add to app using -cp, but add to boot using JVMTI - should find Hello.class in boot loader"); + run(twoAppJars, "JvmtiApp", "bootonly", addappJar); + + System.out.println("Test case 6: add to app using AppCDS, but add to boot using JVMTI - should find Hello.class in boot loader"); + TestCommon.testDump(twoAppJars, TestCommon.list("JvmtiApp", "ExtraClass", "Hello"), use_whitebox_jar); + run(twoAppJars, "JvmtiApp", "bootonly", addappJar); + + System.out.println("Test case 7: add to app using AppCDS, no JVMTI calls - should find Hello.class in app loader"); + run(twoAppJars, "JvmtiApp", "noadd-appcds"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java b/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java new file mode 100644 index 00000000000..085e79e622d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Try different combination of mismatched UseAppCDS between dump time and run time. + * (Note: AppCDS does not support uncompressed oops.) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/CheckIfShared.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main MismatchedUseAppCDS + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class MismatchedUseAppCDS { + public static void main(String[] args) throws Exception { + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String appJar = JarBuilder.build("MismatchedUseAppCDS", "CheckIfShared"); + + OutputAnalyzer output; + + // (1): dump with -XX:+UseAppCDS, but run with -XX:-UseAppCDS + TestCommon.testDump(appJar, TestCommon.list("CheckIfShared"), + // command-line arguments ... + "-XX:+UseAppCDS", + use_whitebox_jar); + + output = TestCommon.exec(appJar, + // command-line arguments ... + use_whitebox_jar, + "-XX:-UseAppCDS", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "CheckIfShared", "false"); + TestCommon.checkExec(output); + + // (2): dump with -XX:-UseAppCDS, but run with -XX:+UseAppCDS + TestCommon.testDump(appJar, TestCommon.list("CheckIfShared"), + // command-line arguments ... + "-XX:-UseAppCDS", + use_whitebox_jar); + + output = TestCommon.exec(appJar, + // command-line arguments ... + use_whitebox_jar, + "-XX:+UseAppCDS", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "CheckIfShared", "false"); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java b/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java new file mode 100644 index 00000000000..fa9bfb93493 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary When super class is missing during dumping, no crash should happen. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/MissingSuper.java + * @run main MissingSuperTest + */ + +public class MissingSuperTest { + + public static void main(String[] args) throws Exception { + // The classes "MissingSuperSup" and "MissingSuperIntf" are intentionally not + // included into the jar to provoke the test condition + JarBuilder.build("missing_super", "MissingSuper", + "MissingSuperSub", "MissingSuperImpl"); + + String appJar = TestCommon.getTestJar("missing_super.jar"); + TestCommon.test(appJar, TestCommon.list("MissingSuper", + "MissingSuperSub", + "MissingSuperImpl"), + "MissingSuper"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java b/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java new file mode 100644 index 00000000000..25861868344 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Run multiple processes with the same archive, ensure they share + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @compile test-classes/MultiProcClass.java + * @run main MultiProcessSharing + */ + +import java.io.File; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + + +public class MultiProcessSharing { + static String useWbJar; + static String sharedClass1Jar; + static boolean checkPmap = false; + + public static void main(String[] args) throws Exception { + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + useWbJar = "-Xbootclasspath/a:" + wbJar; + sharedClass1Jar = JarBuilder.build("shared_class1", "MultiProcClass"); + + // create an archive + OutputAnalyzer out = TestCommon.dump(sharedClass1Jar, + TestCommon.list("MultiProcClass"), useWbJar); + TestCommon.checkDump(out); + + // determine whether OK to use pmap for extra test verification + long myPid = ProcessHandle.current().pid(); + checkPmap = (Platform.isLinux() && (MultiProcClass.runPmap(myPid, false) == 0)); + System.out.println("MultiProcessSharing: checkPmap is " + checkPmap); + + // use an archive in several processes concurrently + int numProcesses = 3; + Thread[] threads = new Thread[numProcesses]; + ProcessHandler[] processHandlers = new ProcessHandler[numProcesses]; + for (int i = 0; i < numProcesses; i++) { + processHandlers[i] = new ProcessHandler(i); + threads[i] = new Thread(processHandlers[i]); + } + + for (Thread t : threads) { + t.start(); + } + + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ie) { + throw ie; + } + } + + // check results + for (ProcessHandler ph : processHandlers) { + TestCommon.checkExec(ph.out); + if (checkPmap && !TestCommon.isUnableToMap(ph.out)) { + checkPmapOutput(ph.out.getOutput()); + } + } + } + + + static class ProcessHandler implements Runnable { + int processNumber; + OutputAnalyzer out; + + ProcessHandler(int processNumber) { + this.processNumber = processNumber; + } + + @Override + public void run() { + try { + out = TestCommon.exec(sharedClass1Jar, + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", useWbJar, + "MultiProcClass", "" + processNumber, "" + checkPmap); + } catch (Exception e) { + throw new RuntimeException("Error occurred when using archive, exec()" + e); + } + } + } + + + private static void checkPmapOutput(String stdio) { + System.out.println("Checking pmap output ..."); + String[] lines = stdio.split("\n"); + + boolean foundJsa = false; + boolean foundReadOnlyJsaSection = false; + + for (String line : lines) { + if (line.contains(TestCommon.getCurrentArchiveName())) + System.out.println(line); + foundJsa = true; + if (line.contains("r--")) { + foundReadOnlyJsaSection = true; + } + + // On certain ARM platforms system maps r/o memory mapped files + // as r/x; see JDK-8145694 for details + if ( (Platform.isARM() || Platform.isAArch64()) && line.contains("r-x") ) { + foundReadOnlyJsaSection = true; + } + } + + Asserts.assertTrue(foundJsa && foundReadOnlyJsaSection); + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java b/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java new file mode 100644 index 00000000000..fdc6ef06492 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016, 2017, 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 MultiReleaseJars + * @bug 8170105 + * @summary Test multi-release jar with AppCDS. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @run main/othervm MultiReleaseJars + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.io.IOException; +import jdk.test.lib.process.OutputAnalyzer; + +public class MultiReleaseJars { + + static final int MAJOR_VERSION = Runtime.version().major(); + static final String MAJOR_VERSION_STRING = String.valueOf(Runtime.version().major()); + + static String[] getMain() { + String[] sts = { + "package version;", + "public class Main {", + " public static void main(String[] args) {", + " Version version = new Version();", + " System.out.println(\"I am running on version \" + version.getVersion());", + " }", + "}" + }; + return sts; + } + + static String[] getVersion(int version) { + String[] sts = { + "package version;", + "public class Version {", + " public int getVersion(){ return " + version + "; }", + "}" + }; + return sts; + } + + static void writeFile(File file, String... contents) throws Exception { + if (contents == null) { + throw new java.lang.RuntimeException("No input for writing to file" + file); + } + FileOutputStream fos = new FileOutputStream(file); + PrintStream ps = new PrintStream(fos); + for (String str : contents) { + ps.println(str); + } + ps.close(); + fos.close(); + } + + /* version.jar entries and files: + * META-INF/ + * META-INF/MANIFEST.MF + * version/ + * version/Main.class + * version/Version.class + * META-INF/versions/ + * META-INF/versions/<major-version>/ + * META-INF/versions/<major-version>/version/ + * META-INF/versions/<major-version>/version/Version.class + */ + static void createClassFilesAndJar() throws Exception { + String tempDir = System.getProperty("test.classes"); + File baseDir = new File(tempDir + File.separator + "base"); + File vDir = new File(tempDir + File.separator + MAJOR_VERSION_STRING); + + baseDir.mkdirs(); + vDir.mkdirs(); + + File fileMain = TestCommon.getOutputSourceFile("Main.java"); + writeFile(fileMain, getMain()); + + File fileVersion = TestCommon.getOutputSourceFile("Version.java"); + writeFile(fileVersion, getVersion(7)); + JarBuilder.compile(baseDir.getAbsolutePath(), fileVersion.getAbsolutePath(), "--release", "7"); + JarBuilder.compile(baseDir.getAbsolutePath(), fileMain.getAbsolutePath(), + "-cp", baseDir.getAbsolutePath(), "--release", MAJOR_VERSION_STRING); + + String[] meta = { + "Multi-Release: true", + "Main-Class: version.Main" + }; + File metainf = new File(tempDir, "mf.txt"); + writeFile(metainf, meta); + + fileVersion = TestCommon.getOutputSourceFile("Version.java"); + writeFile(fileVersion, getVersion(MAJOR_VERSION)); + JarBuilder.compile(vDir.getAbsolutePath(), fileVersion.getAbsolutePath(), "--release", MAJOR_VERSION_STRING); + + JarBuilder.build("version", baseDir, metainf.getAbsolutePath(), + "--release", MAJOR_VERSION_STRING, "-C", vDir.getAbsolutePath(), "."); + + // the following jar file is for testing case-insensitive "Multi-Release" + // attibute name + String[] meta2 = { + "multi-Release: true", + "Main-Class: version.Main" + }; + metainf = new File(tempDir, "mf2.txt"); + writeFile(metainf, meta2); + JarBuilder.build("version2", baseDir, metainf.getAbsolutePath(), + "--release", MAJOR_VERSION_STRING, "-C", vDir.getAbsolutePath(), "."); + } + + static void checkExecOutput(OutputAnalyzer output, String expectedOutput) throws Exception { + try { + TestCommon.checkExec(output, expectedOutput); + } catch (java.lang.RuntimeException re) { + String cause = re.getMessage(); + if (!expectedOutput.equals(cause)) { + throw re; + } + } + } + + public static void main(String... args) throws Exception { + // create version.jar which contains Main.class and Version.class. + // Version.class has two versions: 8 and the current version. + createClassFilesAndJar(); + + String mainClass = "version.Main"; + String loadInfo = "[class,load] version.Version source: shared objects file"; + String appClasses[] = {"version/Main", "version/Version"}; + String appJar = TestCommon.getTestJar("version.jar"); + String appJar2 = TestCommon.getTestJar("version2.jar"); + String verboseMode = "-verbose:class"; + String enableMultiRelease = "-Djdk.util.jar.enableMultiRelease=true"; + String jarVersion = null; + String expectedOutput = null; + + // 1. default to highest version + // if META-INF/versions exists, no other commandline options like -Djdk.util.jar.version and + // -Djdk.util.jar.enableMultiRelease passed to vm + OutputAnalyzer output = TestCommon.dump(appJar, appClasses); + output.shouldContain("Loading classes to share: done."); + output.shouldHaveExitValue(0); + + output = TestCommon.exec(appJar, verboseMode, mainClass); + checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING); + + // 2. Test versions 7 and the current major version. + // -Djdk.util.jar.enableMultiRelease=true (or force), default is true. + // a) -Djdk.util.jar.version=7 does not exist in jar. + // It will fallback to the root version which is also 7 in this test. + // b) -Djdk.util.jar.version=MAJOR_VERSION exists in the jar. + for (int i : new int[] {7, MAJOR_VERSION}) { + jarVersion = "-Djdk.util.jar.version=" + i; + expectedOutput = "I am running on version " + i; + output = TestCommon.dump(appJar, appClasses, enableMultiRelease, jarVersion); + output.shouldContain("Loading classes to share: done."); + output.shouldHaveExitValue(0); + + output = TestCommon.exec(appJar, verboseMode, mainClass); + checkExecOutput(output, expectedOutput); + } + + // 3. For unsupported version, 5 and current major version + 1, the multiversion + // will be turned off, so it will use the default (root) version. + for (int i : new int[] {5, MAJOR_VERSION + 1}) { + jarVersion = "-Djdk.util.jar.version=" + i; + output = TestCommon.dump(appJar, appClasses, enableMultiRelease, jarVersion); + output.shouldHaveExitValue(0); + // With the fix for 8172218, multi-release jar is being handled in + // jdk corelib which doesn't emit the following warning message. + //output.shouldContain("JDK" + i + " is not supported in multiple version jars"); + + output = TestCommon.exec(appJar, verboseMode, mainClass); + if (i == 5) + checkExecOutput(output, "I am running on version 7"); + else + checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING); + } + + // 4. If explicitly disabled from command line for multiversion jar, it will use default + // version at root regardless multiversion versions exists. + // -Djdk.util.jar.enableMultiRelease=false (not 'true' or 'force') + for (int i = 6; i < MAJOR_VERSION + 1; i++) { + jarVersion = "-Djdk.util.jar.version=" + i; + output = TestCommon.dump(appJar, appClasses, "-Djdk.util.jar.enableMultiRelease=false", jarVersion); + output.shouldHaveExitValue(0); + + output = TestCommon.exec(appJar, verboseMode, mainClass); + expectedOutput = "I am running on version 7"; + checkExecOutput(output, expectedOutput); + } + + // 5. Sanity test with -Xbootclasspath/a + // AppCDS behaves the same as the non-AppCDS case. A multi-release + // jar file in the -Xbootclasspath/a will be ignored. + output = TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + appJar, enableMultiRelease, jarVersion); + output.shouldContain("Loading classes to share: done."); + output.shouldHaveExitValue(0); + + output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, verboseMode, mainClass); + checkExecOutput(output, "I am running on version 7"); + + // 6. Sanity test case-insensitive "Multi-Release" attribute name + output = TestCommon.dump(appJar2, appClasses); + output.shouldContain("Loading classes to share: done."); + output.shouldHaveExitValue(0); + + output = TestCommon.exec(appJar2, verboseMode, mainClass); + checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/OldClassTest.java b/test/hotspot/jtreg/runtime/appcds/OldClassTest.java new file mode 100644 index 00000000000..d41faf33608 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/OldClassTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary classes with major version < JDK_1.5 (48) should not be included in CDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run build TestCommon JarBuilder + * @run main OldClassTest + */ + +import java.io.File; +import java.io.FileOutputStream; +import jdk.test.lib.process.OutputAnalyzer; +import java.nio.file.Files; + +import java.util.*; +import jdk.internal.org.objectweb.asm.*; + +public class OldClassTest implements Opcodes { + + public static void main(String[] args) throws Exception { + File jarSrcFile = new File(JarBuilder.getOrCreateHelloJar()); + + File dir = new File(System.getProperty("test.classes", ".")); + File jarFile = new File(dir, "OldClassTest_old.jar"); + String jar = jarFile.getPath(); + + if (!jarFile.exists() || jarFile.lastModified() < jarSrcFile.lastModified()) { + createTestJarFile(jarSrcFile, jarFile); + } else { + System.out.println("Already up-to-date: " + jarFile); + } + + String appClasses[] = TestCommon.list("Hello"); + + // CASE 1: pre-JDK 1.5 compiled classes should be excluded from the dump + OutputAnalyzer output = TestCommon.dump(jar, appClasses); + TestCommon.checkExecReturn(output, 0, true, "Pre JDK 1.5 class not supported by CDS"); + + output = TestCommon.execCommon( + "-cp", jar, + "-verbose:class", + "Hello"); + TestCommon.checkExecReturn(output, 0, true, "Hello Unicode world (Old)"); + + // CASE 2: if we exlcude old version of this class, we should not pick up + // the newer version of this class in a subsequent classpath element. + String classpath = jar + File.pathSeparator + jarSrcFile.getPath(); + output = TestCommon.dump(classpath, appClasses); + TestCommon.checkExecReturn(output, 0, true, "Pre JDK 1.5 class not supported by CDS"); + + output = TestCommon.execCommon( + "-cp", classpath, + "-verbose:class", + "Hello"); + TestCommon.checkExecReturn(output, 0, true, "Hello Unicode world (Old)"); + } + + static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception { + jarFile.delete(); + Files.copy(jarSrcFile.toPath(), jarFile.toPath()); + + File dir = new File(System.getProperty("test.classes", ".")); + File outdir = new File(dir, "old_class_test_classes"); + outdir.delete(); + outdir.mkdir(); + + writeClassFile(new File(outdir, "Hello.class"), makeOldHello()); + + JarBuilder.update(jarFile.getPath(), outdir.getPath()); + } + + static void writeClassFile(File file, byte bytecodes[]) throws Exception { + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(bytecodes); + } + } + +/* makeOldHello() was obtained using JDK8. We use a method name > 128 that would + trigger a call to java.lang.Character.isJavaIdentifierStart() during class + file parsing. + +cat > Hello.java <<EOF +public class Hello { + public static void main(String args[]) { + System.out.println(\u1234()); + } + static String \u1234() { + return "Hello Unicode world (Old)"; + } +} +EOF +javac Hello.java +java jdk.internal.org.objectweb.asm.util.ASMifier Hello.class + + */ + + static byte[] makeOldHello() throws Exception { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + +//WAS cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "Hello", null, "java/lang/Object", null); + cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, "Hello", null, "java/lang/Object", null); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitMethodInsn(INVOKESTATIC, "Hello", "\u1234", "()Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "\u1234", "()Ljava/lang/String;", null, null); + mv.visitCode(); + mv.visitLdcInsn("Hello Unicode world (Old)"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 0); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/PackageSealing.java b/test/hotspot/jtreg/runtime/appcds/PackageSealing.java new file mode 100644 index 00000000000..00651ee0a3f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/PackageSealing.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary AppCDS handling of package. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @compile test-classes/C1.java + * @compile test-classes/C2.java + * @compile test-classes/PackageSealingTest.java + * @run main PackageSealing + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class PackageSealing { + public static void main(String args[]) throws Exception { + String[] classList = {"sealed/pkg/C1", "pkg/C2", "PackageSealingTest"}; + String appJar = ClassFileInstaller.writeJar("pkg_seal.jar", + ClassFileInstaller.Manifest.fromSourceFile("test-classes/package_seal.mf"), + "PackageSealingTest", "sealed/pkg/C1", "pkg/C2"); + + // test shared package from -cp path + TestCommon.testDump(appJar, TestCommon.list(classList)); + OutputAnalyzer output; + output = TestCommon.exec(appJar, "PackageSealingTest"); + TestCommon.checkExec(output, "OK"); + + // test shared package from -Xbootclasspath/a + TestCommon.dump(appJar, TestCommon.list(classList), + "-Xbootclasspath/a:" + appJar); + output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, "PackageSealingTest"); + TestCommon.checkExec(output, "OK"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java b/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java new file mode 100644 index 00000000000..6c07c6bebce --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Load app classes from CDS archive in parallel threads. Similar to ParallelLoad.java, but each class in its own JAR + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/ParallelLoad.java + * @compile test-classes/ParallelClasses.java + * @run main ParallelLoad2 + */ + +import java.io.File; + +public class ParallelLoad2 { + public static int MAX_CLASSES = 40; + public static void main(String[] args) throws Exception { + JarBuilder.build("parallel_load2", "ParallelLoad", "ParallelLoadThread", "ParallelLoadWatchdog"); + for (int i=0; i<MAX_CLASSES; i++) { + JarBuilder.build("parallel_load2_" + i, "ParallelClass" + i); + } + + String cp = TestCommon.getTestJar("parallel_load2.jar"); + for (int i=0; i<MAX_CLASSES; i++) { + cp += File.pathSeparator + TestCommon.getTestJar("parallel_load2_" + i + ".jar"); + } + + String[] class_list = new String[MAX_CLASSES + 2]; + for (int i=0; i<MAX_CLASSES; i++) { + class_list[i] = "ParallelClass" + i; + } + class_list[class_list.length - 1] = "ParallelLoad"; + class_list[class_list.length - 2] = "ParallelLoadThread"; + + TestCommon.test(cp, class_list, + "ParallelLoad"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java b/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java new file mode 100644 index 00000000000..b5dd75a0a7c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Load app classes from CDS archive in parallel threads + * AppCDS does not support uncompressed oops + * @library /test/lib + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/ParallelLoad.java + * @compile test-classes/ParallelClasses.java + * @run main ParallelLoadTest + */ + +public class ParallelLoadTest { + public static final int MAX_CLASSES = 40; + + public static void main(String[] args) throws Exception { + JarBuilder.build("parallel_load", getClassList(true)); + String appJar = TestCommon.getTestJar("parallel_load.jar"); + TestCommon.test(appJar, getClassList(false), "ParallelLoad"); + } + + private static String[] getClassList(boolean includeWatchdog) { + int extra = includeWatchdog ? 3 : 2; + String[] classList = new String[MAX_CLASSES + extra]; + + int i; + for (i=0; i<MAX_CLASSES; i++) { + classList[i] = "ParallelClass" + i; + } + + classList[i++] = "ParallelLoad"; + classList[i++] = "ParallelLoadThread"; + if (includeWatchdog) + classList[i++] = "ParallelLoadWatchdog"; + + return classList; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java b/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java new file mode 100644 index 00000000000..e3072bba759 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary test the -XX:+PrintSharedArchiveAndExit flag + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @compile test-classes/HelloMore.java + * @run main/othervm/timeout=3600 PrintSharedArchiveAndExit + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class PrintSharedArchiveAndExit { + private static void check(OutputAnalyzer output, int ret, boolean checkContain, String... matches) throws Exception { + // Tests specific to this test + TestCommon.checkExecReturn(output, ret, checkContain, matches); + + // In all test case, we should never print out the following due to + // PrintSharedArchiveAndExit. JVM should have been terminated + // before reaching these outputs. + TestCommon.checkExecReturn(output, ret, false, + "Usage:", // JVM help message + "java version", // JVM version + "Hello World"); // output from the Hello.class in hello.jar + } + + private static void log(String msg) { + System.out.println(">---------------------------------------------------------------------"); + System.out.println(msg); + System.out.println("<---------------------------------------------------------------------"); + } + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String appJar2 = JarBuilder.build("PrintSharedArchiveAndExit-more", "HelloMore"); + + String cp = appJar + File.pathSeparator + appJar2; + String lastCheckMsg = "checking shared classpath entry: " + appJar2; // the last JAR to check + + TestCommon.testDump(cp, TestCommon.list("Hello")); + + OutputAnalyzer output; + + log("Normal execution -- all the JAR paths should be checked"); + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit"); + check(output, 0, true, lastCheckMsg); + + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit", + "-XX:+PrintSharedDictionary"); // Test PrintSharedDictionary as well. + check(output, 0, true, lastCheckMsg, "java.lang.Object"); + + log("Normal execution -- Make sure -version, help message and app main()\n" + + "class are not invoked. These are checked inside check()."); + output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "-version"); + check(output, 0, true, lastCheckMsg); + + output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "-help"); + check(output, 0, true, lastCheckMsg); + + output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "Hello"); + check(output, 0, true, lastCheckMsg); + + log("Execution with simple errors -- with 'simple' errors like missing or modified\n" + + "JAR files, the VM should try to continue to print the remaining information.\n" + + "Use an invalid Boot CP -- all the JAR paths should be checked"); + output = TestCommon.execCommon( + "-cp", cp, + "-Xbootclasspath/a:foo.jar", + "-XX:+PrintSharedArchiveAndExit"); + check(output, 1, true, lastCheckMsg, "[BOOT classpath mismatch, "); + + log("Use an App CP shorter than the one at dump time -- all the JAR paths should be checked"); + output = TestCommon.execCommon( + "-cp", ".", + "-XX:+PrintSharedArchiveAndExit"); + check(output, 1, true, lastCheckMsg, "Run time APP classpath is shorter than the one at dump time: ."); + + log("Use an invalid App CP -- all the JAR paths should be checked"); + String invalidCP = "non-existing-dir" + File.pathSeparator + cp; + output = TestCommon.execCommon( + "-cp", invalidCP, + "-XX:+PrintSharedArchiveAndExit"); + check(output, 1, true, lastCheckMsg, "APP classpath mismatch, actual: -Djava.class.path=" + invalidCP); + + log("Changed modification time of hello.jar -- all the JAR paths should be checked"); + (new File(appJar)).setLastModified(System.currentTimeMillis() + 2000); + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit"); + check(output, 1, true, lastCheckMsg, "[Timestamp mismatch]"); + + log("Even if hello.jar is out of date, we should still be able to print the dictionary."); + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit", + "-XX:+PrintSharedDictionary"); // Test PrintSharedDictionary as well. + check(output, 1, true, lastCheckMsg, "java.lang.Object"); + + + log("Remove hello.jar -- all the JAR paths should be checked"); + (new File(appJar)).delete(); + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit"); + check(output, 1, true, lastCheckMsg, "[Required classpath entry does not exist: " + appJar + "]"); + + log("Execution with major errors -- with 'major' errors like the JSA file\n" + + "is missing, we should stop immediately to avoid crashing the JVM."); + output = TestCommon.execCommon( + "-cp", cp, + "-XX:+PrintSharedArchiveAndExit", + "-XX:SharedArchiveFile=./no-such-fileappcds.jsa"); + check(output, 1, false, lastCheckMsg); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java new file mode 100644 index 00000000000..33ea5a33f0c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary AppCDS handling of prohibited package. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/ProhibitedHelper.java test-classes/Prohibited.jasm + * @run main ProhibitedPackage + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + +public class ProhibitedPackage { + + public static void main(String[] args) throws Exception { + JarBuilder.build("prohibited_pkg", "java/lang/Prohibited", "ProhibitedHelper"); + + String appJar = TestCommon.getTestJar("prohibited_pkg.jar"); + + // AppCDS for custom loader is only supported on linux-x64 and + // Solaris 64-bit platforms. + if ((Platform.isLinux() || Platform.isSolaris()) && + Platform.is64bit()) { + String classlist[] = new String[] { + "java/lang/Object id: 1", + "java/lang/Prohibited id: 2 super: 1 source: " + appJar + }; + + // Make sure a class in a prohibited package for a custom loader + // will be ignored during dumping. + TestCommon.dump(appJar, + classlist, + "-XX:+PrintSystemDictionaryAtExit") + .shouldContain("Dumping") + .shouldNotContain("java.lang.Prohibited") + .shouldHaveExitValue(0); + } + + + // Make sure a class in a prohibited package for a non-custom loader + // will be ignored during dumping. + TestCommon.dump(appJar, + TestCommon.list("java/lang/Prohibited", "ProhibitedHelper"), + "-XX:+PrintSystemDictionaryAtExit") + .shouldContain("Dumping") + .shouldNotContain("java.lang.Prohibited") + .shouldHaveExitValue(0); + + // Try loading the class in a prohibited package with various -Xshare + // modes. The class shouldn't be loaded and appropriate exceptions + // are expected. + + OutputAnalyzer output; + + // -Xshare:on + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper"); + TestCommon.checkExec(output, "Prohibited package name: java.lang"); + + // -Xshare:auto + output = TestCommon.execAuto( + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper"); + TestCommon.checkExec(output, "Prohibited package name: java.lang"); + + // -Xshare:off + output = TestCommon.execOff( + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper"); + output.shouldContain("Prohibited package name: java.lang"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java b/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java new file mode 100644 index 00000000000..2ff23525a1a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary AppCDS handling of protection domain. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/ProtDomain.java + * @compile test-classes/ProtDomainB.java + * @compile test-classes/JimageClassProtDomain.java + * @run main ProtectionDomain + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class ProtectionDomain { + public static void main(String[] args) throws Exception { + JarBuilder.build("prot_domain", "ProtDomain", "ProtDomainB", "ProtDomainOther", + "ProtDomainBOther", "JimageClassProtDomain"); + + String appJar = TestCommon.getTestJar("prot_domain.jar"); + TestCommon.testDump(appJar, + TestCommon.list("ProtDomain", + "ProtDomainBOther", + "java/util/Dictionary", + "sun/tools/javac/Main", + "jdk/nio/zipfs/ZipInfo", + "java/net/URL", + "sun/rmi/rmic/Main", + "com/sun/jndi/dns/DnsName")); + + OutputAnalyzer output; + + // First class is loaded from CDS, second class is loaded from JAR + output = TestCommon.exec(appJar, "-verbose:class", "ProtDomain"); + TestCommon.checkExec(output, "Protection Domains match"); + + // First class is loaded from JAR, second class is loaded from CDS + output = TestCommon.exec(appJar, "-verbose:class", "ProtDomainB"); + TestCommon.checkExec(output, "Protection Domains match"); + + // Test ProtectionDomain for application and extension module classes from the + // "modules" jimage + output = TestCommon.exec(appJar, "-verbose:class", "JimageClassProtDomain"); + output.shouldNotContain("Failed: Protection Domains do not match"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java b/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java new file mode 100644 index 00000000000..d49587d6bce --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Use ClassLoader.defineClass() to load a class with rewritten bytecode. Make sure + * the archived class with the same name is not loaded. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/RewriteBytecodes.java test-classes/Util.java test-classes/Super.java test-classes/Child.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main RewriteBytecodesTest + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class RewriteBytecodesTest { + public static void main(String[] args) throws Exception { + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String appJar = JarBuilder.build("dynamic_define", "RewriteBytecodes", "Util", "Super", "Child"); + String superClsFile = (new File(System.getProperty("test.classes", "."), "Super.class")).getPath(); + + TestCommon.dump(appJar, TestCommon.list("RewriteBytecodes", "Super", "Child"), + // command-line arguments ... + use_whitebox_jar); + + OutputAnalyzer output = TestCommon.exec(appJar, + // command-line arguments ... + "--add-opens=java.base/java.lang=ALL-UNNAMED", + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "RewriteBytecodes", superClsFile); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java new file mode 100644 index 00000000000..e7b1dac98ed --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary SharedArchiveConsistency + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @build sun.hotspot.WhiteBox + * @compile test-classes/Hello.java + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SharedArchiveConsistency + */ +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Utils; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import java.nio.file.StandardOpenOption; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import sun.hotspot.WhiteBox; + +public class SharedArchiveConsistency { + public static WhiteBox wb; + public static int offset_magic; // FileMapHeader::_magic + public static int sp_offset_crc; // FileMapHeader::space_info::_crc + public static int file_header_size = -1;// total size of header, variant, need calculation + public static int space_info_size; // size of space_info + public static int sp_offset; // offset of FileMapHeader::space_info + public static int sp_used_offset; // offset of space_info::_used + public static int size_t_size; // size of size_t + + public static File jsa; // will be updated during test + public static File orgJsaFile; // kept the original file not touched. + public static String[] shared_region_name = {"MiscCode", "ReadWrite", "ReadOnly", "MiscData"}; + public static int num_regions = shared_region_name.length; + public static String[] matchMessages = { + "Unable to use shared archive", + "An error has occurred while processing the shared archive file.", + "Checksum verification failed.", + "The shared archive file has been truncated." + }; + + public static void getFileOffsetInfo() throws Exception { + wb = WhiteBox.getWhiteBox(); + offset_magic = wb.getOffsetForName("FileMapHeader::_magic"); + sp_offset_crc = wb.getOffsetForName("space_info::_crc"); + try { + int nonExistOffset = wb.getOffsetForName("FileMapHeader::_non_exist_offset"); + System.exit(-1); // should fail + } catch (Exception e) { + // success + } + + sp_offset = wb.getOffsetForName("FileMapHeader::_space[0]") - offset_magic; + sp_used_offset = wb.getOffsetForName("space_info::_used") - sp_offset_crc; + size_t_size = wb.getOffsetForName("size_t_size"); + space_info_size = wb.getOffsetForName("space_info_size"); + } + + public static int getFileHeaderSize(FileChannel fc) throws Exception { + if (file_header_size != -1) { + return file_header_size; + } + // this is not real header size, it is struct size + file_header_size = wb.getOffsetForName("file_header_size"); + int offset_path_misc_info = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") - + offset_magic; + int path_misc_info_size = (int)readInt(fc, offset_path_misc_info, size_t_size); + file_header_size += path_misc_info_size; //readInt(fc, offset_path_misc_info, size_t_size); + System.out.println("offset_path_misc_info = " + offset_path_misc_info); + System.out.println("path_misc_info_size = " + path_misc_info_size); + System.out.println("file_header_size = " + file_header_size); + file_header_size = (int)align_up_page(file_header_size); + System.out.println("file_header_size (aligned to page) = " + file_header_size); + return file_header_size; + } + + public static long align_up_page(long l) throws Exception { + // wb is obtained in getFileOffsetInfo() which is called first in main() else we should call + // WhiteBox.getWhiteBox() here first. + int pageSize = wb.getVMPageSize(); + return (l + pageSize -1) & (~ (pageSize - 1)); + } + + private static long getRandomBetween(long start, long end) throws Exception { + if (start > end) { + throw new IllegalArgumentException("start must be less than end"); + } + Random aRandom = Utils.getRandomInstance(); + int d = aRandom.nextInt((int)(end - start)); + if (d < 1) { + d = 1; + } + return start + d; + } + + public static long readInt(FileChannel fc, long offset, int nbytes) throws Exception { + ByteBuffer bb = ByteBuffer.allocate(nbytes); + bb.order(ByteOrder.nativeOrder()); + fc.position(offset); + fc.read(bb); + return (nbytes > 4 ? bb.getLong(0) : bb.getInt(0)); + } + + public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception { + fc.position(offset); + fc.write(bb); + fc.force(true); + } + + public static FileChannel getFileChannel() throws Exception { + List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>(); + arry.add(READ); + arry.add(WRITE); + return FileChannel.open(jsa.toPath(), new HashSet<StandardOpenOption>(arry)); + } + + public static void modifyJsaContentRandomly() throws Exception { + FileChannel fc = getFileChannel(); + // corrupt random area in the data areas (MiscCode, ReadWrite, ReadOnly, MiscData) + long[] used = new long[num_regions]; // record used bytes + long start0, start, end, off; + int used_offset, path_info_size; + + int bufSize; + System.out.printf("%-12s%-12s%-12s%-12s%-12s\n", "Space Name", "Offset", "Used bytes", "Reg Start", "Random Offset"); + start0 = getFileHeaderSize(fc); + for (int i = 0; i < num_regions; i++) { + used_offset = sp_offset + space_info_size * i + sp_used_offset; + // read 'used' + used[i] = readInt(fc, used_offset, size_t_size); + start = start0; + for (int j = 0; j < i; j++) { + start += align_up_page(used[j]); + } + end = start + used[i]; + off = getRandomBetween(start, end); + System.out.printf("%-12s%-12d%-12d%-12d%-12d\n", shared_region_name[i], used_offset, used[i], start, off); + if (end - off < 1024) { + bufSize = (int)(end - off + 1); + } else { + bufSize = 1024; + } + ByteBuffer bbuf = ByteBuffer.wrap(new byte[bufSize]); + writeData(fc, off, bbuf); + } + if (fc.isOpen()) { + fc.close(); + } + } + + public static void modifyJsaContent() throws Exception { + FileChannel fc = getFileChannel(); + byte[] buf = new byte[4096]; + ByteBuffer bbuf = ByteBuffer.wrap(buf); + + long total = 0L; + long used_offset = 0L; + long[] used = new long[num_regions]; + System.out.printf("%-12s%-12s\n", "Space name", "Used bytes"); + for (int i = 0; i < num_regions; i++) { + used_offset = sp_offset + space_info_size* i + sp_used_offset; + // read 'used' + used[i] = readInt(fc, used_offset, size_t_size); + System.out.printf("%-12s%-12d\n", shared_region_name[i], used[i]); + total += used[i]; + } + System.out.printf("%-12s%-12d\n", "Total: ", total); + long corrupt_used_offset = getFileHeaderSize(fc); + System.out.println("Corrupt RO section, offset = " + corrupt_used_offset); + while (used_offset < used[0]) { + writeData(fc, corrupt_used_offset, bbuf); + bbuf.clear(); + used_offset += 4096; + } + fc.force(true); + if (fc.isOpen()) { + fc.close(); + } + } + + public static void modifyJsaHeader() throws Exception { + FileChannel fc = getFileChannel(); + // screw up header info + byte[] buf = new byte[getFileHeaderSize(fc)]; + ByteBuffer bbuf = ByteBuffer.wrap(buf); + writeData(fc, 0L, bbuf); + if (fc.isOpen()) { + fc.close(); + } + } + + public static void copyFile(File from, File to) throws Exception { + if (to.exists()) { + if(!to.delete()) { + throw new IOException("Could not delete file " + to); + } + } + to.createNewFile(); + setReadWritePermission(to); + Files.copy(from.toPath(), to.toPath(), REPLACE_EXISTING); + } + + // Copy file with bytes deleted or inserted + // del -- true, deleted, false, inserted + public static void copyFile(File from, File to, boolean del) throws Exception { + FileChannel inputChannel = null; + FileChannel outputChannel = null; + try { + inputChannel = new FileInputStream(from).getChannel(); + outputChannel = new FileOutputStream(to).getChannel(); + long size = inputChannel.size(); + int init_size = getFileHeaderSize(inputChannel); + outputChannel.transferFrom(inputChannel, 0, init_size); + int n = (int)getRandomBetween(0, 1024); + if (del) { + System.out.println("Delete " + n + " bytes at data start section"); + inputChannel.position(init_size + n); + outputChannel.transferFrom(inputChannel, init_size, size - init_size - n); + } else { + System.out.println("Insert " + n + " bytes at data start section"); + outputChannel.position(init_size); + outputChannel.write(ByteBuffer.wrap(new byte[n])); + outputChannel.transferFrom(inputChannel, init_size + n , size - init_size); + } + } finally { + inputChannel.close(); + outputChannel.close(); + } + } + + public static void restoreJsaFile() throws Exception { + Files.copy(orgJsaFile.toPath(), jsa.toPath(), REPLACE_EXISTING); + } + + public static void setReadWritePermission(File file) throws Exception { + if (!file.canRead()) { + if (!file.setReadable(true)) { + throw new IOException("Cannot modify file " + file + " as readable"); + } + } + if (!file.canWrite()) { + if (!file.setWritable(true)) { + throw new IOException("Cannot modify file " + file + " as writable"); + } + } + } + + public static void testAndCheck(String[] execArgs) throws Exception { + OutputAnalyzer output = TestCommon.execCommon(execArgs); + String stdtxt = output.getOutput(); + System.out.println("Note: this test may fail in very rare occasions due to CRC32 checksum collision"); + for (String message : matchMessages) { + if (stdtxt.contains(message)) { + // match any to return + return; + } + } + TestCommon.checkExec(output); + } + + // dump with hello.jsa, then + // read the jsa file + // 1) run normal + // 2) modify header + // 3) keep header correct but modify content + // 4) update both header and content, test + // 5) delete bytes in data begining + // 6) insert bytes in data begining + // 7) randomly corrupt data in four areas: RO, RW. MISC DATA, MISC CODE + public static void main(String... args) throws Exception { + // must call to get offset info first!!! + getFileOffsetInfo(); + Path currentRelativePath = Paths.get(""); + String currentDir = currentRelativePath.toAbsolutePath().toString(); + System.out.println("Current relative path is: " + currentDir); + // get jar file + String jarFile = JarBuilder.getOrCreateHelloJar(); + + // dump (appcds.jsa created) + TestCommon.testDump(jarFile, null); + + // test, should pass + System.out.println("1. Normal, should pass but may fail\n"); + String[] execArgs = {"-cp", jarFile, "Hello"}; + + OutputAnalyzer output = TestCommon.execCommon(execArgs); + + try { + TestCommon.checkExecReturn(output, 0, true, "Hello World"); + } catch (Exception e) { + TestCommon.checkExecReturn(output, 1, true, matchMessages[0]); + } + + // get current archive name + jsa = new File(TestCommon.getCurrentArchiveName()); + if (!jsa.exists()) { + throw new IOException(jsa + " does not exist!"); + } + + setReadWritePermission(jsa); + + // save as original untouched + orgJsaFile = new File(new File(currentDir), "appcds.jsa.bak"); + copyFile(jsa, orgJsaFile); + + + // modify jsa header, test should fail + System.out.println("\n2. Corrupt header, should fail\n"); + modifyJsaHeader(); + output = TestCommon.execCommon(execArgs); + output.shouldContain("The shared archive file has the wrong version"); + output.shouldNotContain("Checksum verification failed"); + + // modify content + System.out.println("\n3. Corrupt Content, should fail\n"); + copyFile(orgJsaFile, jsa); + modifyJsaContent(); + testAndCheck(execArgs); + + // modify both header and content, test should fail + System.out.println("\n4. Corrupt Header and Content, should fail\n"); + copyFile(orgJsaFile, jsa); + modifyJsaHeader(); + modifyJsaContent(); // this will not be reached since failed on header change first + output = TestCommon.execCommon(execArgs); + output.shouldContain("The shared archive file has the wrong version"); + output.shouldNotContain("Checksum verification failed"); + + // delete bytes in data sectoin + System.out.println("\n5. Delete bytes at begining of data section, should fail\n"); + copyFile(orgJsaFile, jsa, true); + testAndCheck(execArgs); + + // insert bytes in data sectoin forward + System.out.println("\n6. Insert bytes at begining of data section, should fail\n"); + copyFile(orgJsaFile, jsa, false); + testAndCheck(execArgs); + + System.out.println("\n7. modify Content in random areas, should fail\n"); + copyFile(orgJsaFile, jsa); + modifyJsaContentRandomly(); + testAndCheck(execArgs); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java b/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java new file mode 100644 index 00000000000..f18b5f21880 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary The diagnostic option, -XX:SharedArchiveFile can be unlocked using -XX:+UseAppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main SharedArchiveFile + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import java.util.Properties; + +public class SharedArchiveFile { + public static void main(String[] args) throws Exception { + boolean isProduct = !Platform.isDebugBuild(); + String appJar = JarBuilder.getOrCreateHelloJar(); + + // 1) Using -XX:SharedArchiveFile without -XX:+UseAppCDS should fail + // on product binary without -XX:+UnlockDiagnosticVMOptions. + if (isProduct) { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + "-XX:SharedArchiveFile=./SharedArchiveFile.jsa", "-Xshare:dump"); + OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "dump"); + out.shouldContain("Error: VM option 'SharedArchiveFile' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); + } + + // 2) Dumping with -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile + // should always succeed. + CDSTestUtils.createArchive("-XX:+UnlockDiagnosticVMOptions") + .shouldContain("Dumping"); + + // 3) Using -XX:SharedArchiveFile with -XX:+UseAppCDS should work + // on product binary by default. + OutputAnalyzer output3 = TestCommon.dump(appJar, TestCommon.list("Hello")); + output3.shouldContain("Dumping"); + output3 = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(output3, "Hello World"); + + // 4) Using -XX:+UseAppCDS should not affect other diagnostic flags, + // such as LogEvents + OutputAnalyzer output4 = TestCommon.exec(appJar, "-XX:+LogEvents", "Hello"); + if (isProduct) { + output4.shouldContain("Error: VM option 'LogEvents' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); + } else { + TestCommon.checkExec(output4, "Hello World"); + } + + // 5) 8066921 - Extra -XX:+UseAppCDS + TestCommon.testDump(appJar, TestCommon.list("Hello"), "-XX:+UseAppCDS"); + OutputAnalyzer output5 = TestCommon.exec(appJar, "-XX:+UseAppCDS", "Hello"); + TestCommon.checkExec(output5); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java b/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java new file mode 100644 index 00000000000..b317aac0d8f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 2017, 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 SharedBaseAddress + * @summary Test variety of values for SharedBaseAddress, in AppCDS mode, + * making sure VM handles normal values as well as edge values + * w/o a crash. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main/timeout=240 SharedBaseAddress + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class SharedBaseAddress { + + // shared base address test table + private static final String[] testTable = { + "1g", "8g", "64g","512g", "4t", + "32t", "128t", "0", + "1", "64k", "64M" + }; + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + + for (String testEntry : testTable) { + System.out.println("sharedBaseAddress = " + testEntry); + + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, new String[] {"Hello"}, "-XX:SharedBaseAddress=" + testEntry); + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + + OutputAnalyzer execOutput = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(execOutput, "Hello World"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SharedPackages.java b/test/hotspot/jtreg/runtime/appcds/SharedPackages.java new file mode 100644 index 00000000000..c748d53c39c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SharedPackages.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary AppCDS handling of package. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/PackageTest.java + * @compile test-classes/JimageClassPackage.java + * @run main SharedPackages + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class SharedPackages { + public static void main(String[] args) throws Exception { + JarBuilder.build("pkg", "p/PackageTest", "JimageClassPackage"); + + String appJar = TestCommon.getTestJar("pkg.jar"); + TestCommon.testDump(appJar, TestCommon.list("p/PackageTest", + "java/util/Dictionary", + "sun/tools/javac/Main", + "jdk/nio/zipfs/ZipInfo", + "java/net/URL", + "sun/rmi/rmic/Main", + "com/sun/jndi/dns/DnsName")); + + OutputAnalyzer output; + + // Test 1: shared class from Jar on the -cp + output = TestCommon.exec(appJar, "-verbose:class", "p.PackageTest"); + TestCommon.checkExec(output, "Expected package"); + if (!TestCommon.isUnableToMap(output)) + output.shouldContain("Package is not sealed"); + + // Test 2: shared classes from "modules" jimage + output = TestCommon.exec(appJar, "-verbose:class", + "JimageClassPackage"); + if (!TestCommon.isUnableToMap(output)) { + output.shouldNotContain("Unexpected package"); + output.shouldNotContain("Package is not sealed"); + } + + // Test 3: shared class from Jar on the -Xbootclasspath/a + TestCommon.dump( + appJar, TestCommon.list("p/PackageTest"), "-Xbootclasspath/a:" + appJar); + output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, "p.PackageTest"); + if (!TestCommon.isUnableToMap(output)) { + output.shouldNotContain("Unexpected package"); + output.shouldContain("Package is not sealed"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SignedJar.java b/test/hotspot/jtreg/runtime/appcds/SignedJar.java new file mode 100644 index 00000000000..7c3391ea034 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SignedJar.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary AppCDS handling of signed JAR. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main SignedJar + */ + +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; + +public class SignedJar { + public static void main(String[] args) throws Exception { + String unsignedJar = JarBuilder.getOrCreateHelloJar(); + JarBuilder.signJar(); + + // Test class exists in signed JAR + String signedJar = TestCommon.getTestJar("signed_hello.jar"); + OutputAnalyzer output; + output = TestCommon.dump(signedJar, TestCommon.list("Hello")); + TestCommon.checkDump(output, "Preload Warning: Skipping Hello from signed JAR"); + + // At runtime, the Hello class should be loaded from the jar file + // instead of from the shared archive since a class from a signed + // jar shouldn't be dumped into the archive. + output = TestCommon.exec(signedJar, "-verbose:class", "Hello"); + String expectedOutput = ".class,load. Hello source: file:.*signed_hello.jar"; + + try { + output.shouldMatch(expectedOutput); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + + // Test class exists in both signed JAR and unsigned JAR + String jars = signedJar + System.getProperty("path.separator") + unsignedJar; + output = TestCommon.dump(jars, TestCommon.list("Hello")); + TestCommon.checkDump(output, "Preload Warning: Skipping Hello from signed JAR"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java b/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java new file mode 100644 index 00000000000..bea6a0ac3f0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary If -Djava.system.class.loader=xxx is specified in command-line, disable UseAppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @compile test-classes/TestClassLoader.java + * @compile test-classes/ReportMyLoader.java + * @compile test-classes/TrySwitchMyLoader.java + * @run main SpecifySysLoaderProp + */ + +import java.io.*; +import jdk.test.lib.process.OutputAnalyzer; + +public class SpecifySysLoaderProp { + + public static void main(String[] args) throws Exception { + JarBuilder.build("sysloader", "TestClassLoader", "ReportMyLoader", "TrySwitchMyLoader"); + + String jarFileName = "sysloader.jar"; + String appJar = TestCommon.getTestJar(jarFileName); + TestCommon.testDump(appJar, TestCommon.list("ReportMyLoader")); + String warning = "VM warning: UseAppCDS is disabled because the java.system.class.loader property is specified"; + + + // (0) Baseline. Do not specify -Djava.system.class.loader + // The test class should be loaded from archive + OutputAnalyzer output = TestCommon.execCommon( + "-verbose:class", + "-cp", appJar, + "ReportMyLoader"); + TestCommon.checkExec(output, + "[class,load] ReportMyLoader source: shared objects file", + "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@"); + + // (1) Try to execute the archive with -Djava.system.class.loader=no.such.Klass, + // it should fail + output = TestCommon.execCommon( + "-cp", appJar, + "-Djava.system.class.loader=no.such.Klass", + "ReportMyLoader"); + try { + output.shouldContain(warning); + output.shouldContain("ClassNotFoundException: no.such.Klass"); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + + // (2) Try to execute the archive with -Djava.system.class.loader=TestClassLoader, + // it should run, but AppCDS should be disabled + output = TestCommon.execCommon( + "-verbose:class", + "-cp", appJar, + "-Djava.system.class.loader=TestClassLoader", + "ReportMyLoader"); + TestCommon.checkExec(output, + "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", //<-this is still printed because TestClassLoader simply delegates to Launcher$AppLoader, but ... + "TestClassLoader.called = true", //<-but this proves that TestClassLoader was indeed called. + "TestClassLoader: loadClass(\"ReportMyLoader\","); //<- this also proves that TestClassLoader was indeed called. + try { + output.shouldMatch(".class,load. TestClassLoader source: file:"); + output.shouldMatch(".class,load. ReportMyLoader source: file:.*" + jarFileName); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + + // (3) Try to change the java.system.class.loader programmatically after + // the app's main method is executed. This should have no effect in terms of + // changing or switching the actual system class loader that's already in use. + output = TestCommon.execCommon( + "-verbose:class", + "-cp", appJar, + "TrySwitchMyLoader"); + TestCommon.checkExec(output, + "[class,load] ReportMyLoader source: shared objects file", + "TrySwitchMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", + "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", + "TestClassLoader.called = false"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/TestCommon.java b/test/hotspot/jtreg/runtime/appcds/TestCommon.java new file mode 100644 index 00000000000..882b235d1cd --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import jdk.test.lib.Utils; +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.Platform; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +/** + * This is a test utility class for common AppCDS test functionality. + * + * Various methods use (String ...) for passing VM options. Note that the order + * of the VM options are important in certain cases. Many methods take arguments like + * + * (String prefix[], String suffix[], String... opts) + * + * Note that the order of the VM options is: + * + * prefix + opts + suffix + */ +public class TestCommon extends CDSTestUtils { + private static final String JSA_FILE_PREFIX = System.getProperty("user.dir") + + File.separator + "appcds-"; + + private static final SimpleDateFormat timeStampFormat = + new SimpleDateFormat("HH'h'mm'm'ss's'SSS"); + + private static final String timeoutFactor = + System.getProperty("test.timeout.factor", "1.0"); + + private static String currentArchiveName; + + // Call this method to start new archive with new unique name + public static void startNewArchiveName() { + deletePriorArchives(); + currentArchiveName = JSA_FILE_PREFIX + + timeStampFormat.format(new Date()) + ".jsa"; + } + + // Call this method to get current archive name + public static String getCurrentArchiveName() { + return currentArchiveName; + } + + // Attempt to clean old archives to preserve space + // Archives are large artifacts (20Mb or more), and much larger than + // most other artifacts created in jtreg testing. + // Therefore it is a good idea to clean the old archives when they are not needed. + // In most cases the deletion attempt will succeed; on rare occasion the + // delete operation will fail since the system or VM process still holds a handle + // to the file; in such cases the File.delete() operation will silently fail, w/o + // throwing an exception, thus allowing testing to continue. + public static void deletePriorArchives() { + File dir = new File(System.getProperty("user.dir")); + String files[] = dir.list(); + for (String name : files) { + if (name.startsWith("appcds-") && name.endsWith(".jsa")) { + if (!(new File(dir, name)).delete()) + System.out.println("deletePriorArchives(): delete failed for file " + name); + } + } + } + + + // Create AppCDS archive using most common args - convenience method + // Legacy name preserved for compatibility + public static OutputAnalyzer dump(String appJar, String appClasses[], + String... suffix) throws Exception { + return createArchive(appJar, appClasses, suffix); + } + + + // Create AppCDS archive using most common args - convenience method + public static OutputAnalyzer createArchive(String appJar, String appClasses[], + String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar) + .setAppClasses(appClasses); + opts.addSuffix(suffix); + return createArchive(opts); + } + + + // Create AppCDS archive using appcds options + public static OutputAnalyzer createArchive(AppCDSOptions opts) + throws Exception { + + ArrayList<String> cmd = new ArrayList<String>(); + File classList = makeClassList(opts.appClasses); + startNewArchiveName(); + + for (String p : opts.prefix) cmd.add(p); + + if (opts.appJar != null) { + cmd.add("-cp"); + cmd.add(opts.appJar); + } else { + cmd.add("-cp"); + cmd.add("\"\""); + } + + cmd.add("-Xshare:dump"); + cmd.add("-Xlog:cds,cds+hashtables"); + cmd.add("-XX:+UseAppCDS"); + cmd.add("-XX:ExtraSharedClassListFile=" + classList.getPath()); + + if (opts.archiveName == null) + opts.archiveName = getCurrentArchiveName(); + + cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); + + for (String s : opts.suffix) cmd.add(s); + + String[] cmdLine = cmd.toArray(new String[cmd.size()]); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); + return executeAndLog(pb, "dump"); + } + + + // Execute JVM using AppCDS archive with specified AppCDSOptions + public static OutputAnalyzer runWithArchive(AppCDSOptions opts) + throws Exception { + + ArrayList<String> cmd = new ArrayList<String>(); + + for (String p : opts.prefix) cmd.add(p); + + cmd.add("-Xshare:" + opts.xShareMode); + cmd.add("-XX:+UseAppCDS"); + cmd.add("-showversion"); + cmd.add("-XX:SharedArchiveFile=" + getCurrentArchiveName()); + cmd.add("-Dtest.timeout.factor=" + timeoutFactor); + + if (opts.appJar != null) { + cmd.add("-cp"); + cmd.add(opts.appJar); + } + + for (String s : opts.suffix) cmd.add(s); + + String[] cmdLine = cmd.toArray(new String[cmd.size()]); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); + return executeAndLog(pb, "exec"); + } + + + public static OutputAnalyzer execCommon(String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()); + opts.addSuffix(suffix); + return runWithArchive(opts); + } + + + public static OutputAnalyzer exec(String appJar, String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar); + opts.addSuffix(suffix); + return runWithArchive(opts); + } + + + public static OutputAnalyzer execAuto(String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()); + opts.addSuffix(suffix).setXShareMode("auto"); + return runWithArchive(opts); + } + + public static OutputAnalyzer execOff(String... suffix) throws Exception { + AppCDSOptions opts = (new AppCDSOptions()); + opts.addSuffix(suffix).setXShareMode("off"); + return runWithArchive(opts); + } + + public static OutputAnalyzer execModule(String prefix[], String upgrademodulepath, String modulepath, + String mid, String... testClassArgs) + throws Exception { + + AppCDSOptions opts = (new AppCDSOptions()); + + opts.addPrefix(prefix); + if (upgrademodulepath == null) { + opts.addSuffix("-p", modulepath, "-m", mid); + } else { + opts.addSuffix("--upgrade-module-path", upgrademodulepath, + "-p", modulepath, "-m", mid); + } + opts.addSuffix(testClassArgs); + + return runWithArchive(opts); + } + + + // A common operation: dump, then check results + public static OutputAnalyzer testDump(String appJar, String appClasses[], + String... suffix) throws Exception { + OutputAnalyzer output = dump(appJar, appClasses, suffix); + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + return output; + } + + + /** + * Simple test -- dump and execute appJar with the given appClasses in classlist. + */ + public static OutputAnalyzer test(String appJar, String appClasses[], String... args) + throws Exception { + testDump(appJar, appClasses); + + OutputAnalyzer output = exec(appJar, args); + return checkExec(output); + } + + + public static OutputAnalyzer checkExecReturn(OutputAnalyzer output, int ret, + boolean checkContain, String... matches) throws Exception { + try { + for (String s : matches) { + if (checkContain) { + output.shouldContain(s); + } else { + output.shouldNotContain(s); + } + } + output.shouldHaveExitValue(ret); + } catch (Exception e) { + checkCommonExecExceptions(output, e); + } + + return output; + } + + + // Convenience concatenation utils + public static String[] list(String ...args) { + return args; + } + + + public static String[] list(String arg, int count) { + ArrayList<String> stringList = new ArrayList<String>(); + for (int i = 0; i < count; i++) { + stringList.add(arg); + } + + String outputArray[] = stringList.toArray(new String[stringList.size()]); + return outputArray; + } + + + public static String[] concat(String... args) { + return list(args); + } + + + public static String[] concat(String prefix[], String... extra) { + ArrayList<String> list = new ArrayList<String>(); + for (String s : prefix) { + list.add(s); + } + for (String s : extra) { + list.add(s); + } + + return list.toArray(new String[list.size()]); + } + + + // ===================== Concatenate paths + public static String concatPaths(String... paths) { + String prefix = ""; + String s = ""; + for (String p : paths) { + s += prefix; + s += p; + prefix = File.pathSeparator; + } + return s; + } + + + public static String getTestJar(String jar) { + File jarFile = CDSTestUtils.getTestArtifact(jar, true); + if (!jarFile.isFile()) { + throw new RuntimeException("Not a regular file: " + jarFile.getPath()); + } + return jarFile.getPath(); + } + + + public static String getTestDir(String d) { + File dirFile = CDSTestUtils.getTestArtifact(d, true); + if (!dirFile.isDirectory()) { + throw new RuntimeException("Not a directory: " + dirFile.getPath()); + } + return dirFile.getPath(); + } + + + // Returns true if custom loader is supported, based on a platform. + // Custom loader AppCDS is only supported for Linux-x64 and Solaris. + public static boolean isCustomLoaderSupported() { + boolean isLinux = Platform.isLinux(); + boolean isX64 = Platform.isX64(); + boolean isSolaris = Platform.isSolaris(); + + System.out.println("isCustomLoaderSupported: isX64 = " + isX64); + System.out.println("isCustomLoaderSupported: isLinux = " + isLinux); + System.out.println("isCustomLoaderSupported: isSolaris = " + isSolaris); + + return ((isX64 && isLinux) || isSolaris); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java b/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java new file mode 100644 index 00000000000..b3f73278dba --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary ensure -XX:+TraceClassPaths showing entire expecting app classpath + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main TraceLongClasspath + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; + +public class TraceLongClasspath { + + final static String ps = File.pathSeparator; + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + + String longClassPath = + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/user-patch.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/abc-startup.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/features/com.foobar.db.jdbc7-dms.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/jdk/lib/tools.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/server/lib/someapps.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/../foobar_common/modules/net.xy.batcontrib_1.1.0.0_1-0b3/lib/bat-contrib.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/modules/features/foobar.aas.common.kkkkkkkkkkk.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.common.adapters_11.1.1/foobar.abc.common.adapters.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.plane.adapter_12.1.3/foobar.plane.adapter.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/lib/ccccccccar-common.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/communications/modules/config.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/communications/modules/userprefs-config.jar" + ps + + "/scratch/xxxx/yyyy/XXXXXX/aaaaaaaa/xxxxxxx/xxxxxxxx.us.foobar.com/CommonDomain/config/abc-infra" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/qqqqqq-all-1.6.5.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/foobar.abc.thread.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/thread-rrrrrrr-ext-aas.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.adapter_11.1.1/foobar.abc.adapter.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.ccc_11.1.1/foobar.abc.ccc.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-configuration.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-lang.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-logging.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.wccore/foobar-ppppppp-api.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.ooo_12.1.3/ooo-manifest.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/internal/features/rrr_aaxyxx_foobar.rrr.aas.classpath.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/rrrrrrrr-api.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/commons-xxx-1.1.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.mgmt_11.1.1/abc-infra-mgmt.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/eee/archives/eee-eee.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/marchnet.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/marchclient.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/march.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/jlib/iiiloader.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/xxxxxxyyzzzzz/classes-xxxxxxyyzzzzz.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_core.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_codec.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_imageio.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/jdk/lib/tools.jar" + ps + + "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.ooo_12.1.3/ooo-manifest.jar"; + + longClassPath += ps + appJar; + // Dump an archive with a specified JAR file in -classpath + TestCommon.testDump(longClassPath, TestCommon.list("Hello")); + + // Then try to execute the archive with a different classpath and with -XX:+TraceClassPaths. + // The diagnosis "expecting" app classpath trace should show the entire classpath. + OutputAnalyzer output = TestCommon.execCommon( + "-XX:+TraceClassPaths", + "-cp", appJar, + "Hello"); + output.shouldContain("Unable to use shared archive"); + output.shouldContain("shared class paths mismatch"); + // the "expecting" app classpath from -XX:+TraceClassPaths should not + // be truncated + output.shouldContain(longClassPath); + output.shouldHaveExitValue(1); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java b/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java new file mode 100644 index 00000000000..639c06de59c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Testing use of UseAppCDS flag + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @build UseAppCDS_Test + * @run main UseAppCDS + */ + +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.util.ArrayList; +import java.util.List; +import java.io.*; + +public class UseAppCDS { + + // Class UseAppCDS_Test is loaded by the App loader + + static final String TEST_OUT = "UseAppCDS_Test.main--executed"; + + private static final String TESTJAR = "./test.jar"; + private static final String TESTNAME = "UseAppCDS_Test"; + private static final String TESTCLASS = TESTNAME + ".class"; + + private static final String CLASSES_DIR = System.getProperty("test.classes", "."); + private static final String CLASSLIST_FILE = "./UseAppCDS.classlist"; + private static final String ARCHIVE_FILE = "./shared.jsa"; + private static final String BOOTCLASS = "java.lang.Class"; + + public static void main(String[] args) throws Exception { + + // First create a jar file for the application "test" class + JDKToolLauncher jar = JDKToolLauncher.create("jar") + .addToolArg("-cf") + .addToolArg(TESTJAR) + .addToolArg("-C") + .addToolArg(CLASSES_DIR) + .addToolArg(TESTCLASS); + + ProcessBuilder pb = new ProcessBuilder(jar.getCommand()); + TestCommon.executeAndLog(pb, "jar01").shouldHaveExitValue(0); + + pb = new ProcessBuilder(jar.getCommand()); + TestCommon.executeAndLog(pb, "jar02").shouldHaveExitValue(0); + + // In all tests the BOOTCLASS should be loaded/dumped/used + + // Test 1: No AppCDS - dumping loaded classes excludes the "test" classes + dumpLoadedClasses(false, new String[] { BOOTCLASS }, + new String[] { TESTNAME }); + + // Test 2: AppCDS - dumping loaded classes includes "test" classes + dumpLoadedClasses(true, new String[] { BOOTCLASS, TESTNAME }, + new String[0]); + + // Next tests rely on the classlist we just dumped + + // Test 3: No AppCDS - "test" classes in classlist ignored when dumping + dumpArchive(false, new String[] { BOOTCLASS }, + new String[] { TESTNAME}); + + // Test 4: AppCDS - "test" classes in classlist are dumped + dumpArchive(true, new String[] { BOOTCLASS, TESTNAME }, + new String[0]); + + // Next tests rely on the archive we just dumped + + // Test 5: No AppCDS - Using archive containing "test" classes ignores them + useArchive(false, new String[] { BOOTCLASS }, + new String[] { TESTNAME }); + + // Test 6: AppCDS - Using archive containing "test" classes loads them + useArchive(true, new String[] { BOOTCLASS, TESTNAME }, + new String[0]); + } + + public static List<String> toClassNames(String filename) throws IOException { + ArrayList<String> classes = new ArrayList<>(); + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename))); + for (; ; ) { + String line = br.readLine(); + if (line == null) + break; + classes.add(line.replaceAll("/", ".")); + } + return classes; + } + + static void dumpLoadedClasses(boolean useAppCDS, String[] expectedClasses, + String[] unexpectedClasses) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + "-XX:DumpLoadedClassList=" + CLASSLIST_FILE, + "-cp", + TESTJAR, + useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS", + TESTNAME, + TEST_OUT); + + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-loaded-classes") + .shouldHaveExitValue(0).shouldContain(TEST_OUT); + + List<String> dumpedClasses = toClassNames(CLASSLIST_FILE); + + for (String clazz : expectedClasses) { + if (!dumpedClasses.contains(clazz)) { + throw new RuntimeException(clazz + " missing in " + + CLASSLIST_FILE); + } + } + for (String clazz : unexpectedClasses) { + if (dumpedClasses.contains(clazz)) { + throw new RuntimeException("Unexpectedly found " + clazz + + " in " + CLASSLIST_FILE); + } + } + } + + static void dumpArchive(boolean useAppCDS, String[] expectedClasses, + String[] unexpectedClasses) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + useAppCDS ? "-XX:-UnlockDiagnosticVMOptions" : + "-XX:+UnlockDiagnosticVMOptions", + "-cp", + TESTJAR, + useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS", + "-XX:SharedClassListFile=" + CLASSLIST_FILE, + "-XX:SharedArchiveFile=" + ARCHIVE_FILE, + "-Xlog:cds", + "-Xshare:dump"); + + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-archive") + .shouldHaveExitValue(0); + + for (String clazz : expectedClasses) { + String failed = "Preload Warning: Cannot find " + clazz; + output.shouldNotContain(failed); + } + for (String clazz : unexpectedClasses) { + String failed = "Preload Warning: Cannot find " + clazz; + output.shouldContain(failed); + } + } + + static void useArchive(boolean useAppCDS, String[] expectedClasses, + String[] unexpectedClasses) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + useAppCDS ? "-XX:-UnlockDiagnosticVMOptions" : + "-XX:+UnlockDiagnosticVMOptions", + "-cp", + TESTJAR, + useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS", + "-XX:SharedArchiveFile=" + ARCHIVE_FILE, + "-verbose:class", + "-Xshare:on", + TESTNAME, + TEST_OUT ); + + OutputAnalyzer output = TestCommon.executeAndLog(pb, "use-archive"); + if (CDSTestUtils.isUnableToMap(output)) + System.out.println("Unable to map: test case skipped"); + else + output.shouldHaveExitValue(0).shouldContain(TEST_OUT); + + // Quote the class name in the regex as it may contain $ + String prefix = ".class,load. "; + String archive_suffix = ".*source: shared objects file.*"; + String jar_suffix = ".*source: .*\\.jar"; + + for (String clazz : expectedClasses) { + String pattern = prefix + clazz + archive_suffix; + try { + output.shouldMatch(pattern); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + } + + for (String clazz : unexpectedClasses) { + String pattern = prefix + clazz + archive_suffix; + try { + output.shouldNotMatch(pattern); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + pattern = prefix + clazz + jar_suffix; + try { + output.shouldMatch(pattern); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java b/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java new file mode 100644 index 00000000000..438c614445b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ +public class UseAppCDS_Test { + // args are from UseAppCDS: + // args[0] = TEST_OUT + public static void main(String[] args) { + System.out.println(args[0]); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest.java new file mode 100644 index 00000000000..ba7f5d1b831 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.io.File; +import java.io.FileOutputStream; +import jdk.test.lib.process.OutputAnalyzer; +import java.nio.file.Files; + +import java.util.*; +import jdk.internal.org.objectweb.asm.*; + +/** + * The testsets contained in this class are executed by ./VerifierTest_*.java, so that + * individual testsets can be executed in parallel to shorten the total time required. + */ +public class VerifierTest implements Opcodes { + // Test verification settings for dumping & runtime + static final String VFY_ALL = "-Xverify:all"; + static final String VFY_REMOTE = "-Xverify:remote"; // default + static final String VFY_NONE = "-Xverify:none"; + + static final String ERR = + "ERROR: class VerifierTestC was loaded unexpectedly"; + static final String MAP_FAIL = + "shared archive file was created with less restrictive verification setting"; + static final String VFY_ERR = "java.lang.VerifyError"; + + enum Testset1Part { + A, B + } + + public static void main(String[] args) throws Exception { + String subCaseId = args[0]; + String jarName_verifier_test_tmp = "verifier_test_tmp" + "_" + subCaseId; + String jarName_verifier_test = "verifier_test" + "_" + subCaseId; + String jarName_greet = "greet" + "_" + subCaseId; + String jarName_hi = "hi" + "_" + subCaseId; + + + JarBuilder.build(jarName_verifier_test_tmp, "VerifierTest0", "VerifierTestA", + "VerifierTestB", "VerifierTestC", "VerifierTestD", "VerifierTestE", + "UnverifiableBase", "UnverifiableIntf", "UnverifiableIntfSub"); + JarBuilder.build(jarName_greet, "Greet"); + JarBuilder.build(jarName_hi, "Hi", "Hi$MyClass"); + + File dir = new File(System.getProperty("test.classes", ".")); + File jarSrcFile = new File(dir, jarName_verifier_test_tmp + ".jar"); + File jarFile = new File(dir, jarName_verifier_test + ".jar"); + String jar = jarFile.getPath(); + + if (!jarFile.exists() || jarFile.lastModified() < jarSrcFile.lastModified()) { + createTestJarFile(jarSrcFile, jarFile); + } else { + System.out.println("Already up-to-date: " + jarFile); + } + + String noAppClasses[] = TestCommon.list(""); + String appClasses[] = TestCommon.list("UnverifiableBase", + "UnverifiableIntf", + "UnverifiableIntfSub", + "VerifierTestA", + "VerifierTestB", + "VerifierTestC", + "VerifierTestD", + "VerifierTestE", + "VerifierTest0"); + + + switch (subCaseId) { + case "0": testset_0(jar, noAppClasses, appClasses); return; + case "1A": testset_1(jar, noAppClasses, appClasses, Testset1Part.A); return; + case "1B": testset_1(jar, noAppClasses, appClasses, Testset1Part.B); return; + case "2": testset_2(jarName_greet, jarName_hi); return; + default: + throw new RuntimeException("Unknown option: " + subCaseId); + } + } + + static void testset_0(String jar, String[] noAppClasses, String[] appClasses) throws Exception { + // Dumping should fail if the IgnoreUnverifiableClassesDuringDump + // option is not enabled. + OutputAnalyzer output = TestCommon.dump(jar, appClasses, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-IgnoreUnverifiableClassesDuringDump"); + output.shouldContain("Please remove the unverifiable classes"); + output.shouldHaveExitValue(1); + + // By default, bad classes should be ignored during dumping. + TestCommon.testDump(jar, appClasses); + } + + static void testset_1(String jar, String[] noAppClasses, String[] appClasses, Testset1Part part) + throws Exception + { + String config[][] = { + // {dump_list, dumptime_verification_setting, + // runtime_verification_setting, runtime_output}, + + // Dump app/ext with -Xverify:remote + {"app", VFY_REMOTE, VFY_REMOTE, VFY_ERR}, + {"app", VFY_REMOTE, VFY_ALL, MAP_FAIL}, + {"app", VFY_REMOTE, VFY_NONE, ERR }, + // Dump app/ext with -Xverify:all + {"app", VFY_ALL, VFY_REMOTE, VFY_ERR }, + {"app", VFY_ALL, VFY_ALL, VFY_ERR }, + {"app", VFY_ALL, VFY_NONE, ERR }, + // Dump app/ext with -Xverify:none + {"app", VFY_NONE, VFY_REMOTE, MAP_FAIL}, + {"app", VFY_NONE, VFY_ALL, MAP_FAIL}, + {"app", VFY_NONE, VFY_NONE, ERR }, + // Dump sys only with -Xverify:remote + {"noApp", VFY_REMOTE, VFY_REMOTE, VFY_ERR}, + {"noApp", VFY_REMOTE, VFY_ALL, VFY_ERR}, + {"noApp", VFY_REMOTE, VFY_NONE, ERR}, + // Dump sys only with -Xverify:all + {"noApp", VFY_ALL, VFY_REMOTE, VFY_ERR}, + {"noApp", VFY_ALL, VFY_ALL, VFY_ERR}, + {"noApp", VFY_ALL, VFY_NONE, ERR}, + // Dump sys only with -Xverify:none + {"noApp", VFY_NONE, VFY_REMOTE, VFY_ERR}, + {"noApp", VFY_NONE, VFY_ALL, VFY_ERR}, + {"noApp", VFY_NONE, VFY_NONE, ERR}, + }; + + int loop_start, loop_stop; + + // Further break down testset_1 into two parts (to be invoked from VerifierTest_1A.java + // and VerifierTest_1B.java) to improve parallel test execution time. + switch (part) { + case A: + loop_start = 0; + loop_stop = 9; + break; + case B: + default: + assert part == Testset1Part.B; + loop_start = 9; + loop_stop = config.length; + break; + } + + String prev_dump_setting = ""; + for (int i = loop_start; i < loop_stop; i ++) { + String dump_list[] = config[i][0].equals("app") ? appClasses : + noAppClasses; + String dump_setting = config[i][1]; + String runtime_setting = config[i][2]; + String runtime_output = config[i][3]; + System.out.println("Test case [" + i + "]: dumping " + config[i][0] + + " with " + dump_setting + + ", run with " + runtime_setting); + if (!dump_setting.equals(prev_dump_setting)) { + OutputAnalyzer dumpOutput = TestCommon.dump( + jar, dump_list, dump_setting, + // FIXME: the following options are for working around a GC + // issue - assert failure when dumping archive with the -Xverify:all + "-Xms256m", + "-Xmx256m"); + } + OutputAnalyzer runtimeOutput = TestCommon.execCommon( + "-cp", jar, + runtime_setting, + "VerifierTest0"); + try { + runtimeOutput.shouldContain(runtime_output); + } catch (RuntimeException re) { + // Check if the failure is due to archive mapping failure. + // If not, a RuntimeException will be thrown. + runtimeOutput.shouldContain("Unable to use shared archive"); + } + prev_dump_setting = dump_setting; + } + } + + static void testset_2(String jarName_greet, String jarName_hi) throws Exception { + String appClasses[]; + String jar; + + // The following section is for testing the scenarios where + // the classes are verifiable during dump time. + appClasses = TestCommon.list("Hi", + "Greet", + "Hi$MyClass"); + jar = TestCommon.getTestJar(jarName_hi + ".jar") + File.pathSeparator + + TestCommon.getTestJar(jarName_greet + ".jar"); + final String PASS_RESULT = "Hi, how are you?"; + String config2[][] = { + // {dump_list, dumptime_verification_setting, + // runtime_verification_setting, runtime_output}, + + // Dump app/ext with -Xverify:remote + {"app", VFY_REMOTE, VFY_REMOTE, PASS_RESULT}, + {"app", VFY_REMOTE, VFY_ALL, MAP_FAIL}, + {"app", VFY_REMOTE, VFY_NONE, PASS_RESULT }, + // Dump app/ext with -Xverify:all + {"app", VFY_ALL, VFY_REMOTE, PASS_RESULT }, + {"app", VFY_ALL, VFY_ALL, PASS_RESULT }, + {"app", VFY_ALL, VFY_NONE, PASS_RESULT }, + // Dump app/ext with -Xverify:none + {"app", VFY_NONE, VFY_REMOTE, MAP_FAIL}, + {"app", VFY_NONE, VFY_ALL, MAP_FAIL}, + {"app", VFY_NONE, VFY_NONE, PASS_RESULT }, + }; + for (int i = 0; i < config2.length; i ++) { + // config2[i][0] is always set to "app" in this test + String dump_setting = config2[i][1]; + String runtime_setting = config2[i][2]; + String runtime_output = config2[i][3]; + System.out.println("Test case [" + i + "]: dumping " + config2[i][0] + + " with " + dump_setting + + ", run with " + runtime_setting); + OutputAnalyzer dumpOutput = TestCommon.dump( + jar, appClasses, dump_setting, + "-XX:+UnlockDiagnosticVMOptions", + // FIXME: the following options are for working around a GC + // issue - assert failure when dumping archive with the -Xverify:all + "-Xms256m", + "-Xmx256m"); + OutputAnalyzer runtimeOutput = TestCommon.execCommon( + "-cp", jar, + runtime_setting, + "Hi"); + try { + runtimeOutput.shouldContain(runtime_output); + } catch (RuntimeException re) { + // Check if the failure is due to archive mapping failure. + // If not, a RuntimeException will be thrown. + runtimeOutput.shouldContain("Unable to use shared archive"); + } + } + + } + + static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception { + jarFile.delete(); + Files.copy(jarSrcFile.toPath(), jarFile.toPath()); + + File dir = new File(System.getProperty("test.classes", ".")); + File outdir = new File(dir, "verifier_test_classes"); + outdir.mkdir(); + + writeClassFile(new File(outdir, "UnverifiableBase.class"), makeUnverifiableBase()); + writeClassFile(new File(outdir, "UnverifiableIntf.class"), makeUnverifiableIntf()); + + JarBuilder.update(jarFile.getPath(), outdir.getPath()); + } + + static void writeClassFile(File file, byte bytecodes[]) throws Exception { + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(bytecodes); + } + } + + // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableBase.class + static byte[] makeUnverifiableBase() throws Exception { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V1_6, ACC_SUPER, "UnverifiableBase", null, "java/lang/Object", null); + { + fv = cw.visitField(ACC_FINAL + ACC_STATIC, "x", "LVerifierTest;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(0, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + //WAS mv.visitTypeInsn(NEW, "VerifierTest"); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false); + mv.visitFieldInsn(PUTSTATIC, "UnverifiableBase", "x", "LVerifierTest;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 0); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableIntf.class + static byte[] makeUnverifiableIntf() throws Exception { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V1_6, ACC_ABSTRACT + ACC_INTERFACE, "UnverifiableIntf", null, "java/lang/Object", null); + + { + fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "x", "LVerifierTest0;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + //WAS mv.visitTypeInsn(NEW, "VerifierTest"); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false); + mv.visitFieldInsn(PUTSTATIC, "UnverifiableIntf", "x", "LVerifierTest0;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 0); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java new file mode 100644 index 00000000000..6ed2f6d35d5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Unverfiable app classes should not be archived. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * java.base/jdk.internal.org.objectweb.asm + * @compile test-classes/Greet.java + * @compile test-classes/Hi.java + * @compile test-classes/VerifierTest0.java + * @run main/othervm/timeout=3600 VerifierTest 0 + */ diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java new file mode 100644 index 00000000000..8a5430e2263 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Unverfiable app classes should not be archived. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * java.base/jdk.internal.org.objectweb.asm + * @compile test-classes/Greet.java + * @compile test-classes/Hi.java + * @compile test-classes/VerifierTest0.java + * @run main/othervm/timeout=3600 VerifierTest 1A + */ diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java new file mode 100644 index 00000000000..7a721d13ba8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Unverfiable app classes should not be archived. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * java.base/jdk.internal.org.objectweb.asm + * @compile test-classes/Greet.java + * @compile test-classes/Hi.java + * @compile test-classes/VerifierTest0.java + * @run main/othervm/timeout=3600 VerifierTest 1B + */ diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java new file mode 100644 index 00000000000..c7bbe8793f7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Unverfiable app classes should not be archived. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * java.base/jdk.internal.org.objectweb.asm + * @compile test-classes/Greet.java + * @compile test-classes/Hi.java + * @compile test-classes/VerifierTest0.java + * @run main/othervm/timeout=3600 VerifierTest 2 + */ diff --git a/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java b/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java new file mode 100644 index 00000000000..9323924449d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Test 'iload_w' bytecode in shared class + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Iloadw.jasm + * @compile test-classes/IloadwMain.java + * @run main WideIloadTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class WideIloadTest { + public static void main(String args[]) throws Exception { + JarBuilder.build("iload_w", "Iloadw", "IloadwMain"); + String appJar = TestCommon.getTestJar("iload_w.jar"); + OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list( + "Iloadw", "IloadwMain")); + TestCommon.checkDump(dumpOutput); + OutputAnalyzer execOutput = TestCommon.exec(appJar, "IloadwMain"); + TestCommon.checkExec(execOutput, "Passed"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java b/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java new file mode 100644 index 00000000000..d3069c33cd9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary classpath mismatch between dump time and execution time + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main WrongClasspath + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class WrongClasspath { + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + + // Dump an archive with a specified JAR file in -classpath + TestCommon.testDump(appJar, TestCommon.list("Hello")); + + // Then try to execute the archive without -classpath -- it should fail + OutputAnalyzer output = TestCommon.execCommon( + /* "-cp", appJar, */ // <- uncomment this and the execution should succeed + "Hello"); + output.shouldContain("Unable to use shared archive"); + output.shouldContain("shared class paths mismatch"); + output.shouldHaveExitValue(1); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java b/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java new file mode 100644 index 00000000000..d3c5c8ef50c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test -Xshare:auto for AppCDS + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java + * @run main XShareAutoWithChangedJar + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class XShareAutoWithChangedJar { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.build("XShareAutoWithChangedJar", "Hello"); + + // 1. dump + OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello")); + TestCommon.checkDump(output); + + // 2. change the jar + JarBuilder.build("XShareAutoWithChangedJar", "Hello"); + + // 3. exec + output = TestCommon.execAuto("-cp", appJar, "Hello"); + output.shouldContain("Hello World"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java new file mode 100644 index 00000000000..73df2eaf816 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, 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 + * @summary Test resolved_references + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox + * @compile CheckCachedResolvedReferencesApp.java + * @compile ../test-classes/Hello.java + * @run main ClassFileInstaller -jar app.jar CheckCachedResolvedReferencesApp + * @run main ClassFileInstaller -jar hello.jar Hello + * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run main CheckCachedResolvedReferences + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +public class CheckCachedResolvedReferences { + public static void main(String[] args) throws Exception { + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + String appJar = ClassFileInstaller.getJarPath("app.jar"); + String helloJarPath = ClassFileInstaller.getJarPath("hello.jar"); + + String classlist[] = new String[] { + "CheckCachedResolvedReferencesApp", + "java/lang/Object id: 1", + "Hello id: 2 super: 1 source: " + helloJarPath + }; + + TestCommon.testDump(appJar, classlist, use_whitebox_jar); + OutputAnalyzer output = TestCommon.exec(appJar, use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "CheckCachedResolvedReferencesApp", + helloJarPath); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java new file mode 100644 index 00000000000..0db69dd3391 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import sun.hotspot.WhiteBox; + +public class CheckCachedResolvedReferencesApp { + public static void main(String args[]) throws Exception { + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + + URLClassLoader loader = new URLClassLoader(urls); + Class hello = loader.loadClass("Hello"); + System.out.println("Loaded " + hello + " from " + url + " using loader " + loader); + + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (!wb.areOpenArchiveHeapObjectsMapped()) { + System.out.println("Archived open_archive_heap objects are not mapped."); + System.out.println("This may happen during normal operation. Test Skipped."); + return; + } + + // CheckCachedResolvedReferencesApp is shared class and loaded by the + // AppClassLoader. It should have cached resolved_references. + if (wb.isSharedClass(CheckCachedResolvedReferencesApp.class)) { + Object refs1 = wb.getResolvedReferences(CheckCachedResolvedReferencesApp.class); + if (refs1 != null && wb.isShared(refs1)) { + System.out.println( + "resolved references from CheckCachedResolvedReferencesApp is cached"); + } else { + throw new RuntimeException( + "FAILED. CheckCachedResolvedReferencesApp has no cached resolved references"); + } + } + + // Hello is shared class and loaded by the 'loader' defined in current app. + // It should not have cached resolved_references. + if (wb.isSharedClass(hello)) { + Object refs2 = wb.getResolvedReferences(hello); + if (refs2 != null) { + if (!wb.isShared(refs2)) { + System.out.println("resolved references from hello is not cached"); + } else { + throw new RuntimeException( + "FAILED. Hello has unexpected cached resolved references"); + } + } else { + throw new RuntimeException("FAILED. Hello has no resolved references"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt new file mode 100644 index 00000000000..fb6912f2c3c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt @@ -0,0 +1,3 @@ +VERSION: 1.0 +@SECTION: String +26: shared_string_from_MyInner diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java new file mode 100644 index 00000000000..12be0931edf --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 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 + * @summary Dump time should not crash if any class with shared strings fails verification due to missing dependencies. + * @bug 8186789 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @compile MyOuter.java MyException.java + * @run main DumpTimeVerifyFailure + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class DumpTimeVerifyFailure { + public static void main(String[] args) throws Exception { + // App classes (see MyOuter.java): + // MyOuter + // MyInnder$MyOuter extends MyOuter + // MyException + // + // MyOuter$MyInner.test() throws MyException. + // The missingMyException.jar file only includes MyOuter and + // MyOuter$MyInner classes, but not the MyException class. + // At dump time, MyOuter and MyOuter$MyInner classes fail + // verification due to missing MyException class. + String[] ARCHIVE_CLASSES = {"MyOuter", "MyOuter$MyInner"}; + String appJar = JarBuilder.build("missingMyException", ARCHIVE_CLASSES); + + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, ARCHIVE_CLASSES, + "-Xlog:verification", + "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("DumpTimeVerifyFailure.config.txt")); + TestCommon.checkDump(dumpOutput, "Loading classes to share"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt new file mode 100644 index 00000000000..af3e33bb977 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt @@ -0,0 +1,3 @@ +VERSION: 1.0 +@SECTION: String +25: GCStressApp_shared_string diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java new file mode 100644 index 00000000000..636da040d6b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.io.*; +import java.util.*; +import sun.hotspot.WhiteBox; + +// All strings in archived classes are shared +public class GCStressApp { + static WhiteBox wb = WhiteBox.getWhiteBox(); + static int[] arr; + + static String get_shared_string() { + String shared_str = "GCStressApp_shared_string"; + return shared_str; + } + + static String get_shared_string1() { + String shared_str1 = "GCStressApp_shared_string1"; + return shared_str1; + } + + static void allocAlot() { + try { + Random random = new Random(); + for (int i = 0; i < 1024 * 1024; i++) { + int len = random.nextInt(10000); + arr = new int[len]; + } + } catch (java.lang.OutOfMemoryError e) { } + } + + static void runGC() { + wb.fullGC(); + } + + public static void main(String args[]) throws Exception { + if (!wb.isSharedClass(GCStressApp.class)) { + System.out.println("GCStressApp is not shared. Possibly there was a mapping failure."); + return; + } + + if (wb.areSharedStringsIgnored()) { + System.out.println("Shared strings are ignored."); + return; + } + + Object refs = wb.getResolvedReferences(GCStressApp.class); + if (wb.isShared(refs)) { + String shared_str = get_shared_string(); + String shared_str1 = get_shared_string1(); + + if (!wb.isShared(shared_str)) { + throw new RuntimeException("FAILED. GCStressApp_shared_string is not shared"); + } + + if (!wb.isShared(shared_str1)) { + throw new RuntimeException("FAILED. GCStressApp_shared_string1 is not shared"); + } + + allocAlot(); + runGC(); + runGC(); + runGC(); + + System.out.println("Passed"); + } else { + System.out.println( + "No cached resolved references. Open archive heap data is not used."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java new file mode 100644 index 00000000000..591de9b3e67 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 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 + * @summary + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox + * @compile GCStressApp.java + * @run main ClassFileInstaller -jar gcstress.jar GCStressApp + * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run main GCStressTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class GCStressTest { + public static void main(String[] args) throws Exception { + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + String appJar = ClassFileInstaller.getJarPath("gcstress.jar"); + String appClasses[] = TestCommon.list("GCStressApp"); + + OutputAnalyzer output = TestCommon.dump(appJar, appClasses, + use_whitebox_jar, + "-Xms20M", "-Xmx20M"); + output = TestCommon.exec(appJar, use_whitebox_jar, + "-Xlog:cds=info", + "-Xms20M", "-Xmx20M", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI","GCStressApp"); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf b/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf new file mode 100644 index 00000000000..58dcf797de6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Premain-Class: InstrumentationRegisterClassFileTransformer +Agent-Class: InstrumentationRegisterClassFileTransformer +Can-Retransform-Classes: true +Can-Redefine-Classes: true diff --git a/src/hotspot/share/classfile/vmSymbols_ext.hpp b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java similarity index 79% rename from src/hotspot/share/classfile/vmSymbols_ext.hpp rename to test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java index 4b68d8f234b..39a464b4f00 100644 --- a/src/hotspot/share/classfile/vmSymbols_ext.hpp +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -21,11 +21,8 @@ * questions. * */ - -#ifndef SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP -#define SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP - -#define VM_SYMBOLS_DO_EXT(template, do_alias) - -#endif // SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP - +public class MyException extends Exception { + public MyException(String msg) { + super(msg); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java new file mode 100644 index 00000000000..8773772ac68 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017, 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. + * + */ +public class MyOuter { + public void exp() throws MyException { + throw new MyException("MyOuter exception"); + } + + public void test() throws Exception { + System.out.println("MyOuter"); + try { + exp(); + } catch (MyException e) { + } + } + + public static final class MyInner extends MyOuter { + static String myString = "shared_string_from_MyInner"; + public void test() { + System.out.println("MyInner"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java new file mode 100644 index 00000000000..df1b348913f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 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 + * @summary Test open archive heap regions + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @compile ../test-classes/Hello.java + * @run main OpenArchiveRegion + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class OpenArchiveRegion { + public static void main(String[] args) throws Exception { + JarBuilder.getOrCreateHelloJar(); + String appJar = TestCommon.getTestJar("hello.jar"); + String appClasses[] = TestCommon.list("Hello"); + + // Dump with open archive heap region, requires G1 GC + OutputAnalyzer output = TestCommon.dump(appJar, appClasses); + TestCommon.checkDump(output, "oa0 space:"); + output.shouldNotContain("oa0 space: 0 ["); + output = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(output, "Hello World"); + output = TestCommon.exec(appJar, "-XX:+UseSerialGC", "Hello"); + TestCommon.checkExec(output, "Hello World"); + + // Dump with open archive heap region disabled when G1 GC is not in use + output = TestCommon.dump(appJar, appClasses, "-XX:+UseParallelGC"); + TestCommon.checkDump(output); + output.shouldNotContain("oa0 space:"); + output = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(output, "Hello World"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java new file mode 100644 index 00000000000..13242f72f0b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 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 + * @summary Shared classes can still be used when archived heap regions cannot be + * mapped due to out of range, and -Xshare:on should not fail. Test on + * linux 64-bit only since the HeapBaseMinAddress value is platform specific. + * The value used in the test may cause different behavior on other platforms. + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (os.family == "linux") & (os.arch == "amd64") & (sun.arch.data.model == "64") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @compile ../test-classes/Hello.java + * @run main RangeNotWithinHeap + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class RangeNotWithinHeap { + public static void main(String[] args) throws Exception { + JarBuilder.getOrCreateHelloJar(); + String appJar = TestCommon.getTestJar("hello.jar"); + String appClasses[] = TestCommon.list("Hello"); + + OutputAnalyzer output = TestCommon.dump(appJar, appClasses, + "-XX:HeapBaseMinAddress=0x600000000", "-Xmx6G", "-Xlog:gc+heap=trace"); + TestCommon.checkDump(output, "oa0 space:"); + + // Force archive region out of runtime java heap + output = TestCommon.exec(appJar, "Hello"); + TestCommon.checkExec(output, "Hello World"); + output = TestCommon.exec(appJar, + "-XX:HeapBaseMinAddress=0x600000000", "-Xmx2G", "-Xlog:gc+heap=trace,cds", "Hello"); + TestCommon.checkExec(output, "Hello World"); + try { + output.shouldContain( + "UseSharedSpaces: Unable to allocate region, range is not within java heap."); + } catch (Exception e) { + // In rare case the heap data is not used. + if (output.getOutput().contains("Cached heap data from the CDS archive is being ignored")) { + return; + } + // Check for common shared class data mapping failures. + TestCommon.checkCommonExecExceptions(output, e); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java new file mode 100644 index 00000000000..fa482e70a5e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.net.URL; +import java.net.URLClassLoader; +import java.io.File; +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import sun.hotspot.WhiteBox; + +public class RedefineClassApp { + static WhiteBox wb = WhiteBox.getWhiteBox(); + + public static interface Intf { // Loaded from Boot class loader (-Xbootclasspath/a). + public String get(); + } + public static class Bar implements Intf { // Loaded from Boot class loader. + public String get() { + return "buzz"; + } + } + public static class Foo implements Intf { // Loaded from AppClassLoader + public String get() { + return "buzz"; + } + } + + static int numTests = 0; + static int failed = 0; + static Instrumentation instrumentation; + + public static void main(String args[]) throws Throwable { + if (wb.areSharedStringsIgnored()) { + System.out.println("Shared strings are ignored."); + return; + } + + File bootJar = new File(args[0]); + File appJar = new File(args[1]); + + instrumentation = InstrumentationRegisterClassFileTransformer.getInstrumentation(); + System.out.println("INFO: instrumentation = " + instrumentation); + + testBootstrapCDS("Bootstrap Loader", bootJar); + testAppCDSv1("Application Loader", appJar); + + if (failed > 0) { + throw new RuntimeException("FINAL RESULT: " + failed + " out of " + numTests + " test case(s) have failed"); + } else { + System.out.println("FINAL RESULT: All " + numTests + " test case(s) have passed!"); + } + + // Full GC. The cached objects in adjustable archive heap regions are + // scanned. The archive regions are verified. No error should be + // reported. + wb.fullGC(); + } + + static void testBootstrapCDS(String group, File jar) throws Throwable { + doTest(group, new Bar(), jar); + } + + static void testAppCDSv1(String group, File jar) throws Throwable { + doTest(group, new Foo(), jar); + } + + static void doTest(String group, Intf object, File jar) throws Throwable { + numTests ++; + + Class klass = object.getClass(); + System.out.println(); + System.out.println("++++++++++++++++++++++++++"); + System.out.println("Test group: " + group); + System.out.println("Testing with classloader = " + klass.getClassLoader()); + System.out.println("Testing with class = " + klass); + System.out.println("Test is shared = " + wb.isSharedClass(klass)); + System.out.println("++++++++++++++++++++++++++"); + + // Call get() before redefine. All strings in archived classes are shared. + String res = object.get(); + System.out.println("get() returns " + res); + if (res.equals("buzz") && wb.isShared(res)) { + System.out.println("get() returns " + res + ", string is shared"); + } else { + if (!res.equals("buzz")) { + System.out.println("FAILED. buzz is expected but got " + res); + } else { + System.out.println("FAILED. " + res + " is not shared"); + } + failed ++; + return; + } + res = null; // release the local reference to the string + + // Run GC + System.gc(); + System.gc(); + System.gc(); + + // Redefine the shared class + byte[] buff = Util.getClassFileFromJar(jar, klass.getName()); + Util.replace(buff, "buzz", "huzz"); + String f = "(failed)"; + try { + instrumentation.redefineClasses(new ClassDefinition(klass, buff)); + f = object.get(); + } catch (UnmodifiableClassException|UnsupportedOperationException e) { + e.printStackTrace(); + } + if (f.equals("huzz")) { + System.out.println("PASSED: object.get() after redefinition returns " + f); + } else { + System.out.println("FAILED: object.get() after redefinition returns " + f); + failed ++; + } + + // Run GC. Should not crash. + System.gc(); + System.gc(); + System.gc(); + + System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++ (done)\n\n"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java new file mode 100644 index 00000000000..bfcd82f5742 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, 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 + * @summary Redefine shared class. GC should not cause crash with cached resolved_references. + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes /test/hotspot/jtreg/runtime/appcds/jvmti + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @requires vm.flavor != "minimal" + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * @build sun.hotspot.WhiteBox + * RedefineClassApp + * InstrumentationClassFileTransformer + * InstrumentationRegisterClassFileTransformer + * @run main/othervm RedefineClassTest + */ + +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import jdk.test.lib.Asserts; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class RedefineClassTest { + public static String bootClasses[] = { + "RedefineClassApp$Intf", + "RedefineClassApp$Bar", + "sun.hotspot.WhiteBox", + }; + public static String appClasses[] = { + "RedefineClassApp", + "RedefineClassApp$Foo", + }; + public static String sharedClasses[] = TestCommon.concat(bootClasses, appClasses); + + public static String agentClasses[] = { + "InstrumentationClassFileTransformer", + "InstrumentationRegisterClassFileTransformer", + "Util", + }; + + public static void main(String[] args) throws Throwable { + runTest(); + } + + public static void runTest() throws Throwable { + String bootJar = + ClassFileInstaller.writeJar("RedefineClassBoot.jar", bootClasses); + String appJar = + ClassFileInstaller.writeJar("RedefineClassApp.jar", appClasses); + String agentJar = + ClassFileInstaller.writeJar("InstrumentationAgent.jar", + ClassFileInstaller.Manifest.fromSourceFile("InstrumentationAgent.mf"), + agentClasses); + + String bootCP = "-Xbootclasspath/a:" + bootJar; + + String agentCmdArg; + agentCmdArg = "-javaagent:" + agentJar; + + TestCommon.testDump(appJar, sharedClasses, bootCP, "-Xlog:gc+region=trace"); + + OutputAnalyzer out = TestCommon.execAuto("-cp", appJar, + bootCP, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xlog:gc+region=trace,cds=info", + agentCmdArg, + "RedefineClassApp", bootJar, appJar); + out.reportDiagnosticSummary(); + + CDSOptions opts = (new CDSOptions()).setXShareMode("auto"); + TestCommon.checkExec(out, opts); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java new file mode 100644 index 00000000000..b90e75d57d8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Tests the format checking of class list format. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * @run main ClassListFormatA + */ + +public class ClassListFormatA extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE A1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ClassListFormatA", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib"); + //---------------------------------------------------------------------- + // TESTGROUP A: general bad input + //---------------------------------------------------------------------- + dumpShouldFail( + "TESTCASE A1: bad input - interface: instead of interfaces:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee interface: 1" + ), + "Unknown input:"); + + dumpShouldFail( + "TESTCASE A2: bad input - negative IDs not allowed", + appJar, classlist( + "Hello", + "java/lang/Object id: -1" + ), + "Error: negative integers not allowed"); + + dumpShouldFail( + "TESTCASE A3: bad input - bad ID (not an integer)", + appJar, classlist( + "Hello", + "java/lang/Object id: xyz" + ), + "Error: expected integer"); + + if (false) { + // FIXME - classFileParser.cpp needs fixing. + dumpShouldFail( + "TESTCASE A4: bad input - bad ID (integer too big)", + appJar, classlist( + "Hello", + "java/lang/Object id: 2147483648" // <- this is 0x80000000 + ), + "Error: expected integer"); + + // FIXME + dumpShouldFail( + "TESTCASE A5: bad input - bad ID (integer too big)", + appJar, classlist( + "Hello", + "java/lang/Object id: 21474836489" // bigger than 32-bit! + ), + "Error: expected integer"); + } + + // Good input: + dumpShouldPass( + "TESTCASE A6: extraneous spaces, tab characters and trailing new line characters", + appJar, classlist( + "Hello ", // trailing spaces + "java/lang/Object\tid:\t1", // \t instead of ' ' + "CustomLoadee id: 2 super: 1 source: " + customJarPath, + "CustomInterface2_ia id: 3 super: 1 source: " + customJarPath + " ", + "CustomInterface2_ib id: 4 super: 1 source: " + customJarPath + "\t\t\r" , + "CustomLoadee2 id: 5 super: 1 interfaces: 3 4 source: " + customJarPath // preceding spaces + )); + + int _max_allowed_line = 4096; // Must match ClassListParser::_max_allowed_line in C code. + int _line_buf_extra = 10; // Must match ClassListParser::_line_buf_extra in C code. + StringBuffer sbuf = new StringBuffer(); + for (int i=0; i<_max_allowed_line+1; i++) { + sbuf.append("x"); + } + + dumpShouldFail( + "TESTCASE A7: bad input - line too long", + appJar, classlist( + sbuf.toString() + ), + "input line too long (must be no longer than " + _max_allowed_line + " chars"); + + for (int i=0; i<_line_buf_extra + 1000; i++) { + sbuf.append("X"); + } + + dumpShouldFail( + "TESTCASE A8: bad input - line too long: try to overflow C buffer", + appJar, classlist( + sbuf.toString() + ), + "input line too long (must be no longer than " + _max_allowed_line + " chars"); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java new file mode 100644 index 00000000000..5e24eefb8c4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * @run main ClassListFormatB + */ + +public class ClassListFormatB extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE B1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ClassListFormatB", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib"); + //---------------------------------------------------------------------- + // TESTGROUP B if source IS specified + //---------------------------------------------------------------------- + dumpShouldFail( + "TESTCASE B1: if source: is specified, must specify super:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee id: 2 source: " + customJarPath + ), + "If source location is specified, super class must be also specified"); + + dumpShouldFail( + "TESTCASE B2: if source: is specified, must specify id:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee super: 1 source: " + customJarPath + ), + "If source location is specified, id must be also specified"); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java new file mode 100644 index 00000000000..7b1301c0137 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * Base class for ClassListFormat[A,B,C...].java + */ +public class ClassListFormatBase { + protected static String RUN_ONLY_TEST = null; + + static void dumpShouldFail(String caseHelp, String appJar, String[] appClasses, + String... expected_errors) throws Throwable { + if (RUN_ONLY_TEST != null && !caseHelp.startsWith(RUN_ONLY_TEST)) { + System.out.println("Skipped via RUN_ONLY_TEST: " + caseHelp); + return; + } + System.out.println("------------------------------"); + System.out.println(caseHelp); + System.out.println("------------------------------"); + + try { + OutputAnalyzer output = TestCommon.dump(appJar, appClasses); + output.shouldHaveExitValue(1); + for (String s : expected_errors) { + output.shouldContain(s); + } + } catch (Throwable t) { + System.out.println("FAILED CASE: " + caseHelp); + throw t; + } + } + + static void dumpShouldPass(String caseHelp, String appJar, String[] appClasses, + String... expected_msgs) throws Throwable { + if (RUN_ONLY_TEST != null && !caseHelp.startsWith(RUN_ONLY_TEST)) { + System.out.println("Skipped via RUN_ONLY_TEST: " + caseHelp); + return; + } + System.out.println("------------------------------"); + System.out.println(caseHelp); + System.out.println("------------------------------"); + + try { + OutputAnalyzer output = TestCommon.dump(appJar, appClasses); + output.shouldHaveExitValue(0); + output.shouldContain("Dumping"); + for (String s : expected_msgs) { + output.shouldContain(s); + } + } catch (Throwable t) { + System.out.println("FAILED CASE: " + caseHelp); + throw t; + } + } + + static String[] classlist(String... args) { + return TestCommon.list(args); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java new file mode 100644 index 00000000000..ab2006db56b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * @run main ClassListFormatC + */ + +public class ClassListFormatC extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE C1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ClassListFormatC", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", + "CustomInterface2_ib"); + + //---------------------------------------------------------------------- + // TESTGROUP C: if source IS NOT specified + //---------------------------------------------------------------------- + dumpShouldFail( + "TESTCASE C1: if source: is NOT specified, must NOT specify super:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee super: 1" + ), + "If source location is not specified, super class must not be specified"); + + dumpShouldFail( + "TESTCASE C2: if source: is NOT specified, must NOT specify interface:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee interfaces: 1" + ), + "If source location is not specified, interface(s) must not be specified"); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java new file mode 100644 index 00000000000..ed379a04629 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * @run main ClassListFormatD + */ + +public class ClassListFormatD extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE D1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ClassListFormatD", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", + "CustomInterface2_ib"); + + //---------------------------------------------------------------------- + // TESTGROUP D: bad use of IDs + //---------------------------------------------------------------------- + dumpShouldFail( + "TESTCASE D1: duplicated id:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee id: 1 super: 1 source: " + customJarPath + ), + "Duplicated ID 1 for class CustomLoadee"); + + dumpShouldFail( + "TESTCASE D2: bad ID for super:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee id: 2 super: 2 source: " + customJarPath + ), + "Super class id 2 is not yet loaded"); + + dumpShouldFail( + "TESTCASE D3: bad ID in interfaces:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee id: 2 super: 1 interfaces: 2 source: " + customJarPath + ), + "Interface id 2 is not yet loaded"); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java new file mode 100644 index 00000000000..f17ad47ab83 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * @run main ClassListFormatE + */ + +public class ClassListFormatE extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE E1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ClassListFormatE", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", + "CustomInterface2_ib"); + + //---------------------------------------------------------------------- + // TESTGROUP E: super class and interfaces + //---------------------------------------------------------------------- + dumpShouldFail( + "TESTCASE E1: missing interfaces: keyword", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomLoadee2 id: 1 super: 1 source: " + customJarPath + ), + "Class CustomLoadee2 implements the interface CustomInterface2_ia, but no interface has been specified in the input line"); + + dumpShouldFail( + "TESTCASE E2: missing one interface", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath, + "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath, + "CustomLoadee2 id: 4 super: 1 interfaces: 2 source: " + customJarPath + ), + "The interface CustomInterface2_ib implemented by class CustomLoadee2 does not match any of the specified interface IDs"); + + dumpShouldFail( + "TESTCASE E3: specifying an interface that's not implemented by the class", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath, + "CustomLoadee id: 2 super: 1 interfaces: 2 source: " + customJarPath + ), + "The number of interfaces (1) specified in class list does not match the class file (0)"); + + dumpShouldFail( + "TESTCASE E4: repeating an ID in the interfaces: keyword", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath, + "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath, + "CustomLoadee2 id: 4 super: 1 interfaces: 2 2 3 source: " + customJarPath + ), + "The number of interfaces (3) specified in class list does not match the class file (2)"); + + dumpShouldFail( + "TESTCASE E5: wrong super class", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath, + "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath, + "CustomLoadee id: 4 super: 1 source: " + customJarPath, + "CustomLoadee2 id: 5 super: 4 interfaces: 2 3 source: " + customJarPath + ), + "The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object"); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java b/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java new file mode 100644 index 00000000000..0075529e8f5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +// This is a utlitity test class for loading classes-under-test +// by means of custom class loader. +// See AppCDS/jvmti/transformRelatedClasses/TransformRelatedClasses.java +// for an example. +// Use this test app in conjunction with other tests +// to load and exercise classes using custom class loader(s). +// This class is intended to be called by the "main test driver" +// inside a child process, normally with sharing enabled. +// +// Arguments: customJarPath, loaderType, testClass +// customJarPath - a path to jar file containing classes for +// loading via this custom class loader, including the +// testClass +// loaderType - Currently only "unregistered" +// (Fingerprint verification method) is allowed +// testClass - the class to be loader; the test method with +// signature 'public static void test()' will be called +// on this class, so class must contain such method + + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Logger; + +public class CustomLoaderApp { + public static void ping() {}; + + private static void log(String msg) { + System.out.println("CustomLoaderApp: " + msg); + } + + public static void main(String[] args) throws Exception { + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + + String loaderType = args[1]; + log("loaderType = " + loaderType); + + String testClass = args[2]; + log("testClass = " + testClass); + + switch(loaderType) { + case "unregistered": + loadAndUseWithUnregisteredLoader(urls, testClass); + break; + default: + throw new IllegalArgumentException("loader type is wrong: " + loaderType); + } + } + + + // Load the test classes using unregistered loader + // (i.e. loader that is not using AppCDS API) + private static void loadAndUseWithUnregisteredLoader(URL[] urls, String testClass) + throws Exception { + URLClassLoader urlClassLoader = new URLClassLoader(urls); + callTestMethod(loadAndCheck(urlClassLoader, testClass)); + } + + private static Class loadAndCheck(ClassLoader loader, String className) + throws ClassNotFoundException { + Class c = loader.loadClass(className); + log("class =" + c); + log("loader = " + c.getClassLoader()); + + // Check that c is defined by the correct loader + if (c.getClassLoader() != loader) { + String msg = String.format("c.getClassLoader() equals to <%s>, expected <%s>", + c.getClassLoader(), loader); + throw new RuntimeException(msg); + } + return c; + } + + private static void callTestMethod(Class c) throws Exception { + Method[] methods = c.getDeclaredMethods(); + for (Method m : methods) { + log("method = " + m.getName()); + if (m.getName().equals("test")) + m.invoke(null); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java new file mode 100644 index 00000000000..0d5257df89b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Hello World test for AppCDS custom loader support + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * @compile test-classes/Hello.java test-classes/CustomLoadee.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller -jar hello.jar Hello + * @run main ClassFileInstaller -jar hello_custom.jar CustomLoadee + * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run main HelloCustom + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +public class HelloCustom { + public static void main(String[] args) throws Exception { + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String appJar = ClassFileInstaller.getJarPath("hello.jar"); + String customJarPath = ClassFileInstaller.getJarPath("hello_custom.jar"); + + // Dump the archive + String classlist[] = new String[] { + "Hello", + "java/lang/Object id: 1", + "CustomLoadee id: 2 super: 1 source: " + customJarPath + }; + + OutputAnalyzer output; + TestCommon.testDump(appJar, classlist, + // command-line arguments ... + use_whitebox_jar); + + output = TestCommon.exec(appJar, + // command-line arguments ... + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "Hello", customJarPath); + TestCommon.checkExec(output); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java new file mode 100644 index 00000000000..52a573f1885 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Check that during dumping, the classes for BOOT/EXT/APP loaders are segregated from the + * custom loader classes. + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/LoaderSegregation.java + * test-classes/CustomLoadee.java test-classes/CustomLoadee2.java + * test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java + * test-classes/CustomLoadee3.java test-classes/CustomLoadee3Child.java + * test-classes/OnlyBuiltin.java + * test-classes/OnlyUnregistered.java + * ../test-classes/Util.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main LoaderSegregationTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +/** + * See "Handling of the classes in the AppCDS archive" at the top of + * systemDicrionatyShared.hpp. + * + * This test ensure that the 2 types of archived classes (BUILTIN and UNREGISTERED) + * are segregated at both dump-time and run time: + * + * [A] An archived BUILTIN class cannot be a subclass of a non-BUILTIN class. + * [B] An archived BUILTIN class cannot implement a non-BUILTIN interface. + * [C] BUILTIN and UNREGISTERED classes can be loaded only by their corresponding + * type of loaders. + * + */ +public class LoaderSegregationTest { + public static void main(String[] args) throws Exception { + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String appJar = JarBuilder.build("LoaderSegregation_app", "LoaderSegregation", + "CustomLoadee", "CustomLoadee2", "CustomLoadee3Child", "CustomInterface2_ia", + "OnlyBuiltin", "Util"); + + String app2Jar = JarBuilder.build("LoaderSegregation_app2", "CustomLoadee3", "CustomInterface2_ib"); + + String customJarPath = JarBuilder.build("LoaderSegregation_custom", "CustomLoadee", + "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib", + "CustomLoadee3", "CustomLoadee3Child", + "OnlyBuiltin", "OnlyUnregistered"); + + // Dump the archive + String classlist[] = new String[] { + "LoaderSegregation", + "java/lang/Object id: 1", + + // These are the UNREGISTERED classes: they have "source:" + // but they don't have "loader:". + "CustomLoadee id: 2 super: 1 source: " + customJarPath, + + "CustomInterface2_ia id: 3 super: 1 source: " + customJarPath, + "CustomInterface2_ib id: 4 super: 1 source: " + customJarPath, + "CustomLoadee2 id: 5 super: 1 interfaces: 3 4 source: " + customJarPath, + + "CustomLoadee3 id: 6 super: 1 source: " + customJarPath, + "CustomLoadee3Child id: 7 super: 6 source: " + customJarPath, + + // At dump time, the following BUILTIN classes are loaded after the UNREGISTERED + // classes from above. However, at dump time, they cannot use the UNREGISTERED classes are their + // super or interface. + "CustomLoadee", // can be loaded at dump time + "CustomLoadee2", // cannot be loaded at dump time (interface missing) + "CustomLoadee3Child", // cannot be loaded at dump time (super missing) + + // Check that BUILTIN and UNREGISTERED classes can be loaded only by their + // corresponding type of loaders. + "OnlyBuiltin", + "OnlyUnregistered id: 9 super: 1 source: " + customJarPath, + }; + + OutputAnalyzer output; + TestCommon.testDump(appJar, classlist, + // command-line arguments ... + use_whitebox_jar); + + output = TestCommon.exec(TestCommon.concatPaths(appJar, app2Jar), + // command-line arguments ... + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.base/java.security=ALL-UNNAMED", + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "LoaderSegregation", customJarPath); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java new file mode 100644 index 00000000000..a536d831a27 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import jdk.test.lib.process.OutputAnalyzer; + +/* + * This is a base class for the following test cases: + * ParallelTestMultiFP.java + * ParallelTestSingleFP.java + */ +public class ParallelTestBase { + public static final int MAX_CLASSES = 40; // must match ../test-classes/ParallelLoad.java + public static int NUM_THREADS = 4; // must match ../test-classes/ParallelLoad.java + + public static final int SINGLE_CUSTOM_LOADER = 1; + public static final int MULTI_CUSTOM_LOADER = 2; + + public static final int FINGERPRINT_MODE = 1; + + public static void run(String[] args, int loaderType, int mode) throws Exception { + String[] cust_classes = new String[MAX_CLASSES]; + String[] cust_list; + + if (mode == FINGERPRINT_MODE) { + cust_list = new String[MAX_CLASSES]; + } else { + cust_list = new String[MAX_CLASSES * NUM_THREADS]; + } + + for (int i = 0; i<MAX_CLASSES; i++) { + cust_classes[i] = "ParallelClass" + i; + } + String customJarPath = JarBuilder.build("ParallelTestBase", cust_classes); + + for (int i = 0, n=0; i<MAX_CLASSES; i++) { + int super_id = 1; + if (mode == FINGERPRINT_MODE) { + // fingerprint mode -- no need to use the "loader:" option. + int id = i + 2; + cust_list[i] = cust_classes[i] + " id: " + id + " super: " + super_id + " source: " + customJarPath; + } else { + throw new RuntimeException("Only FINGERPRINT_MODE is supported"); + } + } + + String app_list[]; + String mainClass; + String appJar; + + if (mode == FINGERPRINT_MODE) { + appJar = JarBuilder.build("parallel_fp", + "ParallelLoad", + "ParallelLoadThread", + "ParallelLoadWatchdog"); + app_list = new String[] { + "java/lang/Object id: 1", + "ParallelLoad", + "ParallelLoadThread", + "ParallelLoadWatchdog" + }; + mainClass = "ParallelLoad"; + } else { + throw new RuntimeException("Currently only FINGERPRINT_MODE is supported"); + } + + OutputAnalyzer output; + TestCommon.testDump(appJar, TestCommon.concat(app_list, cust_list)); + + String loaderTypeArg = (loaderType == SINGLE_CUSTOM_LOADER) ? "SINGLE_CUSTOM_LOADER" : "MULTI_CUSTOM_LOADER"; + String modeArg = "FINGERPRINT_MODE"; + + output = TestCommon.exec(appJar, + // command-line arguments ... + "--add-opens=java.base/java.security=ALL-UNNAMED", + mainClass, loaderTypeArg, modeArg, customJarPath); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java new file mode 100644 index 00000000000..24b1f4034e9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Load classes from CDS archive into multiple custom loader using parallel threads + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile ../test-classes/ParallelLoad.java ../test-classes/ParallelClasses.java + * @run main ParallelTestMultiFP + */ + +public class ParallelTestMultiFP extends ParallelTestBase { + public static void main(String[] args) throws Exception { + ParallelTestBase.run(args, MULTI_CUSTOM_LOADER, FINGERPRINT_MODE); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java new file mode 100644 index 00000000000..1a7f0b75609 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Load classes from CDS archive into a single custom loader using parallel threads (finger print) + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile ../test-classes/ParallelLoad.java ../test-classes/ParallelClasses.java + * @run main ParallelTestSingleFP + */ + +public class ParallelTestSingleFP extends ParallelTestBase { + public static void main(String[] args) throws Exception { + ParallelTestBase.run(args, SINGLE_CUSTOM_LOADER, FINGERPRINT_MODE); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java new file mode 100644 index 00000000000..47f1cafd639 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Make sure prohibited packages cannot be stored into archive for custom loaders. + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile ClassListFormatBase.java test-classes/Hello.java test-classes/InProhibitedPkg.java + * @run main ProhibitedPackageNamesTest + */ + +public class ProhibitedPackageNamesTest extends ClassListFormatBase { + static { + // Uncomment the following line to run only one of the test cases + // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE PPN1"; + } + + public static void main(String[] args) throws Throwable { + String appJar = JarBuilder.getOrCreateHelloJar(); + String customJarPath = JarBuilder.build("ProhibitedPackageNames_custom", "java/InProhibitedPkg"); + + dumpShouldPass( + "TESTCASE PPN1: prohibited package name without loader:", + appJar, classlist( + "Hello", + "java/lang/Object id: 1", + // Without "loader:" keyword. + "java/InProhibitedPkg id: 2 super: 1 source: " + customJarPath + ), + "Prohibited package for non-bootstrap classes: java/InProhibitedPkg.class"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java new file mode 100644 index 00000000000..faeb75ffa82 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary AppCDS handling of protection domain in custom loaders. + * + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/ProtDomain.java + * @run main ProtectionDomain + */ + +public class ProtectionDomain { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.build("ProtectionDomain-app", "ProtDomain"); + + String customJar = JarBuilder.build("ProtectionDomain-custom", + "ProtDomainClassForArchive", "ProtDomainNotForArchive"); + String[] classlist = new String[] { + "java/lang/Object id: 1", + "ProtDomain id: 2 super: 1 source: " + appJar, + "ProtDomainClassForArchive id: 3 super: 1 source: " + customJar + }; + + TestCommon.testDump(appJar, classlist); + + // First class is loaded from CDS, second class is loaded from JAR + TestCommon.checkExec(TestCommon.exec(appJar, "-verbose:class", "ProtDomain", customJar), + "[class,load] ProtDomainClassForArchive source: shared objects file", + "[class,load] ProtDomainNotForArchive source: file"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java new file mode 100644 index 00000000000..2b4e40938db --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Testing the loading of a class with the same name in two different class loaders. + * + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/CustomLoadee.java + * test-classes/CustomLoadee3.java + * test-classes/SameNameUnrelatedLoaders.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main SameNameInTwoLoadersTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + + +public class SameNameInTwoLoadersTest { + private static String appJar; + private static String customJar; + private static String useWbParam; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("SameNameInTwoLoadersTest", + "SameNameUnrelatedLoaders"); + + customJar = JarBuilder.build("SameNameInTwoLoadersTest_custom", "CustomLoadee", "CustomLoadee3"); + + useWbParam = "-Xbootclasspath/a:" + + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");; + + // ====== unrelated loaders + executeTestCase(getClassList_FP(), + "SameNameUnrelatedLoaders", "FpBoth"); + } + + private static void executeTestCase(String[] classlist, + String testClass, String testCaseId) throws Exception { + classlist[0] = testClass; + + TestCommon.testDump(appJar, classlist, useWbParam); + + OutputAnalyzer output = TestCommon.exec(appJar, + // command-line arguments ... + "--add-opens=java.base/java.security=ALL-UNNAMED", + useWbParam, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + testClass, + customJar, testCaseId); + TestCommon.checkExec(output); + } + + // Single entry, no loader specified (FP method) + private static String[] getClassList_FP() { + return new String[] { + "SameNameUnrelatedLoaders", + "java/lang/Object id: 1", + "CustomLoadee id: 10 super: 1 source: " + customJar, + }; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java new file mode 100644 index 00000000000..042db736420 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Make sure classes intended for custom loaders cannot be loaded by BOOT/EXT/APP loaders + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/UnintendedLoaders.java test-classes/CustomLoadee.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main UnintendedLoadersTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +public class UnintendedLoadersTest { + public static void main(String[] args) throws Exception { + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String appJar = JarBuilder.build("UnintendedLoaders_app", "UnintendedLoaders"); + String customJarPath = JarBuilder.build("UnintendedLoaders_custom", "CustomLoadee"); + + // Dump the archive + String classlist[] = new String[] { + "UnintendedLoadersTest", + "java/lang/Object id: 1", + + // Without "loader:" keyword. + "CustomLoadee id: 2 super: 1 source: " + customJarPath, + }; + + OutputAnalyzer output; + TestCommon.testDump(appJar, classlist, + // command-line arguments ... + use_whitebox_jar); + + output = TestCommon.exec(appJar, + // command-line arguments ... + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "UnintendedLoaders"); + TestCommon.checkExec(output); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java new file mode 100644 index 00000000000..8b9885e6bab --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test the behavior when shared classes loaded by custom loaders are + * unloaded. + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model == "64") + * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/testlibrary + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox ClassUnloadCommon + * @compile test-classes/UnloadUnregisteredLoader.java test-classes/CustomLoadee.java + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ClassFileInstaller ClassUnloadCommon + * @run main ClassFileInstaller ClassUnloadCommon$1 + * @run main ClassFileInstaller ClassUnloadCommon$TestFailure + * @run main UnloadUnregisteredLoaderTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + +public class UnloadUnregisteredLoaderTest { + public static void main(String[] args) throws Exception { + String appJar1 = JarBuilder.build("UnloadUnregisteredLoader_app1", "UnloadUnregisteredLoader"); + String appJar2 = JarBuilder.build(true, "UnloadUnregisteredLoader_app2", + "ClassUnloadCommon", "ClassUnloadCommon$1", "ClassUnloadCommon$TestFailure"); + String customJarPath = JarBuilder.build("UnloadUnregisteredLoader_custom", "CustomLoadee"); + String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + String classpath = TestCommon.concatPaths(appJar1, appJar2); + String classlist[] = new String[] { + "UnloadUnregisteredLoader", + "ClassUnloadCommon", + "ClassUnloadCommon$1", + "ClassUnloadCommon$TestFailure", + "java/lang/Object id: 1", + "CustomLoadee id: 2 super: 1 source: " + customJarPath, + }; + + OutputAnalyzer output; + TestCommon.testDump(classpath, classlist, + // command-line arguments ... + use_whitebox_jar); + + output = TestCommon.exec(classpath, + // command-line arguments ... + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "UnloadUnregisteredLoader", + customJarPath); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java new file mode 100644 index 00000000000..0175c5630a4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Ensure that support for AppCDS custom class loaders are not enabled on unsupported platforms. + * The only supported platforms are Linux/AMD64 and 64-bit Solaris. + * (NOTE: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * @compile test-classes/SimpleHello.java + * @run main UnsupportedPlatforms + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; + +public class UnsupportedPlatforms { + public static String PLATFORM_NOT_SUPPORTED_WARNING = + "AppCDS custom class loaders not supported on this platform"; + + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.build("UnsupportedPlatforms", "SimpleHello"); + + // Dump the archive + String classlist[] = new String[] { + "SimpleHello", + "java/lang/Object id: 1", + "CustomLoadee id: 2 super: 1 source: " + appJar + }; + + OutputAnalyzer out = TestCommon.dump(appJar, classlist); + + if ((Platform.isSolaris() && Platform.is64bit()) || + (Platform.isLinux() && Platform.isX64())) { + out.shouldNotContain(PLATFORM_NOT_SUPPORTED_WARNING); + out.shouldHaveExitValue(0); + } else { + out.shouldContain(PLATFORM_NOT_SUPPORTED_WARNING); + out.shouldHaveExitValue(1); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java new file mode 100644 index 00000000000..99da97c4c83 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +interface CustomInterface2_ia {} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java new file mode 100644 index 00000000000..5d4990725b4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +interface CustomInterface2_ib {} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java new file mode 100644 index 00000000000..7848c282873 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class CustomLoadee { + public String toString() { + return "this is CustomLoadee"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java new file mode 100644 index 00000000000..779f1919d28 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class CustomLoadee2 implements CustomInterface2_ia, CustomInterface2_ib { + public String toString() { + return "this is CustomLoadee"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java new file mode 100644 index 00000000000..7af68cf6100 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class CustomLoadee3 { + public String toString() { + return "this is CustomLoadee3"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java new file mode 100644 index 00000000000..fa47bf962cf --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class CustomLoadee3Child extends CustomLoadee3 { + public String toString() { + return "this is CustomLoadee3Child"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java new file mode 100644 index 00000000000..2c9c796bca1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.*; +import java.net.*; +import sun.hotspot.WhiteBox; + +public class Hello { + public static void main(String args[]) throws Exception { + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + System.out.println(path); + System.out.println(url); + + URLClassLoader urlClassLoader = new URLClassLoader(urls); + Class c = urlClassLoader.loadClass("CustomLoadee"); + System.out.println(c); + System.out.println(c.getClassLoader()); + + // [1] Check that CustomLoadee is defined by the correct loader + if (c.getClassLoader() != urlClassLoader) { + throw new RuntimeException("c.getClassLoader() == " + c.getClassLoader() + + ", expected == " + urlClassLoader); + } + + // [2] Check that CustomLoadee is loaded from shared archive. + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(Hello.class)) { + if (!wb.isSharedClass(c)) { + throw new RuntimeException("wb.isSharedClass(c) should be true"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java new file mode 100644 index 00000000000..0bd6ad64385 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +package java; + +public class InProhibitedPkg { + static { + if (true) { + throw new RuntimeException("This class shouldn't be loaded by any loader other than BOOT"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf new file mode 100644 index 00000000000..19f4c6217cd --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Created-By: 1.9.0-internal (Oracle Corporation) +Specification-Title: My Specification Title +Specification-Version: 1.0 +Specification-Vendor: My Specification Vendor +Implementation-Title: My Implementation Title +Implementation-Version: 1.0 +Implementation-Vendor: My Implementation Vendor + +Name: pkg1/ +Implementation-Version: 2.0 +Sealed: true diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java new file mode 100644 index 00000000000..4cd4ab7d640 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.*; +import java.net.*; +import sun.hotspot.WhiteBox; + +public class LoaderSegregation { + // Use these definitions instead of literal strings, to avoid typos. + static final String ONLY_BUILTIN = "OnlyBuiltin"; + static final String ONLY_UNREGISTERED = "OnlyUnregistered"; + + public static void main(String args[]) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + + // [A] An archived BUILTIN class cannot be a subclass of a non-BUILTIN class. + // [B] An archived BUILTIN class cannot implement a non-BUILTIN interface. + if (wb.isSharedClass(LoaderSegregation.class)) { + // [1] check that CustomLoadee is loadable from archive + if (!wb.isSharedClass(CustomLoadee.class)) { + throw new RuntimeException("wb.isSharedClass(CustomLoadee.class) should be true"); + } + + // [2] CustomInterface2_ia should be archived, even though it was not specified in the classlist. + // It was successfully dumped as a side effect of attempting to load CustomLoadee2 + // during dump time. Note that CustomLoadee2 failed to dump because one of its interfaces, + // CustomInterface2_ib, was not loadable from the BOOT/EXT/APP classpath. during dump time. + if (!wb.isSharedClass(CustomInterface2_ia.class)) { + throw new RuntimeException("wb.isSharedClass(CustomInterface2_ia.class) should be true"); + } + + // [3] Check that the BUILTIN versions of CustomLoadee2 and CustomLoadee3Child are loadable + // at run time (since we have append LoaderSegregation_app2.jar the classpath), + // but these classes must be loaded from the JAR file. + if (wb.isSharedClass(CustomLoadee2.class)) { + throw new RuntimeException("wb.isSharedClass(CustomLoadee2.class) should be false"); + } + if (wb.isSharedClass(CustomLoadee3.class)) { + throw new RuntimeException("wb.isSharedClass(CustomLoadee3.class) should be false"); + } + if (wb.isSharedClass(CustomLoadee3Child.class)) { + throw new RuntimeException("wb.isSharedClass(CustomLoadee3Child.class) should be false"); + } + } + + // [C] BUILTIN and UNREGISTERED classes can be loaded only by their corresponding + // type of loaders. + + String path = args[0]; + File jarFile = new File(path); + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + ClassLoader appLoader = LoaderSegregation.class.getClassLoader(); + + { // BUILTIN LOADER + try { + appLoader.loadClass(ONLY_UNREGISTERED); + throw new RuntimeException("BUILTIN loader cannot load archived UNREGISTERED class"); + } catch (ClassNotFoundException expected) {} + } + + { // UNREGISTERED LOADER + URLClassLoader urlClassLoader = new URLClassLoader(urls); + Class c2 = Util.defineClassFromJAR(urlClassLoader, jarFile, ONLY_BUILTIN); + + if (c2.getClassLoader() != urlClassLoader) { + throw new RuntimeException("Error in test"); + } + + if (wb.isSharedClass(LoaderSegregation.class)) { + if (wb.isSharedClass(c2)) { + throw new RuntimeException("wb.isSharedClass(c2) should be false - " + + "unregistered loader cannot load an archived BUILTIN class"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java new file mode 100644 index 00000000000..90390f52e8b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +// See ../LoaderSegregationTest.java for details. +// +// This class is archived only as a BUILTIN class. +public class OnlyBuiltin {} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java new file mode 100644 index 00000000000..9a24f2925ad --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +// See ../LoaderSegregationTest.java for details. +// +// This class is archived only as a UNREGISTERED class. +public class OnlyUnregistered {} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java new file mode 100644 index 00000000000..bdc37ca78f3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.security.ProtectionDomain; +import java.net.URLClassLoader; +import java.net.URL; +import java.io.File; + +// Intended to be called from test ProtectionDomain.java +// +// ProtDomainClassForArchive is stored in CDS archive. +// ProtDomainNotForArchive is NOT stored in CDS archive. +// +// However, they should have the same ProtectionDomain instance. +public class ProtDomain { + public static void main(String args[]) throws Exception { + String customLdrPath = args[0]; + + URL[] urls = new URL[] {new File(customLdrPath).toURI().toURL()}; + URLClassLoader ldr = new URLClassLoader(urls); + ProtectionDomain domain1 = ldr.loadClass("ProtDomainClassForArchive").getProtectionDomain(); + ProtectionDomain domain2 = ldr.loadClass("ProtDomainNotForArchive").getProtectionDomain(); + + System.out.println("domain1 = " + domain1); + System.out.println("domain2 = " + domain2); + + if (domain1 != domain2) + throw new RuntimeException("Protection Domains do not match!"); + } +} + +class ProtDomainClassForArchive {} + +class ProtDomainNotForArchive {} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java new file mode 100644 index 00000000000..10b436563ec --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import sun.hotspot.WhiteBox; + +public class SameNameUnrelatedLoaders { + public static void main(String args[]) throws Exception { + String path = args[0]; + String testCase = args[1]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + + ClassLoader appLoader = SameNameUnrelatedLoaders.class.getClassLoader(); + URLClassLoader ldr01 = null; + URLClassLoader ldr02 = null; + + switch (testCase) { + case "FpBoth": + ldr01 = new URLClassLoader(urls); + ldr02 = new URLClassLoader(urls); + break; + + default: + throw new IllegalArgumentException("Invalid testCase ID"); + } + + + Class class01 = ldr01.loadClass("CustomLoadee"); + Class class02 = ldr02.loadClass("CustomLoadee"); + + System.out.println("class01 = " + class01); + System.out.println("class02 = " + class02); + + if (class01.getClassLoader() != ldr01) { + throw new RuntimeException("class01 loaded by wrong loader"); + } + if (class02.getClassLoader() != ldr02) { + throw new RuntimeException("class02 loaded by wrong loader"); + } + + if (true) { + if (class01.isAssignableFrom(class02)) { + throw new RuntimeException("assignable condition failed"); + } + + Object obj01 = class01.newInstance(); + Object obj02 = class02.newInstance(); + + if (class01.isInstance(obj02)) { + throw new RuntimeException("instance relationship condition 01 failed"); + } + if (class02.isInstance(obj01)) { + throw new RuntimeException("instance relationship condition 02 failed"); + } + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(SameNameUnrelatedLoaders.class)) { + boolean class1Shared = wb.isSharedClass(class01); + boolean class2Shared = wb.isSharedClass(class02); + + if (testCase.equals("FpBoth")) { + if (!class1Shared) { + throw new RuntimeException("first class is not shared"); + } + + if (class2Shared) { + throw new RuntimeException("second class is shared, " + + "and it should not be - first come first serve violation"); + } + } else { + if (! (class1Shared && class2Shared) ) + throw new RuntimeException("both classes expected to be shared, but are not"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java new file mode 100644 index 00000000000..3da0d6c8cb5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class SimpleHello { + public static void main(String[] args) { + System.out.println("Simple Hello"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java new file mode 100644 index 00000000000..c380876d3ad --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class UnintendedLoaders { + public static void main(String[] args) throws Exception { + ClassLoader loaders[] = new ClassLoader[2]; + loaders[0] = UnintendedLoaders.class.getClassLoader(); // app loader + loaders[1] = loaders[0].getParent(); // platform loader + + String[] names = { + "CustomLoadee", + }; + + + for (int i=0; i<3; i++) { + for (String s : names) { + try { + if (i <= 1) { + System.out.println(loaders[i].loadClass(s)); + } else { + System.out.println(Class.forName(s)); + } + } catch (ClassNotFoundException e) { + System.out.println("Expected exception:" + e); + continue; + } + throw new RuntimeException("The class \"" + s + "\" should not be resolved by the application or platform class loader"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java new file mode 100644 index 00000000000..47bf6d1b2e1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import sun.hotspot.WhiteBox; + +public class UnloadUnregisteredLoader { + public static void main(String args[]) throws Exception { + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + WhiteBox wb = WhiteBox.getWhiteBox(); + String className = "CustomLoadee"; + + for (int i=0; i<5; i++) { + doit(urls, className, (i == 0)); + + ClassUnloadCommon.triggerUnloading(); + ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded"); + } + } + + public static void doit(URL urls[], String className, boolean isFirstTime) throws Exception { + ClassLoader appLoader = UnloadUnregisteredLoader.class.getClassLoader(); + URLClassLoader custLoader = new URLClassLoader(urls, appLoader); + + Class klass = custLoader.loadClass(className); + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(UnloadUnregisteredLoader.class)) { + if (isFirstTime) { + // First time: we should be able to load the class from the CDS archive + if (!wb.isSharedClass(klass)) { + throw new RuntimeException("wb.isSharedClass(klass) should be true for first time"); + } + } else { + // Second time: the class in the CDS archive is not available, because it has not been cleaned + // up (see bug 8140287), so we must load the class dynamically. + // + // FIXME: after 8140287 is fixed, class should be shard regardless of isFirstTime. + if (wb.isSharedClass(klass)) { + throw new RuntimeException("wb.isSharedClass(klass) should be false for second time"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java new file mode 100644 index 00000000000..3deb59b3219 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 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 + * @summary test the ability to archive array classes and load them from the archive + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules jdk.jartool/sun.tools.jar + * @compile ArrayTestHelper.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ArrayTest + */ + +import java.util.List; +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; + +public class ArrayTest { + + static String arrayClasses[] = { + "ArrayTestHelper", + "[Ljava/lang/Comparable;", + "[I" + }; + + public static void main(String[] args) throws Exception { + JarBuilder.build("arrayTestHelper", "ArrayTestHelper"); + + String appJar = TestCommon.getTestJar("arrayTestHelper.jar"); + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar; + + // create an archive containing array classes + TestCommon.dump(appJar, TestCommon.list(arrayClasses), bootClassPath, "-verbose:class"); + + List<String> argsList = new ArrayList<String>(); + argsList.add("-XX:+UnlockDiagnosticVMOptions"); + argsList.add("-XX:+WhiteBoxAPI"); + argsList.add("-cp"); + argsList.add(appJar); + argsList.add(bootClassPath); + argsList.add("-verbose:class"); + argsList.add("ArrayTestHelper"); + // the following are input args to the ArrayTestHelper. + for (int i = 0; i < arrayClasses.length; i++) { + argsList.add(arrayClasses[i]); + } + String[] opts = new String[argsList.size()]; + opts = argsList.toArray(opts); + OutputAnalyzer output = TestCommon.execCommon(opts); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java new file mode 100644 index 00000000000..614f47e6546 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class ArrayTestHelper { + public static void main(String[] args) throws Throwable { + + // load the classes one by one and ensure each one is from + // the shared archive + for (int i = 0; i < args.length; i++) { + + String cn = args[i].replace('/', '.'); + Class cls = Class.forName(cn); + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(cls)) { + System.out.println("As expected, " + args[i] + " is in shared space."); + } else { + throw new java.lang.RuntimeException(args[i] + " is not in shared space."); + } + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java b/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java new file mode 100644 index 00000000000..804c480cd74 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 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 + * @summary ensure no anonymous class is being dumped into the CDS archive + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules jdk.jartool/sun.tools.jar + * @compile ../test-classes/Hello.java + * @run main CheckAnonymousClass + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class CheckAnonymousClass { + + public static void main(String[] args) throws Exception { + JarBuilder.build("hello", "Hello"); + + String appJar = TestCommon.getTestJar("hello.jar"); + + TestCommon.dump(appJar, TestCommon.list("Hello", "org/omg/CORBA/ORB"), + "--add-modules", "java.corba", "-Xlog:class+load=info"); + + OutputAnalyzer output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", + "-cp", appJar, "-Xlog:class+load=info", "--add-modules", "java.corba", "Hello"); + + String prefix = ".class.load. "; + // class name pattern like the following: + // jdk.internal.loader.BuiltinClassLoader$$Lambda$1/1816757085 + // java.lang.invoke.LambdaForm$MH/1585787493 + String class_pattern = ".*Lambda([a-z0-9$]+)/([0-9]+).*"; + String suffix = ".*source: shared objects file.*"; + String pattern = prefix + class_pattern + suffix; + // during run time, anonymous classes shouldn't be loaded from the archive + try { + output.shouldNotMatch(pattern); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + + // inspect the archive and make sure no anonymous class is in there + output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", + "-cp", appJar, "-Xlog:class+load=info", "-XX:+PrintSharedArchiveAndExit", + "-XX:+PrintSharedDictionary", "--add-modules", "java.corba", "Hello"); + try { + output.shouldNotMatch(class_pattern); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java new file mode 100644 index 00000000000..22a7dad0a6d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, 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 + * @summary When dumping the CDS archive, try to cause garbage collection while classes are being loaded. + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * @build GCDuringDumpTransformer Hello + * @run main/othervm GCDuringDump + */ + +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class GCDuringDump { + public static String appClasses[] = { + "Hello", + }; + public static String agentClasses[] = { + "GCDuringDumpTransformer", + }; + + public static void main(String[] args) throws Throwable { + String agentJar = + ClassFileInstaller.writeJar("GCDuringDumpTransformer.jar", + ClassFileInstaller.Manifest.fromSourceFile("GCDuringDumpTransformer.mf"), + agentClasses); + + String appJar = + ClassFileInstaller.writeJar("GCDuringDumpApp.jar", appClasses); + + String gcLog = "-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug"; + + for (int i=0; i<2; i++) { + // i = 0 -- run without agent = no extra GCs + // i = 1 -- run with agent = cause extra GCs + + String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar; + + TestCommon.testDump(appJar, TestCommon.list("Hello"), + extraArg, "-Xmx32m", gcLog); + + OutputAnalyzer output = TestCommon.execCommon( + "-cp", appJar, + "-Xmx32m", + "-XX:+PrintSharedSpaces", + gcLog, + "Hello"); + TestCommon.checkExec(output); + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java new file mode 100644 index 00000000000..bbde62429c4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; + +public class GCDuringDumpTransformer implements ClassFileTransformer { + static int n = 0; + public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined, + ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException { + n++; + + System.out.println("dump time loading: " + name + " in loader: " + loader); + System.out.println("making garbage: " + n); + try { + makeGarbage(); + } catch (Throwable t) { + t.printStackTrace(); + try { + Thread.sleep(200); // let GC to have a chance to run + } catch (Throwable t2) {} + } + System.out.println("making garbage: done"); + + return null; + } + + private static Instrumentation savedInstrumentation; + + public static void premain(String agentArguments, Instrumentation instrumentation) { + System.out.println("ClassFileTransformer.premain() is called"); + instrumentation.addTransformer(new GCDuringDumpTransformer(), /*canRetransform=*/true); + savedInstrumentation = instrumentation; + } + + public static Instrumentation getInstrumentation() { + return savedInstrumentation; + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + premain(args, inst); + } + + public static void makeGarbage() { + for (int x=0; x<10; x++) { + Object[] a = new Object[10000]; + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf new file mode 100644 index 00000000000..8b44e6e9801 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Premain-Class: GCDuringDumpTransformer +Agent-Class: GCDuringDumpTransformer +Can-Retransform-Classes: true +Can-Redefine-Classes: true diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java new file mode 100644 index 00000000000..38e23c31a30 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, 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 + * @summary Similar to GCDuringDumping.java, this test adds the -XX:SharedArchiveConfigFile + * option for testing the interaction with GC and shared strings. + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @requires vm.gc.G1 + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * @build sun.hotspot.WhiteBox GCDuringDumpTransformer GCSharedStringsDuringDumpWb + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm/timeout=480 GCSharedStringsDuringDump + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import sun.hotspot.WhiteBox; + +public class GCSharedStringsDuringDump { + public static String appClasses[] = { + "GCSharedStringsDuringDumpWb", + }; + public static String agentClasses[] = { + "GCDuringDumpTransformer", + }; + + public static void main(String[] args) throws Throwable { + String agentJar = + ClassFileInstaller.writeJar("GCDuringDumpTransformer.jar", + ClassFileInstaller.Manifest.fromSourceFile("GCDuringDumpTransformer.mf"), + agentClasses); + + String appJar = + ClassFileInstaller.writeJar("GCSharedStringsDuringDumpApp.jar", appClasses); + + String gcLog = "-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug"; + + String sharedArchiveCfgFile = + System.getProperty("user.dir") + File.separator + "GCSharedStringDuringDump_gen.txt"; + try (FileOutputStream fos = new FileOutputStream(sharedArchiveCfgFile)) { + PrintWriter out = new PrintWriter(new OutputStreamWriter(fos)); + out.println("VERSION: 1.0"); + out.println("@SECTION: String"); + out.println("31: shared_test_string_unique_14325"); + for (int i=0; i<100000; i++) { + String s = "generated_string " + i; + out.println(s.length() + ": " + s); + } + out.close(); + } + + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar; + + for (int i=0; i<2; i++) { + // i = 0 -- run without agent = no extra GCs + // i = 1 -- run with agent = cause extra GCs + + String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar; + + OutputAnalyzer output = TestCommon.dump( + appJar, TestCommon.list("GCSharedStringsDuringDumpWb"), + bootClassPath, extraArg, "-Xmx32m", gcLog, + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:SharedReadOnlySize=30m", + "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile); + + if (output.getStdout().contains("Too many string space regions") || + output.getStderr().contains("Unable to write archive heap memory regions") || + output.getStdout().contains("Try increasing NewSize") || + output.getExitValue() != 0) { + // Try again with larger heap and NewSize, this should increase the + // G1 heap region size to 2M + TestCommon.testDump( + appJar, TestCommon.list("GCSharedStringsDuringDumpWb"), + bootClassPath, extraArg, "-Xmx8g", "-XX:NewSize=8m", gcLog, + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:SharedReadOnlySize=30m", + "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile); + } + + output = TestCommon.execCommon( + "-cp", appJar, + bootClassPath, + "-Xmx32m", + "-XX:+PrintSharedSpaces", + "-XX:+UseCompressedOops", + "-XX:+UseG1GC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:SharedReadOnlySize=30m", + gcLog, + "GCSharedStringsDuringDumpWb"); + TestCommon.checkExec(output); + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java new file mode 100644 index 00000000000..3fcebe5f5ed --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class GCSharedStringsDuringDumpWb { + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + String s = "shared_test_string_unique_14325"; + s = s.intern(); + CheckString(wb, s); + for (int i=0; i<100000; i++) { + s = "generated_string " + i; + s = s.intern(); + CheckString(wb, s); + } + } + + public static void CheckString(WhiteBox wb, String s) { + if (!wb.areSharedStringsIgnored() && !wb.isShared(s)) { + throw new RuntimeException("String is not shared."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java new file mode 100644 index 00000000000..99efbcbf135 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Abort dumping if any of the new jigsaw vm options is specified. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib .. + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile ../test-classes/Hello.java + * @run main CheckUnsupportedDumpingOptions + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class CheckUnsupportedDumpingOptions { + private static final String[] jigsawOptions = { + "-m", + "--limit-modules", + "--module-path", + "--upgrade-module-path", + "--patch-module" + }; + private static final String[] optionValues = { + "mymod", + "mymod", + "mydir", + ".", + "java.naming=javax.naming.spi.NamingManger" + }; + private static final int infoIdx = 1; + + public static void main(String[] args) throws Exception { + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + "mods/java.naming"); + + JarBuilder.build("hello", "Hello"); + String appJar = TestCommon.getTestJar("hello.jar"); + String appClasses[] = {"Hello"}; + for (int i = 0; i < jigsawOptions.length; i++) { + OutputAnalyzer output; + if (i == 5) { + // --patch-module + output = TestCommon.dump(appJar, appClasses, "-Xlog:cds,cds+hashtables", + jigsawOptions[i] + optionValues[i] + appJar); + } else { + output = TestCommon.dump(appJar, appClasses, "-Xlog:cds,cds+hashtables", + jigsawOptions[i], optionValues[i]); + } + if (i < infoIdx) { + output.shouldContain("Cannot use the following option " + + "when dumping the shared archive: " + jigsawOptions[i]) + .shouldHaveExitValue(1); + } else { + output.shouldContain("Info: the " + jigsawOptions[i] + + " option is ignored when dumping the shared archive"); + if (optionValues[i].equals("mymod")) { + // java will throw FindException for a module + // which cannot be found during init_phase2() of vm init + output.shouldHaveExitValue(1) + .shouldContain("java.lang.module.FindException: Module mymod not found"); + } else { + output.shouldHaveExitValue(0); + } + } + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java new file mode 100644 index 00000000000..2513d0e5a19 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test combinations of jigsaw options that affect the use of AppCDS + * + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib .. + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile ../test-classes/Hello.java ../test-classes/HelloMore.java + * @run main JigsawOptionsCombo + */ +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; + + +// Remaining WORK: TODO: +// 1. test with -m initial-module; waiting for changes from Chris will provide +// utils to build modules +// 2. Loading classes from Jmod files - waiting on utils +// 3. Loading classes from exploded module dir" + +public class JigsawOptionsCombo { + + public static void main(String[] args) throws Exception { + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + "mods/java.naming"); + + JarBuilder.build("hello", "Hello"); + JarBuilder.build("hello_more", "HelloMore"); + + (new JigsawOptionsCombo()).runTests(); + } + + + private ArrayList<TestCase> testCaseTable = new ArrayList<TestCase>(); + + public static String infoDuringDump(String option) { + return "Info: the " + option + + " option is ignored when dumping the shared archive"; + } + + public void runTests() throws Exception { + + testCaseTable.add(new TestCase( + "basic: Basic dump and execute, to verify the test plumbing works", + "", "", 0, + "", "", 0) ); + + String bcpArg = "-Xbootclasspath/a:" + + TestCommon.getTestJar("hello_more.jar"); + + testCaseTable.add(new TestCase( + "Xbootclasspath/a: is OK for both dump and run time", + bcpArg, "", 0, + bcpArg, "", 0) ); + + testCaseTable.add(new TestCase( + "module-path-01: --module-path is ignored for dump time", + "--module-path mods", + infoDuringDump("--module-path"), 0, + null, null, 0) ); + + testCaseTable.add(new TestCase( + "module-path-02: --module-path is ok for run time", + "", "", 0, + "--module-path mods", "", 0) ); + + testCaseTable.add(new TestCase( + "add-modules-01: --add-modules is ok at dump time", + "--add-modules java.management", + "", 0, + null, null, 0) ); + + testCaseTable.add(new TestCase( + "add-modules-02: --add-modules is ok at run time", + "", "", 0, + "--add-modules java.management", "", 0) ); + + testCaseTable.add(new TestCase( + "limit-modules-01: --limit-modules is ignored at dump time", + "--limit-modules java.base", + infoDuringDump("--limit-modules"), 0, + null, null, 0) ); + + testCaseTable.add(new TestCase( + "limit-modules-02: --limit-modules is ok at run time", + "", "", 0, + "--limit-modules java.base", "", 0) ); + + testCaseTable.add(new TestCase( + "upgrade-module-path-01: --upgrade-module-path is ignored at dump time", + "--upgrade-module-path mods", + infoDuringDump("--upgrade-module-path"), 0, + null, null, 0) ); + + testCaseTable.add(new TestCase( + "-upgrade-module-path-module-path-02: --upgrade-module-path is ok at run time", + "", "", 0, + "--upgrade-module-path mods", "", 0) ); + + for (TestCase tc : testCaseTable) tc.execute(); + } + + + // class representing a singe test case + public class TestCase { + String description; + String dumpTimeArgs; + String dumpTimeExpectedOutput; + int dumpTimeExpectedExitValue; + String runTimeArgs; + String runTimeExpectedOutput; + int runTimeExpectedExitValue; + + private String appJar = TestCommon.getTestJar("hello.jar"); + private String appClasses[] = {"Hello"}; + + + public TestCase(String description, + String dumpTimeArgs, String dumpTimeExpectedOutput, int dumpTimeExpectedExitValue, + String runTimeArgs, String runTimeExpectedOutput, int runTimeExpectedExitValue) { + + this.description = description; + this.dumpTimeArgs = dumpTimeArgs; + this.dumpTimeExpectedOutput = dumpTimeExpectedOutput; + this.dumpTimeExpectedExitValue = dumpTimeExpectedExitValue; + this.runTimeArgs = runTimeArgs; + this.runTimeExpectedOutput = runTimeExpectedOutput; + this.runTimeExpectedExitValue = runTimeExpectedExitValue; + } + + + public void execute() throws Exception { + System.out.println("Description: " + description); + + // ===== dump step - create the archive + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, appClasses, getDumpOptions()); + + if (dumpTimeExpectedExitValue == 0) { + TestCommon.checkDump(dumpOutput, dumpTimeExpectedOutput); + } else { + dumpOutput.shouldMatch(dumpTimeExpectedOutput); + dumpOutput.shouldHaveExitValue(dumpTimeExpectedExitValue); + } + + // ===== exec step - use the archive + if (runTimeArgs != null) { + OutputAnalyzer execOutput = TestCommon.exec(appJar, getRunOptions()); + + if (runTimeExpectedExitValue == 0) { + TestCommon.checkExec(execOutput, runTimeExpectedOutput, "Hello World"); + } else { + execOutput.shouldMatch(dumpTimeExpectedOutput); + execOutput.shouldHaveExitValue(dumpTimeExpectedExitValue); + } + } + } + + + // dump command line options can be separated by a space + private String[] getDumpOptions() { + return dumpTimeArgs.split(" "); + } + + + // run command line options can be separated by a space + private String[] getRunOptions() { + ArrayList<String> result = new ArrayList<>(); + + if (runTimeArgs != "") { + String splitArgs[] = runTimeArgs.split(" "); + for (String arg : splitArgs) + result.add(arg); + } + + result.add("Hello"); + return result.toArray(new String[1]); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java new file mode 100644 index 00000000000..07a09fda627 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary a test to demonstrate that an application class in the -cp + * will be archived although --patch-module is specified. The class in + * the -cp has no dependencies on the class in the --patch-module. + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main AppClassInCP + */ + +import java.io.File; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class AppClassInCP { + private static String moduleJar; + private static String appJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + String classDir = System.getProperty("test.classes"); + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + classDir); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + String source2 = "package mypackage; " + + "public class Hello { " + + " static { " + + " System.out.println(\"Hello!\"); " + + " } " + + "}"; + ClassFileInstaller.writeClassToDisk("mypackage/Hello", + InMemoryJavaCompiler.compile("mypackage.Hello", source2), + classDir); + + JarBuilder.build("hello", "mypackage/Hello"); + appJar = TestCommon.getTestJar("hello.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(appJar, + TestCommon.list("javax/naming/spi/NamingManager", "mypackage/Hello"), + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "PatchMain", "javax.naming.spi.NamingManager", "mypackage.Hello"); + TestCommon.checkDump(output, "Loading classes to share"); + + String classPath = appJar + File.pathSeparator + classDir; + System.out.println("classPath: " + classPath); + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "-cp", classPath, + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "PatchMain", "javax.naming.spi.NamingManager", "mypackage.Hello"); + TestCommon.checkExec(output, + "I pass!", + "Hello!", + "Hello source: shared objects file"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java new file mode 100644 index 00000000000..9f6fbbb2ad1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary if a class is defined to a package which is not defined to any + * module in the jimage, the class will not be found during dump + * time but it will be used during run time. + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main CustomPackage + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class CustomPackage { + private static String moduleJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.myspi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("javax/naming/myspi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.myspi.NamingManager", source, "--patch-module=java.naming"), + System.getProperty("test.classes")); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/myspi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(null, + TestCommon.list("javax/naming/myspi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.myspi.NamingManager"); + TestCommon.checkDump(output, "Preload Warning: Cannot find javax/naming/myspi/NamingManager"); + + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.myspi.NamingManager"); + TestCommon.checkExec(output, "I pass!"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java new file mode 100644 index 00000000000..b614f58a551 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary different settings of --patch-module at dump time and runtime are + * acceptable. The class found in runtime --patch-module entry should + * be used. + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main MismatchedPatchModule + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class MismatchedPatchModule { + private static String moduleJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + System.getProperty("test.classes")); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + // Case 1: --patch-module specified for dump time and run time + System.out.println("Case 1: --patch-module specified for dump time and run time"); + OutputAnalyzer output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + // javax.naming.spi.NamingManager is not patched at runtime + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming2=" + moduleJar, + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + output.shouldNotContain("I pass!"); + + // Case 2: --patch-module specified for dump time but not for run time + System.out.println("Case 2: --patch-module specified for dump time but not for run time"); + output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + // javax.naming.spi.NamingManager is not patched at runtime + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + output.shouldNotContain("I pass!"); + + // Case 3: --patch-module specified for run time but not for dump time + System.out.println("Case 3: --patch-module specified for run time but not for dump time"); + output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + // javax.naming.spi.NamingManager is patched at runtime + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkExec(output, "I pass!"); + + // Case 4: mismatched --patch-module entry counts between dump time and run time + System.out.println("Case 4: mismatched --patch-module entry counts between dump time and run time"); + output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + // javax.naming.spi.NamingManager is patched at runtime + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming=" + moduleJar, + "--patch-module=java.naming2=" + moduleJar, + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkExec(output, "I pass!"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java new file mode 100644 index 00000000000..854d4419922 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary a simple test to ensure that a directory in the --patch-module + * option does not affect dump process + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main PatchDir + */ + +import java.io.File; +import jdk.test.lib.compiler.InMemoryJavaCompiler; + +public class PatchDir { + private static String moduleJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + String classDir = System.getProperty("test.classes"); + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + classDir); + + JarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + System.out.println("Test dumping with --patch-module"); + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "PatchMain", "javax.naming.spi.NamingManager") + .shouldContain("Loading classes to share") + .shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java new file mode 100644 index 00000000000..726cf1b7675 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary sharing is disabled if java.base is patch at runtime + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main PatchJavaBase + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class PatchJavaBase { + private static String moduleJar; + + public static void main(String args[]) throws Throwable { + + String source = "package java.lang; " + + "public class NewClass { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("java/lang/NewClass", + InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"), + System.getProperty("test.classes")); + + JarBuilder.build("javabase", "java/lang/NewClass"); + moduleJar = TestCommon.getTestJar("javabase.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(null, null, + "--patch-module=java.base=" + moduleJar, + "PatchMain", "java.lang.NewClass"); + TestCommon.checkDump(output, "Loading classes to share"); + + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.base=" + moduleJar, + "PatchMain", "java.lang.NewClass"); + output.shouldContain("CDS is disabled when java.base module is patched"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java new file mode 100644 index 00000000000..86430957875 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +// This loads the class affected by the --patch-module option. For the test to pass +// it must load the class from the --patch-module directory, not the jimage file. +public class PatchMain { + public static void main(String[] args) throws Exception { + for (int i = 0; i < args.length; i++) { + Class.forName(args[i]); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java new file mode 100644 index 00000000000..80959541cfd --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary a simple test to ensure that class is loaded from jar file in --patch-module at runtime + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main Simple + */ + +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class Simple { + private static String moduleJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + System.getProperty("test.classes")); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkExec(output, "I pass!"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java new file mode 100644 index 00000000000..de460b5f2ee --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary the class in the -cp is a subclass of the class in --patch-module. The + * patched class should be used at runtime. + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main SubClassOfPatchedClass + */ + +import java.io.File; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class SubClassOfPatchedClass { + private static String moduleJar; + private static String appJar; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming; " + + "public class Reference { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + String classDir = System.getProperty("test.classes"); + + ClassFileInstaller.writeClassToDisk("javax/naming/Reference", + InMemoryJavaCompiler.compile("javax.naming.Reference", source, "--patch-module=java.naming"), + classDir); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/Reference"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + String source2 = "package mypackage; " + + "public class MyReference extends javax.naming.Reference { " + + " static { " + + " System.out.println(\"MyReference!\"); " + + " } " + + " public MyReference(String mystring) { " + + " super(mystring); " + + " } " + + "}"; + ClassFileInstaller.writeClassToDisk("mypackage/MyReference", + InMemoryJavaCompiler.compile("mypackage.MyReference", source2), + classDir); + + JarBuilder.build("myjavanaming", "mypackage/MyReference"); + appJar = TestCommon.getTestJar("myjavanaming.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(appJar, + TestCommon.list("javax/naming/Reference", "mypackage/MyReference"), + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "PatchMain", "javax.naming.Reference", "mypackage.MyReference"); + TestCommon.checkDump(output, "Loading classes to share"); + + String classPath = appJar + File.pathSeparator + classDir; + System.out.println("classPath: " + classPath); + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "-cp", classPath, + "--patch-module=java.naming=" + moduleJar, + "-Xlog:class+load", + "PatchMain", "javax.naming.Reference", "mypackage.MyReference"); + TestCommon.checkExec(output, + "I pass!", + "MyReference source: file:"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java new file mode 100644 index 00000000000..ef8b71a2165 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @summary a patched class found in --patch-module should be used at runtime + * @library ../.. + * @library /test/hotspot/jtreg/testlibrary + * @library /test/lib + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * @build PatchMain + * @run main TwoJars + */ + +import java.io.File; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; + +public class TwoJars { + private static String moduleJar; + private static String moduleJar2; + + public static void main(String args[]) throws Throwable { + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming.jar file. + String source = "package javax.naming.spi; " + + "public class NamingManager { " + + " static { " + + " System.out.println(\"I pass!\"); " + + " } " + + "}"; + + // Create a class file in the module java.naming. This class file + // will be put in the javanaming2.jar file. + String source2 = "package javax.naming.spi; " + + "public class DirectoryManager { " + + " static { " + + " System.out.println(\"I fail!\"); " + + " } " + + "}"; + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager", + InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"), + System.getProperty("test.classes")); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming", "javax/naming/spi/NamingManager"); + moduleJar = TestCommon.getTestJar("javanaming.jar"); + + ClassFileInstaller.writeClassToDisk("javax/naming/spi/DirectoryManager", + InMemoryJavaCompiler.compile("javax.naming.spi.DirectoryManager", source2, "--patch-module=java.naming"), + System.getProperty("test.classes")); + + // Build the jar file that will be used for the module "java.naming". + JarBuilder.build("javanaming2", "javax/naming/spi/DirectoryManager"); + moduleJar2 = TestCommon.getTestJar("javanaming2.jar"); + + System.out.println("Test dumping with --patch-module"); + OutputAnalyzer output = + TestCommon.dump(null, + TestCommon.list("javax/naming/spi/NamingManager"), + "--patch-module=java.naming=" + moduleJar2 + File.pathSeparator + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkDump(output, "Loading classes to share"); + + output = TestCommon.execCommon( + "-XX:+UnlockDiagnosticVMOptions", + "--patch-module=java.naming=" + moduleJar2 + File.pathSeparator + moduleJar, + "-Xlog:class+load", + "-Xlog:class+path=info", + "PatchMain", "javax.naming.spi.NamingManager"); + TestCommon.checkExec(output, "I pass"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java new file mode 100644 index 00000000000..71392801fda --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary AppCDS tests for testing -Xbootclasspath/a + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile src/jdk/test/Main.java + * @compile src/com/sun/tools/javac/Main2.jasm + * @compile src/sun/nio/cs/ext/MyClass.java + * @compile src/sun/nio/cs/ext1/MyClass.java + * @run main BootAppendTests + */ + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public class BootAppendTests { + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path CLASSES_DIR = Paths.get("classes"); + + private static final String MAIN_CLASS = "jdk.test.Main"; + private static final String APP_MODULE_CLASS = "com/sun/tools/javac/Main2"; + private static final String BOOT_APPEND_MODULE_CLASS = "sun/nio/cs/ext/MyClass"; + private static final String BOOT_APPEND_CLASS = "sun/nio/cs/ext1/MyClass"; + private static final String[] ARCHIVE_CLASSES = + {APP_MODULE_CLASS, BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_CLASS}; + + private static String appJar; + private static String bootAppendJar; + private static String testArchiveName; + + public static void main(String... args) throws Exception { + dumpArchive(); + + System.out.println("TESTCASE: 1: testBootAppendModuleClassWithoutAppCDS"); + testBootAppendModuleClassWithoutAppCDS(); + + System.out.println("TESTCASE: 2" ); + testBootAppendModuleClassWithAppCDS(); + + System.out.println("TESTCASE: 3" ); + testBootAppendExcludedModuleClassWithoutAppCDS(); + + System.out.println("TESTCASE: 4" ); + testBootAppendExcludedModuleClassWithAppCDS(); + + System.out.println("TESTCASE: 5" ); + testBootAppendClassWithoutAppCDS(); + + System.out.println("TESTCASE: 6" ); + testBootAppendClassWithAppCDS(); + + System.out.println("TESTCASE: 7" ); + testBootAppendAppModuleClassWithoutAppCDS(); + + System.out.println("TESTCASE: 9" ); + testBootAppendAppModuleClassWithAppCDS(); + + System.out.println("TESTCASE: 9" ); + testBootAppendAppExcludeModuleClassWithoutAppCDS(); + + System.out.println("TESTCASE: 10" ); + testBootAppendAppExcludeModuleClassAppCDS(); + } + + static void dumpArchive() throws Exception { + JarBuilder.build("classpathtests", "jdk/test/Main"); + appJar = TestCommon.getTestJar("classpathtests.jar"); + + JarBuilder.build("bootAppend", + APP_MODULE_CLASS, BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_CLASS); + bootAppendJar = TestCommon.getTestJar("bootAppend.jar"); + + OutputAnalyzer output1 = TestCommon.dump( + appJar, TestCommon.list(ARCHIVE_CLASSES), "-Xbootclasspath/a:" + bootAppendJar); + TestCommon.checkDump(output1); + + if (!TestCommon.isUnableToMap(output1)) { + // Make sure all the classes were successfully archived. + for (String archiveClass : ARCHIVE_CLASSES) { + output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass); + } + } + + testArchiveName = TestCommon.getCurrentArchiveName(); + } + + // Test #1: A class in package defined in boot module + // - should not be loaded from the -Xbootclasspath/a without AppCDS + public static void testBootAppendModuleClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar) + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #1", BOOT_APPEND_MODULE_CLASS, "false"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // Test #2: A shared class in package defined in boot module that's archived + // from -Xbootclasspath/a + // - should not be loaded by AppCDS + public static void testBootAppendModuleClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "-Xbootclasspath/a:" + bootAppendJar, + MAIN_CLASS, + "Test #2", BOOT_APPEND_MODULE_CLASS, "false"); + TestCommon.checkExec(output); + } + + + // Test #3: A class in excluded package defined in boot module + // - should be loaded from the -Xbootclasspath/a by the boot classloader + public static void testBootAppendExcludedModuleClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar, + "--limit-modules", "java.base") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #3", BOOT_APPEND_MODULE_CLASS, "true", "BOOT"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // Test #4: A shared class in excluded package that's archived from + // -Xbootclasspath/a + // - should be loaded from the archive by the bootstrap classloader + public static void testBootAppendExcludedModuleClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "-Xbootclasspath/a:" + bootAppendJar, + "--limit-modules", "java.base", + "-XX:+TraceClassLoading", + MAIN_CLASS, + "Test #4", BOOT_APPEND_MODULE_CLASS, "true", "BOOT"); + TestCommon.checkExec(output); + if (!TestCommon.isUnableToMap(output)) + output.shouldContain("[class,load] sun.nio.cs.ext.MyClass source: shared objects file"); + } + + + // Test #5: A class not in package defined in boot module + // - should be loaded from the -Xbootclasspath/a without AppCDS + public static void testBootAppendClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar) + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #5", BOOT_APPEND_CLASS, "true", "BOOT"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + + // Test #6: A shared class not in package defined in boot module that's + // archived from -Xbootclasspath/a + // - should be loaded from the archive by the bootstrap class loader + public static void testBootAppendClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "-Xbootclasspath/a:" + bootAppendJar, + "-XX:+TraceClassLoading", + MAIN_CLASS, + "Test #6", BOOT_APPEND_CLASS, "true", "BOOT"); + TestCommon.checkExec(output); + if (!TestCommon.isUnableToMap(output)) + output.shouldContain("[class,load] sun.nio.cs.ext1.MyClass source: shared objects file"); + } + + + // Test #7: A class in package defined in jimage app module + // - should not be loaded from the -Xbootclasspath/a without AppCDS + public static void testBootAppendAppModuleClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar) + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #7", APP_MODULE_CLASS, "false"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + + // Test #8: A shared class in package defined in jimage app module that's + // archived from -Xbootclasspath/a + // - should not be loaded from the archive + public static void testBootAppendAppModuleClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "-Xbootclasspath/a:" + bootAppendJar, + MAIN_CLASS, + "Test #8", APP_MODULE_CLASS, "false"); + TestCommon.checkExec(output); + } + + + // Test #9: A class in excluded package defined in jimage app module + // - should be loaded from the -Xbootclasspath/a without AppCDS + public static void testBootAppendAppExcludeModuleClassWithoutAppCDS() + throws Exception { + + CDSOptions opts = (new CDSOptions()) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar, + "--limit-modules", "java.base") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #9", APP_MODULE_CLASS, "true", "BOOT"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // Test #10: A shared class in excluded package defined in jimage app module + // - should be loaded from the -Xbootclasspath/a with AppCDS + public static void testBootAppendAppExcludeModuleClassAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "-Xbootclasspath/a:" + bootAppendJar, + "-XX:+TraceClassLoading", + "--limit-modules", "java.base", + MAIN_CLASS, + "Test #10", APP_MODULE_CLASS, "true", "BOOT"); + TestCommon.checkExec(output); + + if (!TestCommon.isUnableToMap(output)) + output.shouldContain("[class,load] com.sun.tools.javac.Main2 source: shared objects file"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java new file mode 100644 index 00000000000..6f929ab227f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library ../.. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * @compile src/jdk/test/Main.java + * @compile src/com/sun/tools/javac/Main.jasm + * @compile src/com/sun/tools/javac/Main2.jasm + * @compile src/javax/activation/UnsupportedDataTypeException2.jasm + * @run main ClassPathTests + * @summary AppCDS tests for testing classpath/package conflicts + */ + +/* + * These tests will verify that AppCDS will correctly handle archived classes + * on the classpath that are in a package that is also exported by the jimage. + * These classes should fail to load unless --limit-modules is used to hide the + * package exported by the jimage. There are 8 variants of this test: + * - With a jimage app package and with a jimage ext package + * - With --limit-modules and without --limit-modules + * - With AppCDS and without AppCDS (to verify behaviour is the same for both). + * + * There is also a 9th test to verify that when --limit-modules is used, a jimage + * class in the archive can be replaced by a classpath class with the + * same name and package. + */ + +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.Asserts; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + + +public class ClassPathTests { + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path CLASSES_DIR = Paths.get("classes"); + + // the test module + private static final String MAIN_CLASS = "jdk.test.Main"; + private static final String LIMITMODS_MAIN_CLASS = "jdk.test.LimitModsMain"; + + // test classes to archive. These are both in UPGRADED_MODULES + private static final String JIMAGE_CLASS = "com/sun/tools/javac/Main"; + private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main2"; + private static final String PLATFORM_ARCHIVE_CLASS = "javax/activation/UnsupportedDataTypeException2"; + private static final String[] ARCHIVE_CLASSES = {APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, JIMAGE_CLASS}; + private static final int NUMBER_OF_TEST_CASES = 10; + + private static String appJar; + private static String testArchiveName; + + + public static void main(String[] args) throws Exception { + ClassPathTests tests = new ClassPathTests(); + tests.dumpArchive(); + + Method[] methods = tests.getClass().getDeclaredMethods(); + int numOfTestMethodsRun = 0; + for (Method m : methods) { + if (m.getName().startsWith("test")) { + System.out.println("About to run test method: " + m.getName()); + m.invoke(tests); + numOfTestMethodsRun++; + } + } + + Asserts.assertTrue((numOfTestMethodsRun == NUMBER_OF_TEST_CASES), + "Expected " + NUMBER_OF_TEST_CASES + " test methods to run, actual number is " + + numOfTestMethodsRun); + } + + private void dumpArchive() throws Exception { + // Create a jar file with all the classes related to this test. + JarBuilder.build( "classpathtests", + APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, JIMAGE_CLASS, + "jdk/test/Main"); + appJar = TestCommon.getTestJar("classpathtests.jar"); + + // dump the archive with altnernate jdk.comiler and jdk.activation classes in the class list + OutputAnalyzer output1 = TestCommon.dump(appJar, TestCommon.list(ARCHIVE_CLASSES)); + TestCommon.checkDump(output1); + // Only a class that belongs to a module which is not defined by default + // can be found. In this case the PLATFORM_ARCHIVE_CLASS belongs + // to the java.activation which is not defined by default; it is the only + // class can be found during dumping. + for (String archiveClass : ARCHIVE_CLASSES) { + if (archiveClass.equals(PLATFORM_ARCHIVE_CLASS)) { + output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass); + } else { + output1.shouldContain("Preload Warning: Cannot find " + archiveClass); + } + } + + testArchiveName = TestCommon.getCurrentArchiveName(); + } + + // #1: Archived classpath class in same package as jimage app class. With AppCDS. + // Should fail to load. + public void testAppClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, MAIN_CLASS, + "Test #1", APP_ARCHIVE_CLASS, "false"); // last 3 args passed to test + TestCommon.checkExec(output); + } + + // #2: Archived classpath class in same package as jimage app class. Without AppCDS. + // Should fail to load. + public void testAppClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar) + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #2", APP_ARCHIVE_CLASS, "false"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // For tests #3 and #4, we need to "--add-modules java.activation" since the + // java.activation module won't be defined by default. + + // #3: Archived classpath class in same package as jimage ext class. With AppCDS. + // Should fail to load. + public void testExtClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, "--add-modules", "java.activation", MAIN_CLASS, + "Test #3", PLATFORM_ARCHIVE_CLASS, "false"); // last 3 args passed to test + TestCommon.checkExec(output); + } + + // #4: Archived classpath class in same package as jimage ext class. Without AppCDS. + // Should fail to load. + public void testExtClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar, "--add-modules", "java.activation") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #4", PLATFORM_ARCHIVE_CLASS, "false"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // #5: Archived classpath class in same package as jimage app class. With AppCDS. + // Should load because --limit-modules is used. + public void testAppClassWithLimitModsWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "--limit-modules", "java.base", + MAIN_CLASS, + "Test #5", APP_ARCHIVE_CLASS, "true"); // last 3 args passed to test + TestCommon.checkExec(output); + } + + // #6: Archived classpath class in same package as jimage app class. Without AppCDS. + // Should load because --limit-modules is used. + public void testAppClassWithLimitModsWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar, "--limit-modules", "java.base") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #6", APP_ARCHIVE_CLASS, "true"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // #7: Archived classpath class in same package as jimage ext class. With AppCDS. + // Should load because --limit-modules is used. + public void testExtClassWithLimitModsWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "--limit-modules", "java.base", + MAIN_CLASS, + "Test #7", PLATFORM_ARCHIVE_CLASS, "true"); // last 3 args passed to test + TestCommon.checkExec(output); + } + + // #8: Archived classpath class in same package as jimage ext class. Without AppCDS. + // Should load because --limit-modules is used. + public void testExtClassWithLimitModsWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar, "--limit-modules", "java.base") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #8", PLATFORM_ARCHIVE_CLASS, "true"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + + // #9: Archived classpath class with same name as jimage app class. With AppCDS. + // Should load because --limit-modules is used. + public void testReplacingJImageClassWithAppCDS() throws Exception { + OutputAnalyzer output = TestCommon.exec( + appJar, + "--limit-modules", "java.base", "-XX:+TraceClassLoading", + MAIN_CLASS, + "Test #9", JIMAGE_CLASS, "true"); // last 3 args passed to test + TestCommon.checkExec(output); + } + + // #10: Archived classpath class with same name as jimage app class. Without AppCDS. + // Should load because --limit-modules is used. Note the archive will actually contain + // the original jimage version of the class, but AppCDS should refuse to load it + // since --limit-modules is used. This should result in the -cp version being used. + public void testReplacingJImageClassWithoutAppCDS() throws Exception { + CDSOptions opts = (new CDSOptions()) + .addPrefix("-cp", appJar, "--limit-modules", "java.base") + .setArchiveName(testArchiveName) + .addSuffix(MAIN_CLASS, "Test #10", JIMAGE_CLASS, "true"); + + CDSTestUtils.runWithArchiveAndCheck(opts); + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java new file mode 100644 index 00000000000..05311e818a1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Ensure that classes found in jimage takes precedence over classes found in -Xbootclasspath/a. + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.activation + * jdk.jartool/sun.tools.jar + * @compile ../../test-classes/DummyClassHelper.java + * @compile ../../test-classes/java/net/HttpCookie.jasm + * @compile ../../test-classes/javax/activation/MimeType.jasm + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main DummyClassesInBootClassPath + */ + +import java.io.File; +import java.util.List; +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; + +public class DummyClassesInBootClassPath { + private static final String METHOD_NAME = "thisClassIsDummy()"; + + public static void main(String[] args) throws Exception { + String classNames[] = { "java/net/HttpCookie", + "javax/activation/MimeType"}; + JarBuilder.build("dummyClasses", classNames[0], classNames[1]); + + String appJar = TestCommon.getTestJar("dummyClasses.jar"); + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, classNames, "-Xbootclasspath/a:" + appJar); + + List<String> argsList = new ArrayList<String>(); + for (int i = 0; i < classNames.length; i++) { + argsList.add(classNames[i].replace('/', '.')); + } + String[] arguments = new String[argsList.size()]; + arguments = argsList.toArray(arguments); + OutputAnalyzer execOutput = TestCommon.execCommon( + "-cp", TestCommon.getTestDir("."), "-verbose:class", + "--add-modules", "java.activation", + "-Xbootclasspath/a:" + appJar, "DummyClassHelper", + arguments[0], arguments[1]); + for (int i = 0; i < arguments.length; i++) { + TestCommon.checkExec(execOutput, + "java.lang.NoSuchMethodException: " + arguments[i] + "." + + METHOD_NAME); + } + + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar"); + String bootClassPath = "-Xbootclasspath/a:" + appJar + + File.pathSeparator + whiteBoxJar; + argsList.add("testWithWhiteBox"); + arguments = new String[argsList.size()]; + arguments = argsList.toArray(arguments); + String[] opts = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + bootClassPath, "-XX:+TraceClassPaths", "DummyClassHelper", + arguments[0], arguments[1], arguments[2]}; + OutputAnalyzer output = TestCommon.execCommon(opts); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java new file mode 100644 index 00000000000..a635603473e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test a few scenarios if an empty class, which has the same name as the one in the jimage, is specified in the -Xbootclasspath/a + * 1) boot loader will always load the class from the bootclasspath + * 2) app loader will load the class from the jimage by default; + * app loader will load the class from the bootclasspath if the + * "--limit-modules java.base" option is specified + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile ../../test-classes/EmptyClassHelper.java + * @compile ../../test-classes/com/sun/tools/javac/Main.jasm + * @run main EmptyClassInBootClassPath + */ + +import java.io.File; +import java.lang.*; +import java.lang.reflect.*; +import java.util.List; +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; + +public class EmptyClassInBootClassPath { + static final String EXPECTED_EXCEPTION = + "java.lang.NoSuchMethodException: com.sun.tools.javac.Main.main([Ljava.lang.String;)"; + public static void main(String[] args) throws Exception { + String[] className = {"com/sun/tools/javac/Main"}; + JarBuilder.build("emptyClass", className); + String appJar = TestCommon.getTestJar("emptyClass.jar"); + JarBuilder.build("EmptyClassHelper", "EmptyClassHelper"); + String helperJar = TestCommon.getTestJar("EmptyClassHelper.jar"); + OutputAnalyzer dumpOutput = TestCommon.dump( + appJar, className, "-Xbootclasspath/a:" + appJar); + TestCommon.checkDump(dumpOutput); + dumpOutput.shouldNotContain("Preload Warning: skipping class from -Xbootclasspath/a " + className[0]); + + String bootclasspath = "-Xbootclasspath/a:" + appJar; + String classPath = "-Djava.class.path=" + appJar + File.pathSeparator + helperJar; + List<String> argsList = new ArrayList<String>(); + argsList.add(classPath); + argsList.add(bootclasspath); + argsList.add("--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED"); + argsList.add("EmptyClassHelper"); + + // case 1: load class in bootclasspath using app loader + argsList.add("useAppLoader"); + String[] opts = new String[argsList.size()]; + opts = argsList.toArray(opts); + OutputAnalyzer runOutput = TestCommon.execCommon(opts); + TestCommon.checkExec(runOutput, "appLoader found method main"); + + // case 2: load class in bootclasspath using boot loader + argsList.remove(argsList.size() - 1); + argsList.add("useBootLoader"); + opts = new String[argsList.size()]; + opts = argsList.toArray(opts); + runOutput = TestCommon.execCommon(opts); + TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION); + + // case 3: load class in bootclasspath using app loader with '--limit-modules java.base' + argsList.add(0, "--limit-modules"); + argsList.add(1, "java.base"); + argsList.remove(argsList.size() - 1); + argsList.add("useAppLoader"); + opts = new String[argsList.size()]; + opts = argsList.toArray(opts); + runOutput = TestCommon.execCommon(opts); + TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION); + + // case 4: load class in bootclasspath using boot loader with '--limit-modules java.base' + argsList.remove(argsList.size() - 1); + argsList.add("useBootLoader"); + opts = new String[argsList.size()]; + opts = argsList.toArray(opts); + runOutput = TestCommon.execCommon(opts); + TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION); + + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm new file mode 100644 index 00000000000..efe6c4a8e6e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package com/sun/tools/javac; + +public class Main + version 51:0 +{ + +public Method "<init>":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; +} + +public Method toString:"()Ljava/lang/String;" + stack 1 locals 1 +{ + ldc String "hi"; + areturn; +} + +} // end class Main diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm new file mode 100644 index 00000000000..3d0f43256bf --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package com/sun/tools/javac; + +public class Main2 + version 51:0 +{ + +public Method "<init>":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; +} + +public Method toString:"()Ljava/lang/String;" + stack 1 locals 1 +{ + ldc String "hi"; + areturn; +} + +} // end class Main2 diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm new file mode 100644 index 00000000000..3a30e511397 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package javax/activation; + +public class UnsupportedDataTypeException2 + version 51:0 +{ + +public Method "<init>":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; +} + +public Method toString:"()Ljava/lang/String;" + stack 1 locals 1 +{ + ldc String "hi"; + areturn; +} + +} // end class UnsupportedDataTypeException2 diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java new file mode 100644 index 00000000000..752fd9b7baf --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +/** + * Tests loading an archived class that has the same class name as one in the + * jimage. The class should normally fail to load since a classpath class is not + * allowed to have the same package name as a module in the jimage. However, + * if --limit-modules was used then archived class should be loaded. + */ + +package jdk.test; + +public class Main { + static final ClassLoader BOOT_LOADER = null; + static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader(); + static final ClassLoader SYS_LOADER = ClassLoader.getSystemClassLoader(); + + public static void main(String[] args) throws Exception { + boolean shouldLoad = false; + ClassLoader expectedLoader = SYS_LOADER; + + /* + * 3 Arguments are passed to this test: + * 1. testName: Name of the test being run. + * 2. className: Name of the class to load and instantiate. + * 3. shouldLoad: Either "true" or "false" to indicate whether the class should + * successfully load ("true" indicates --limit-modules was used.) + * The 4th argument is optional. It specifies the classloader. + */ + + assertTrue(args.length <= 4); + String testName = args[0]; + String className = args[1].replace('/', '.'); + String shouldLoadName = args[2]; // "true" or "false" + String loaderName = "SYS"; + if (args.length == 4) { + loaderName = args[3]; + } + + if (shouldLoadName.equals("true")) { + shouldLoad = true; + } else if (shouldLoadName.equals("false")) { + shouldLoad = false; + } else { + assertTrue(false); + } + + if (loaderName.equals("SYS")) { + expectedLoader = SYS_LOADER; + } else if (loaderName.equals("EXT")) { + expectedLoader = PLATFORM_LOADER; + } else if (loaderName.equals("BOOT")) { + expectedLoader = BOOT_LOADER; + } + + System.out.println(testName + ": class=" + className + " shouldLoad=" + + shouldLoadName + " by loader:" + expectedLoader); + + // Try to load the specified class with the default ClassLoader. + Class<?> clazz = null; + try { + clazz = Class.forName(className); + } catch (ClassNotFoundException e) { + System.out.println(e); + } + + if (clazz != null) { + // class loaded + if (shouldLoad) { + // Make sure we got the expected defining ClassLoader + ClassLoader actualLoader = clazz.getClassLoader(); + if (actualLoader != expectedLoader) { + throw new RuntimeException(testName + " FAILED: " + clazz + " loaded by " + actualLoader + + ", expected " + expectedLoader); + } + // Make sure we got the right version of the class. toString() of an instance + // of the overridden version of the class should return "hi". + String s = clazz.newInstance().toString(); + if (!s.equals("hi")) { + throw new RuntimeException(testName + " FAILED: toString() returned \"" + s + + "\" instead of \"hi\"" ); + } + System.out.println(testName + " PASSED: class loaded as expected."); + } else { + throw new RuntimeException(testName + " FAILED: class loaded, but should have failed to load."); + } + } else { + // class did not load + if (shouldLoad) { + throw new RuntimeException(testName + " FAILED: class failed to load."); + } else { + System.out.println(testName + " PASSED: ClassNotFoundException thrown as expected"); + } + } + } + + static void assertTrue(boolean expr) { + if (!expr) + throw new RuntimeException("assertion failed"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java new file mode 100644 index 00000000000..92e9204dce2 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +package sun.nio.cs.ext; + +public class MyClass { + public String toString() { + return "hi"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java new file mode 100644 index 00000000000..43b24f598ee --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +package sun.nio.cs.ext1; + +public class MyClass { + public String toString() { + return "hi"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java new file mode 100644 index 00000000000..cb30412bae4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +/** + * Used with -p or --upgrade-module-path to exercise the replacement + * of classes in modules that are linked into the runtime image. + */ + +import java.lang.*; +import java.lang.reflect.*; +import sun.hotspot.WhiteBox; + + +public class LimitModsHelper { + static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader(); + static final ClassLoader SYS_LOADER = ClassLoader.getSystemClassLoader(); + + public static void main(String[] args) throws Exception { + assertTrue(args.length == 4); + String[] classNames = new String[3]; + for (int i = 0; i < 3; i++) { + classNames[i] = args[i].replace('/', '.'); + } + int excludeModIdx = Integer.parseInt(args[3]); + + ClassLoader expectedLoaders[] = {null, PLATFORM_LOADER, SYS_LOADER}; + + WhiteBox wb = WhiteBox.getWhiteBox(); + + Class<?> clazz = null; + for (int i = 0; i < 3; i++) { + try { + // Load the class with the default ClassLoader. + clazz = Class.forName(classNames[i]); + } catch (Exception e) { + if (i == excludeModIdx) { + System.out.println(classNames[i] + " not found as expected because the module isn't in the --limit-modules - PASSED"); + } else { + throw(e); + } + } + + if (clazz != null && i != excludeModIdx) { + // Make sure we got the expected defining ClassLoader + testLoader(clazz, expectedLoaders[i]); + + // Make sure the class is in the shared space + if (!wb.isSharedClass(clazz)) { + throw new RuntimeException(clazz.getName() + + ".class should be in the shared space. " + + "loader=" + clazz.getClassLoader() + " module=" + clazz.getModule().getName()); + } + } + clazz = null; + } + } + + /** + * Asserts that given class has the expected defining loader. + */ + static void testLoader(Class<?> clazz, ClassLoader expected) { + ClassLoader loader = clazz.getClassLoader(); + if (loader != expected) { + throw new RuntimeException(clazz + " loaded by " + loader + ", expected " + expected); + } + } + + static void assertTrue(boolean expr) { + if (!expr) + throw new RuntimeException("assertion failed"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java new file mode 100644 index 00000000000..434ddfcfae6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library ../.. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules jdk.jartool/sun.tools.jar + * jdk.internal.jvmstat/sun.jvmstat.monitor + * @compile LimitModsHelper.java + * @compile ../../test-classes/java/net/HttpCookie.jasm + * @compile ../../test-classes/jdk/dynalink/DynamicLinker.jasm + * @compile ../../test-classes/com/sun/tools/javac/Main.jasm + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main LimitModsTests + * @summary AppCDS tests for excluding class in module by using --limit-modules. + */ + +/** + * This is for testing the --limit-modules option with AppCDS. + * This test assumes the following defining class loader, module, class relations: + * class loader module class + * ----------------------------------------------------- + * boot java.base java/net/HttpCookie + * platform jdk.dynalink jdk/dynalink/DynamicLinker + * app jdk.compiler com/sun/tools/javac/Main + * + * This test dumps the above 3 classes into a shared archive. + * Then it will run the following 4 -limit-modules scenarios: + * 1. without --limit-modules + * All 3 classes should be loaded successfully. + * All 3 classes should be loaded by the appropriate class loader. + * All 3 classes should be found in the shared archive. + * 2. --limit-modules java.base,jdk.dynalink + * The loading of the com/sun/tools/javac/Main class should fail. + * The other 2 classes should be loaded successfully and by the appropriate class loader. + * The other 2 classes should be found in the shared archive. + * 3. --limit-modules java.base,jdk.compiler + * The loading of the jdk/nio/dynalink/DynamicLinker class should fail. + * The other 2 classes should be loaded successfully and by the appropriate class loader. + * The other 2 classes should be found in the shared archive. + * 4. --limit-modules jdk.dynalink,jdk.compiler + * The java.base module can't be excluded. + * The results for this case is the same as for case #1. + */ + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + + +public class LimitModsTests { + + // the module that is limited + private static final String[] LIMIT_MODULES = {"java.base", "jdk.dynalink", "jdk.compiler"}; + + // test classes to archive. + private static final String BOOT_ARCHIVE_CLASS = "java/net/HttpCookie"; + private static final String PLATFORM_ARCHIVE_CLASS = "jdk/dynalink/DynamicLinker"; + private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main"; + private static final String[] ARCHIVE_CLASSES = { + BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS}; + private String bootClassPath = null; + private String whiteBoxJar = null; + private String helperJar = null; + private String appJar = null; + private OutputAnalyzer output = null; + + public static void main(String[] args) throws Exception { + LimitModsTests tests = new LimitModsTests(); + tests.dumpArchive(); + tests.runTestNoLimitMods(); + tests.runTestLimitMods(); + } + + void dumpArchive() throws Exception { + JarBuilder.build("limitModsTest", BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS); + JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox"); + JarBuilder.build("limitModsHelper", "LimitModsHelper"); + + appJar = TestCommon.getTestJar("limitModsTest.jar"); + whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar"); + helperJar = TestCommon.getTestJar("limitModsHelper.jar"); + bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar; + // Dump the test classes into the archive + OutputAnalyzer output1 = TestCommon.dump(appJar, TestCommon.list(ARCHIVE_CLASSES), bootClassPath); + TestCommon.checkDump(output1); + // Make sure all the classes where successfully archived. + for (String archiveClass : ARCHIVE_CLASSES) { + output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass); + } + } + + // run the test without --limit-modules + public void runTestNoLimitMods() throws Exception { + output = TestCommon.exec( + appJar + File.pathSeparator + helperJar, + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootClassPath, + "LimitModsHelper", + BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS, "-1"); // last 4 args passed to test + TestCommon.checkExec(output); + } + + // run the test with --limit-modules + // + // --limit-modules jdk.dynalink,jdk.compiler + // It seems we can't exclude the java.base module. For this case, + // although the java.base module isn't in --limit-modules, the class + // in the java.base module (java.net.HttpCookie) can also be found. + // + // --limit-modules java.base,jdk.dynalink + // --limit-modules java.base,jdk.compiler + public void runTestLimitMods() throws Exception { + String limitMods = null; + for (int excludeModIdx = 0; excludeModIdx < 3; excludeModIdx++) { + for (int includeModIdx = 0; includeModIdx < 3; includeModIdx++) { + if (includeModIdx != excludeModIdx) { + if (limitMods != null) { + limitMods += ","; + limitMods += LIMIT_MODULES[includeModIdx]; + } else { + limitMods = LIMIT_MODULES[includeModIdx]; + } + } + } + output = TestCommon.exec( + appJar + File.pathSeparator + helperJar, + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootClassPath, + "--limit-modules", limitMods, + "LimitModsHelper", + BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS, + Integer.toString(excludeModIdx)); // last 4 args passed to test + TestCommon.checkExec(output); + limitMods = null; + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java new file mode 100644 index 00000000000..42355b73ec2 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @modules java.base/jdk.internal.misc + * @library ../.. + * @library /test/lib + * @run main OverrideTests + * @summary AppCDS tests for overriding archived classes with -p and --upgrade-module-path + */ + +/* + * This test consists of 4 tests: + * 1. Archive PLATFORM class and override with --upgrade-module-path. + * 2. Archive PLATFORM class and override with -p. + * 3. Archive APP class and override with --upgrade-module-path. + * 4. Archive App class and override with -p. + * For all 4 tests, the class is instantiatied and toString() is called + * to check whether the archived version or the override version was instantiatied. + * For tests 1 and 3, the overridden version should be instantiatied. + * For tests 2 and 4, the archived version should be instantiated. + * + * This test uses the same test helper class in all 4 cases. It is located in + * src/test/jdk/test/Main.java. It will be invoked once for each test cases, + * with parameters to the test determining how it is run and what the + * expected result is. See Main.java for a description of these 3 arguments. + */ + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.Asserts; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + + +public class OverrideTests { + private static final String TEST_SRC = System.getProperty("test.src"); + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get("mods"); + + // the module that is upgraded + private static final String[] UPGRADED_MODULES = {"jdk.compiler", "java.activation"}; + private static final Path[] UPGRADEDMODS_DIR = {Paths.get("upgradedmod1"), Paths.get("upgradedmod2")}; + + // the test module + private static final String TEST_MODULE = "test"; + private static final String MAIN_CLASS = "jdk.test.Main"; + + // test classes to archive. These are both in UPGRADED_MODULES + private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main"; + private static final String PLATFORM_ARCHIVE_CLASS = "javax/activation/UnsupportedDataTypeException"; + private static final String[] ARCHIVE_CLASSES = {APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS}; + private static String testArchiveName; + + + public static void main(String[] args) throws Exception { + OverrideTests tests = new OverrideTests(); + tests.compileModulesAndDumpArchive(); + tests.testAppClassOverriding(); + tests.testPlatformClassOverriding(); + } + + void compileModulesAndDumpArchive() throws Exception { + boolean compiled; + // javac -d upgradedmods/$upgradedMod src/$upgradedMod/** + int i = 0; + for (String upgradedMod : UPGRADED_MODULES) { + compiled = CompilerUtils.compile( + SRC_DIR.resolve(upgradedMod), + UPGRADEDMODS_DIR[i].resolve(upgradedMod) + ); + Asserts.assertTrue(compiled, upgradedMod + " did not compile"); + i++; + } + + // javac -d mods/test --upgrade-module-path upgradedmods ... + compiled = CompilerUtils.compile( + SRC_DIR.resolve(TEST_MODULE), + MODS_DIR.resolve(TEST_MODULE), + "--upgrade-module-path", UPGRADEDMODS_DIR[0].toString() + + System.getProperty("path.separator") + UPGRADEDMODS_DIR[1].toString() + ); + Asserts.assertTrue(compiled, TEST_MODULE + " did not compile"); + + // the java.activation module is not defined by default; --add-modules is required. + // dumping without "--add-modules java.activation" + // the class in the javax.activation package cannot be found + OutputAnalyzer output1 = TestCommon.dump(null /* appJar*/, TestCommon.list(ARCHIVE_CLASSES)); + TestCommon.checkDump(output1); + output1.shouldContain( + "Preload Warning: Cannot find javax/activation/UnsupportedDataTypeException"); + + // dump the archive with jdk.comiler and java.activation classes in the class list + // with "--add-modules java.activation" + output1 = TestCommon.dump(null /* appJar*/, TestCommon.list(ARCHIVE_CLASSES), + "--add-modules", "java.activation"); + TestCommon.checkDump(output1); + // Make sure all the classes where successfully archived. + for (String archiveClass : ARCHIVE_CLASSES) { + output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass); + } + + testArchiveName = TestCommon.getCurrentArchiveName(); + } + + /** + * APP Class Overriding Tests + * + * Archive APP class com.sun.tools.javac.Main from module jdk.compiler. + * -At run time, upgrade module jdk.compiler using --upgrade-module-path. + * Class.forname(Main) MUST NOT load the archived Main. + * -At run time, module jdk.compiler also exists in --module-path. + * Class.forname(Main) MUST load the archived Main. + */ + public void testAppClassOverriding() throws Exception { + testClassOverriding(APP_ARCHIVE_CLASS, "app"); + } + + /** + * PLATFORM Class Overriding Tests + * + * Archive PLATFORM class javax.activation.UnsupportedDataTypeException from module jdk.activation. + * -At run time, upgrade module jdk.activation using --upgrade-module-path. + * Class.forname(UnsupportedDataTypeException) MUST NOT load the archived UnsupportedDataTypeException. + * -At run time, module jdk.activation also exists in --module-path. + * Class.forname(UnsupportedDataTypeException) MUST load the archived UnsupportedDataTypeException. + */ + public void testPlatformClassOverriding() throws Exception { + testClassOverriding(PLATFORM_ARCHIVE_CLASS, "platform"); + } + + /** + * Run the test twice. Once with upgrade module on --upgrade-module-path and once with it on -p. + * Only modules defined to the PlatformClassLoader are upgradeable. + * Modules defined to the AppClassLoader are not upgradeble; we expect the + * FindException to be thrown. + */ + void testClassOverriding(String archiveClass, String loaderName) throws Exception { + String mid = TEST_MODULE + "/" + MAIN_CLASS; + OutputAnalyzer output; + boolean isAppLoader = loaderName.equals("app"); + int upgradeModIdx = isAppLoader ? 0 : 1; + String expectedException = "java.lang.module.FindException: Unable to compute the hash"; + String prefix[] = new String[4]; + prefix[0] = "-cp"; + prefix[1] = "\"\""; + prefix[2] = "--add-modules"; + prefix[3] = "java.activation"; + + // Run the test with --upgrade-module-path set to alternate location of archiveClass + // The alternate version of archiveClass SHOULD be found. + output = TestCommon.execModule( + prefix, + UPGRADEDMODS_DIR[upgradeModIdx].toString(), + MODS_DIR.toString(), + mid, + archiveClass, loaderName, "true"); // last 3 args passed to test + if (isAppLoader) { + try { + output.shouldContain(expectedException); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + } else { + TestCommon.checkExec(output); + } + + // Now run this same test again, but this time without AppCDS. Behavior should be the same. + CDSOptions opts = (new CDSOptions()) + .addPrefix(prefix) + .setArchiveName(testArchiveName).setUseVersion(false) + .addSuffix("--upgrade-module-path", UPGRADEDMODS_DIR[upgradeModIdx].toString(), + "-p", MODS_DIR.toString(), "-m", mid) + .addSuffix(archiveClass, loaderName, "true"); + + output = CDSTestUtils.runWithArchive(opts); + + if (isAppLoader) { + try { + output.shouldContain(expectedException); + } catch (Exception e) { + TestCommon.checkCommonExecExceptions(output, e); + } + } else { + if (!CDSTestUtils.isUnableToMap(output)) + output.shouldHaveExitValue(0); + } + + // Run the test with -p set to alternate location of archiveClass. + // The alternate version of archiveClass SHOULD NOT be found. + output = TestCommon.execModule( + prefix, + null, + UPGRADEDMODS_DIR[upgradeModIdx].toString() + java.io.File.pathSeparator + MODS_DIR.toString(), + mid, + archiveClass, loaderName, "false"); // last 3 args passed to test + TestCommon.checkExec(output); + + // Now run this same test again, but this time without AppCDS. Behavior should be the same. + opts = (new CDSOptions()) + .addPrefix(prefix) + .setArchiveName(testArchiveName).setUseVersion(false) + .addSuffix("-p", MODS_DIR.toString(), "-m", mid) + .addSuffix(archiveClass, loaderName, "false"); // params to the test class + + OutputAnalyzer out = CDSTestUtils.runWithArchive(opts); + if (!CDSTestUtils.isUnableToMap(out)) + out.shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java new file mode 100644 index 00000000000..3016bb41a10 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +package javax.activation; + +import java.io.IOException; + +public class UnsupportedDataTypeException extends IOException { + public UnsupportedDataTypeException() { + } + + public String toString() { + return "hi"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java new file mode 100644 index 00000000000..23c707a46f7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +module java.activation { + exports javax.activation; +} + diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java new file mode 100644 index 00000000000..f535ccd034a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +package com.sun.tools.javac; + +public class Main { + public String toString() { + return "hi"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java new file mode 100644 index 00000000000..a9c236d7bac --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +module jdk.compiler { + exports com.sun.tools.javac; +} + diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java new file mode 100644 index 00000000000..e752f4d3da5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +/** + * Used with -p or --upgrade-module-path to exercise the replacement + * of classes in modules that are linked into the runtime image. + */ + +package jdk.test; + +public class Main { + static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader(); + static final ClassLoader SYS_LOADER = ClassLoader.getSystemClassLoader(); + + public static void main(String[] args) throws Exception { + ClassLoader loader = null; + boolean shouldOverride = false; + + /* + * 3 Arguments are passed to this test: + * 1. className: Name of the class to load. + * 2. loaderName: Either "platform" or "app", which specifies which ClassLoader is expected + * to be the defining ClassLoader once the class is loaded. The initiating + * ClassLoader is always the default ClassLoader (which should be the + * app (system) ClassLoader. + * 3. shouldOverride: Either "true" or "false" to indicate whether the loaded class + * should be the one we are attempting to override with (not the archived version). + */ + + assertTrue(args.length == 3, "Unexpected number of arguments: expected 3, actual " + args.length); + String className = args[0].replace('/', '.'); + String loaderName = args[1]; // "platform" or "app" + String shouldOverrideName = args[2]; // "true" or "false" + + if (loaderName.equals("app")) { + loader = SYS_LOADER; + } else if (loaderName.equals("platform")) { + loader = PLATFORM_LOADER; + } else { + assertTrue(false); + } + + if (shouldOverrideName.equals("true")) { + shouldOverride = true; + } else if (shouldOverrideName.equals("false")) { + shouldOverride = false; + } else { + assertTrue(false); + } + + // Load the class with the default ClassLoader. + Class<?> clazz = Class.forName(className, true, loader); + // Make sure we got the expected defining ClassLoader + testLoader(clazz, loader); + // Create an instance and see what toString() returns + String s = clazz.newInstance().toString(); + // The overridden version of the class should return "hi". Make sure + // it does only if we are expecting to have loaded the overridden version. + assertTrue(s.equals("hi") == shouldOverride); + } + + /** + * Asserts that given class has the expected defining loader. + */ + static void testLoader(Class<?> clazz, ClassLoader expected) { + ClassLoader loader = clazz.getClassLoader(); + if (loader != expected) { + throw new RuntimeException(clazz + " loaded by " + loader + ", expected " + expected); + } + } + + static void assertTrue(boolean expr) { + assertTrue(expr, ""); + } + + static void assertTrue(boolean expr, String msg) { + if (!expr) + throw new RuntimeException("assertion failed: " + msg); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java new file mode 100644 index 00000000000..8ca8942555b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +module test { + requires jdk.compiler; + requires java.activation; +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java new file mode 100644 index 00000000000..7d564caa8d1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +class LoadMe { + static String getValue() { + return "beforeHook"; + } + static String getOtherValue() { + return "abc-beforeHook-xyz"; + } +} + +public class ClassFileLoadHook { + public enum TestCaseId { + SHARING_OFF_CFLH_ON, // test case to establish a baseline + SHARING_ON_CFLH_OFF, + SHARING_AUTO_CFLH_ON, + SHARING_ON_CFLH_ON + } + + public static void main(String args[]) { + TestCaseId testCase = TestCaseId.valueOf(args[0]); + WhiteBox wb = WhiteBox.getWhiteBox(); + + System.out.println("====== ClassFileLoadHook.main():testCase = " + testCase); + System.out.println("getValue():" + LoadMe.getValue()); + System.out.println("getOtherValue():" + LoadMe.getOtherValue()); + + switch (testCase) { + case SHARING_OFF_CFLH_ON: + assertTrue("after_Hook".equals(LoadMe.getValue()) && + "abc-after_Hook-xyz".equals(LoadMe.getOtherValue()), + "Not sharing, this test should replace beforeHook " + + "with after_Hook"); + break; + + case SHARING_ON_CFLH_OFF: + assertTrue(wb.isSharedClass(LoadMe.class), + "LoadMe should be shared, but is not"); + assertTrue("beforeHook".equals(LoadMe.getValue()) && + "abc-beforeHook-xyz".equals(LoadMe.getOtherValue()), + "CFLH off, bug values are redefined"); + break; + + case SHARING_AUTO_CFLH_ON: + case SHARING_ON_CFLH_ON: + // LoadMe is rewritten on CFLH + assertFalse(wb.isSharedClass(LoadMe.class), + "LoadMe should not be shared if CFLH has modified the class"); + assertFalse("beforeHook".equals(LoadMe.getValue()) && + "abc-beforeHook-xyz".equals(LoadMe.getOtherValue()), + "Class contents should be changed if CFLH is enabled"); + break; + + default: + throw new RuntimeException("Invalid testcase"); + + } + } + + private static void assertTrue(boolean expr, String msg) { + if (!expr) + throw new RuntimeException(msg); + } + + private static void assertFalse(boolean expr, String msg) { + if (expr) + throw new RuntimeException(msg); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java new file mode 100644 index 00000000000..e62013ba8a3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Test jvmti class file loader hook interaction with AppCDS + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * @build ClassFileLoadHook + * @run main/othervm/native ClassFileLoadHookTest + */ + + +import jdk.test.lib.Asserts; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + + +public class ClassFileLoadHookTest { + public static String sharedClasses[] = { + "ClassFileLoadHook", + "ClassFileLoadHook$TestCaseId", + "ClassFileLoadHook$1", + "LoadMe" + }; + + public static void main(String[] args) throws Exception { + String wbJar = + ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox"); + String appJar = + ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses); + String useWb = "-Xbootclasspath/a:" + wbJar; + + // First, run the test class directly, w/o sharing, as a baseline reference + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + useWb, + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "ClassFileLoadHook", + "" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON); + TestCommon.executeAndLog(pb, "no-sharing").shouldHaveExitValue(0); + + // Run with AppCDS, but w/o CFLH - second baseline + TestCommon.testDump(appJar, sharedClasses, useWb); + OutputAnalyzer out = TestCommon.exec(appJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "ClassFileLoadHook", + "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_OFF); + + TestCommon.checkExec(out); + + + // Now, run with AppCDS with -Xshare:auto and CFLH + out = TestCommon.execAuto("-cp", appJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "ClassFileLoadHook", + "" + ClassFileLoadHook.TestCaseId.SHARING_AUTO_CFLH_ON); + + CDSOptions opts = (new CDSOptions()).setXShareMode("auto"); + TestCommon.checkExec(out, opts); + + // Now, run with AppCDS -Xshare:on and CFLH + out = TestCommon.exec(appJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", useWb, + "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook", + "ClassFileLoadHook", + "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON); + TestCommon.checkExec(out); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf new file mode 100644 index 00000000000..58dcf797de6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Premain-Class: InstrumentationRegisterClassFileTransformer +Agent-Class: InstrumentationRegisterClassFileTransformer +Can-Retransform-Classes: true +Can-Redefine-Classes: true diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java new file mode 100644 index 00000000000..1328f583031 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.net.URL; +import java.net.URLClassLoader; +import java.io.File; +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import sun.hotspot.WhiteBox; + +public class InstrumentationApp { + static WhiteBox wb = WhiteBox.getWhiteBox(); + + public static final String COO_CLASS_NAME = "InstrumentationApp$Coo"; + + public static interface Intf { // Loaded from Boot class loader (-Xbootclasspath/a). + public String get(); + } + public static class Bar implements Intf { // Loaded from Boot class loader. + public String get() { + // The initial transform: + // change "buzz" -> "fuzz" + // The re-transform: + // change "buzz" -> "guzz" + return "buzz"; + } + } + public static class Foo implements Intf { // Loaded from AppClassLoader, or from a custom loader + public String get() { + // The initial transform: + // change "buzz" -> "fuzz" + // The re-transform: + // change "buzz" -> "guzz" + return "buzz"; + } + } + public static class Coo implements Intf { // Loaded from custom class loader. + public String get() { + // The initial transform: + // change "buzz" -> "fuzz" + // The re-transform: + // change "buzz" -> "guzz" + return "buzz"; + } + } + + // This class file should be archived if AppCDSv2 is enabled on this platform. See + // the comments around the call to TestCommon.dump in InstrumentationTest.java. + public static class ArchivedIfAppCDSv2Enabled {} + + public static boolean isAppCDSV2Enabled() { + return wb.isSharedClass(ArchivedIfAppCDSv2Enabled.class); + } + + public static class MyLoader extends URLClassLoader { + public MyLoader(URL[] urls, ClassLoader parent, File jar) { + super(urls, parent); + this.jar = jar; + } + File jar; + + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + // First, check if the class has already been loaded + Class<?> clz = findLoadedClass(name); + if (clz != null) { + return clz; + } + + if (name.equals(COO_CLASS_NAME)) { + try { + byte[] buff = Util.getClassFileFromJar(jar, name); + return defineClass(name, buff, 0, buff.length); + } catch (Throwable t) { + t.printStackTrace(); + throw new RuntimeException("Unexpected", t); + } + } + } + return super.loadClass(name, resolve); + } + } + + static int numTests = 0; + static int failed = 0; + static boolean isAttachingAgent = false; + static Instrumentation instrumentation; + + public static void main(String args[]) throws Throwable { + System.out.println("INFO: AppCDSv1 " + (wb.isSharedClass(InstrumentationApp.class) ? "enabled" :"disabled")); + System.out.println("INFO: AppCDSv2 " + (isAppCDSV2Enabled() ? "enabled" : "disabled")); + + File bootJar = new File(args[0]); + File appJar = new File(args[1]); + File custJar = new File(args[2]); + String flagFile = args[3]; + waitAttach(flagFile); + + instrumentation = InstrumentationRegisterClassFileTransformer.getInstrumentation(); + System.out.println("INFO: instrumentation = " + instrumentation); + + testBootstrapCDS("Bootstrap Loader", bootJar); + testAppCDSv1("Application Loader", appJar); + + if (isAppCDSV2Enabled()) { + testAppCDSv2("Custom Loader (unregistered)", custJar); + } + + if (failed > 0) { + throw new RuntimeException("FINAL RESULT: " + failed + " out of " + numTests + " test case(s) have failed"); + } else { + System.out.println("FINAL RESULT: All " + numTests + " test case(s) have passed!"); + } + } + + static void waitAttach(String flagFile) throws Throwable { + if (!flagFile.equals("noattach")) { + File f = new File(flagFile); + long start = System.currentTimeMillis(); + while (f.exists()) { + long elapsed = System.currentTimeMillis() - start; + System.out.println(".... (" + elapsed + ") waiting for deletion of " + f); + Thread.sleep(1000); + } + System.out.println("Attach succeeded (child)"); + isAttachingAgent = true; + } + } + + static void testBootstrapCDS(String group, File jar) throws Throwable { + doTest(group, new Bar(), jar); + } + + static void testAppCDSv1(String group, File jar) throws Throwable { + doTest(group, new Foo(), jar); + } + + static void testAppCDSv2(String group, File jar) throws Throwable { + URL[] urls = new URL[] {jar.toURI().toURL()}; + MyLoader loader = new MyLoader(urls, InstrumentationApp.class.getClassLoader(), jar); + Class klass = loader.loadClass(COO_CLASS_NAME); + doTest(group, (Intf)klass.newInstance(), jar); + } + + static void doTest(String group, Intf object, File jar) throws Throwable { + Class klass = object.getClass(); + System.out.println(); + System.out.println("++++++++++++++++++++++++++"); + System.out.println("Test group: " + group); + System.out.println("Testing with classloader = " + klass.getClassLoader()); + System.out.println("Testing with class = " + klass); + System.out.println("++++++++++++++++++++++++++"); + + // Initial transform + String f = object.get(); + assertTrue(f.equals("fuzz"), "object.get(): Initial transform should give 'fuzz'", f); + + // Retransform + f = "(failed)"; + try { + instrumentation.retransformClasses(klass); + f = object.get(); + } catch (UnmodifiableClassException|UnsupportedOperationException e) { + e.printStackTrace(); + } + assertTrue(f.equals("guzz"), "object.get(): retransformation should give 'guzz'", f); + + // Redefine + byte[] buff = Util.getClassFileFromJar(jar, klass.getName()); + Util.replace(buff, "buzz", "huzz"); + f = "(failed)"; + try { + instrumentation.redefineClasses(new ClassDefinition(klass, buff)); + f = object.get(); + } catch (UnmodifiableClassException|UnsupportedOperationException e) { + e.printStackTrace(); + } + assertTrue(f.equals("quzz"), "object.get(): redefinition should give 'quzz'", f); + + System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++ (done)\n\n"); + } + + private static void assertTrue(boolean expr, String msg, String string) { + numTests ++; + System.out.printf("Test case %2d ", numTests); + + if (expr) { + System.out.println("PASSED: " + msg + " and we got '" + string + "'"); + } else { + failed ++; + System.out.println("FAILED: " + msg + " but we got '" + string + "'"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java new file mode 100644 index 00000000000..64eabbaf003 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; + +// Note: Util is from /test/hotspot/jtreg/runtime/appcds/test-classes/TestCommon.java + +public class InstrumentationClassFileTransformer implements ClassFileTransformer { + public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined, + ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException { + + if (name.startsWith("InstrumentationApp$") && !name.equals("InstrumentationApp$NotTransformed")) { + System.out.println("Transforming: " + name + " class = " + classBeingRedefined); + try { + if (classBeingRedefined == null) { + // Initial transform + replace(buffer, "buzz", "fuzz"); + } else { + replace(buffer, "buzz", "guzz"); // Retransform + replace(buffer, "huzz", "quzz"); // Redefine + } + } catch (Throwable t) { + t.printStackTrace(); + } + return buffer; + } + return null; + } + + static void replace(byte[] buffer, String from, String to) { + int n = Util.replace(buffer, from, to); + System.out.println("..... replaced " + n + " occurrence(s) of '" + from + "' to '" + to + "'"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java new file mode 100644 index 00000000000..2d2aec74269 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; + +// This class is available on the classpath so it can be accessed by InstrumentationApp +public class InstrumentationRegisterClassFileTransformer { + private static Instrumentation savedInstrumentation; + + public static void premain(String agentArguments, Instrumentation instrumentation) { + System.out.println("InstrumentationRegisterClassFileTransformer.premain() is called"); + instrumentation.addTransformer(new InstrumentationClassFileTransformer(), /*canRetransform=*/true); + savedInstrumentation = instrumentation; + } + + public static Instrumentation getInstrumentation() { + return savedInstrumentation; + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + premain(args, inst); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java new file mode 100644 index 00000000000..1e869c28929 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2014, 2017, 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 + * @summary Exercise the java.lang.instrument.Instrumentation APIs on classes archived + * using CDS/AppCDSv1/AppCDSv2. + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * @build sun.hotspot.WhiteBox + * InstrumentationApp + * InstrumentationClassFileTransformer + * InstrumentationRegisterClassFileTransformer + * @run main/othervm InstrumentationTest + */ + +// Note: TestCommon is from /test/hotspot/jtreg/runtime/appcds/TestCommon.java +// Note: Util is from /test/hotspot/jtreg/runtime/appcds/test-classes/TestCommon.java + +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import jdk.test.lib.Asserts; +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class InstrumentationTest { + public static String bootClasses[] = { + "InstrumentationApp$Intf", + "InstrumentationApp$Bar", + "sun.hotspot.WhiteBox", + }; + public static String appClasses[] = { + "InstrumentationApp", + "InstrumentationApp$Foo", + "InstrumentationApp$MyLoader", + }; + public static String custClasses[] = { + "InstrumentationApp$Coo", + }; + public static String sharedClasses[] = TestCommon.concat(bootClasses, appClasses); + + public static String agentClasses[] = { + "InstrumentationClassFileTransformer", + "InstrumentationRegisterClassFileTransformer", + "Util", + }; + + public static void main(String[] args) throws Throwable { + runTest(false); + runTest(true); + } + + public static void runTest(boolean attachAgent) throws Throwable { + String bootJar = + ClassFileInstaller.writeJar("InstrumentationBoot.jar", bootClasses); + String appJar = + ClassFileInstaller.writeJar("InstrumentationApp.jar", + TestCommon.concat(appClasses, + "InstrumentationApp$ArchivedIfAppCDSv2Enabled")); + String custJar = + ClassFileInstaller.writeJar("InstrumentationCust.jar", custClasses); + String agentJar = + ClassFileInstaller.writeJar("InstrumentationAgent.jar", + ClassFileInstaller.Manifest.fromSourceFile("InstrumentationAgent.mf"), + agentClasses); + + String bootCP = "-Xbootclasspath/a:" + bootJar; + + System.out.println(""); + System.out.println("============================================================"); + System.out.println("CDS: NO, attachAgent: " + (attachAgent ? "YES" : "NO")); + System.out.println("============================================================"); + System.out.println(""); + + String agentCmdArg, flagFile; + if (attachAgent) { + // we will attach the agent, so don't specify -javaagent in the command line. We'll use + // something harmless like -showversion to make it easier to construct the command line + agentCmdArg = "-showversion"; + } else { + agentCmdArg = "-javaagent:" + agentJar; + } + + // First, run the test class directly, w/o sharing, as a baseline reference + flagFile = getFlagFile(attachAgent); + AgentAttachThread t = doAttach(attachAgent, flagFile, agentJar); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + bootCP, + "-cp", appJar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xshare:off", + agentCmdArg, + "InstrumentationApp", bootJar, appJar, custJar, flagFile); + TestCommon.executeAndLog(pb, "no-sharing").shouldHaveExitValue(0); + checkAttach(t); + + // Dump the AppCDS archive. On some platforms AppCDSv2 may not be enabled, so we + // first try the v2 classlist, and if that fails, revert to the v1 classlist. + // Note that the InstrumentationApp$ArchivedIfAppCDSv2Enabled class is archived + // only if V2 is enabled. This is tested by InstrumentationApp.isAppCDSV2Enabled(). + String[] v2Classes = { + "InstrumentationApp$ArchivedIfAppCDSv2Enabled", + "java/lang/Object id: 0", + "InstrumentationApp$Intf id: 1", + "InstrumentationApp$Coo id: 2 super: 0 interfaces: 1 source: " + custJar, + }; + String[] sharedClassesWithV2 = TestCommon.concat(v2Classes, sharedClasses); + OutputAnalyzer out = TestCommon.dump(appJar, sharedClassesWithV2, bootCP); + if (out.getExitValue() != 0) { + System.out.println("Redumping with AppCDSv2 disabled"); + TestCommon.testDump(appJar, sharedClasses, bootCP); + } + + // Run with AppCDS. + System.out.println(""); + System.out.println("============================================================"); + System.out.println("CDS: YES, attachAgent: " + (attachAgent ? "YES" : "NO")); + System.out.println("============================================================"); + System.out.println(""); + + flagFile = getFlagFile(attachAgent); + t = doAttach(attachAgent, flagFile, agentJar); + out = TestCommon.execAuto("-cp", appJar, + bootCP, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + agentCmdArg, + "InstrumentationApp", bootJar, appJar, custJar, flagFile); + + CDSOptions opts = (new CDSOptions()).setXShareMode("auto"); + TestCommon.checkExec(out, opts); + checkAttach(t); + } + + static int flagFileSerial = 1; + static private String getFlagFile(boolean attachAgent) { + if (attachAgent) { + // Do not reuse the same file name as Windows may fail to + // delete the file. + return "attach.flag." + ProcessHandle.current().pid() + + "." + (flagFileSerial++) + "." + System.currentTimeMillis(); + } else { + return "noattach"; + } + } + + static AgentAttachThread doAttach(boolean attachAgent, String flagFile, String agentJar) throws Throwable { + if (!attachAgent) { + return null; + } + + // We use the flagFile to prevent the child process to make progress, until we have + // attached to it. + File f = new File(flagFile); + FileOutputStream o = new FileOutputStream(f); + o.write(1); + o.close(); + if (!f.exists()) { + throw new RuntimeException("Failed to create " + f); + } + + // At this point, the child process is not yet launched. Note that + // TestCommon.exec() and OutputAnalyzer.OutputAnalyzer() both block + // until the child process has finished. + // + // So, we will launch a AgentAttachThread which will poll the system + // until the child process is launched, and then do the attachment. + // The child process is uniquely identified by having flagFile in its + // command-line -- see AgentAttachThread.getPid(). + AgentAttachThread t = new AgentAttachThread(flagFile, agentJar); + t.start(); + return t; + } + + static void checkAttach(AgentAttachThread thread) throws Throwable { + if (thread != null) { + thread.check(); + } + } + + static class AgentAttachThread extends Thread { + String flagFile; + String agentJar; + volatile boolean succeeded; + + AgentAttachThread(String flagFile, String agentJar) { + this.flagFile = flagFile; + this.agentJar = agentJar; + this.succeeded = false; + } + + static String getPid(String flagFile) throws Throwable { + while (true) { + // Keep polling until the child process has been launched. If for some + // reason the child process fails to launch, this test will be terminated + // by JTREG's time-out mechanism. + Thread.sleep(100); + List<VirtualMachineDescriptor> vmds = VirtualMachine.list(); + for (VirtualMachineDescriptor vmd : vmds) { + if (vmd.displayName().contains(flagFile) && vmd.displayName().contains("InstrumentationApp")) { + // We use flagFile (which has the PID of this process) as a unique identifier + // to ident the child process, which we want to attach to. + System.out.println("Process found: " + vmd.id() + " " + vmd.displayName()); + return vmd.id(); + } + } + } + } + + public void run() { + try { + String pid = getPid(flagFile); + VirtualMachine vm = VirtualMachine.attach(pid); + System.out.println(agentJar); + vm.loadAgent(agentJar); + } catch (Throwable t) { + t.printStackTrace(); + throw new RuntimeException(t); + } + + // Delete the flagFile to indicate to the child process that we + // have attached to it, so it should proceed. + File f = new File(flagFile); + for (int i=0; i<5; i++) { + // The detele may fail on Windows if the child JVM is checking + // f.exists() at exactly the same time?? Let's do a little + // dance. + f.delete(); + try { + Thread.sleep(10); + } catch (Throwable t) {;} + } + if (f.exists()) { + throw new RuntimeException("Failed to delete " + f); + } + System.out.println("Attach succeeded (parent)"); + succeeded = true; + } + + void check() throws Throwable { + super.join(); + if (!succeeded) { + throw new RuntimeException("Attaching agent to child VM failed"); + } + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java new file mode 100644 index 00000000000..b7cfe22d5fc --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +class ParallelClassTr0 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr1 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr2 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr3 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr4 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr5 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr6 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr7 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr8 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr9 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr10 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr11 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr12 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr13 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr14 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr15 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr16 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr17 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr18 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr19 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr20 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr21 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr22 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr23 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr24 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr25 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr26 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr27 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr28 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr29 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr30 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr31 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr32 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr33 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr34 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr35 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr36 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr37 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr38 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } +class ParallelClassTr39 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; } + +class ParallelClassesTransform { + public static final int NUMBER_OF_CLASSES = 40; + public static final String BEFORE_PATTERN = "class-transform-check: this-should-be-transformed"; + public static final String AFTER_PATTERN = "class-transform-check: this-has-been--transformed"; +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java new file mode 100644 index 00000000000..6356e2d4a6d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Load app classes from CDS archive in parallel threads, + * use initial transformation (CFLH) + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * /test/hotspot/jtreg/runtime/appcds/test-classes /test/hotspot/jtreg/runtime/appcds/jvmti + * /test/hotspot/jtreg/testlibrary/jvmti + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @modules java.base/jdk.internal.misc + * java.management + * jdk.jartool/sun.tools.jar + * java.instrument + * @build TransformUtil TransformerAgent ParallelLoad + * @run main ParallelLoadAndTransformTest + */ +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class ParallelLoadAndTransformTest { + + public static void main(String[] args) throws Exception { + String prop = "-Dappcds.parallel.transform.mode=cflh"; + String appJar = ClassFileInstaller.writeJar("parallel_load.jar", + getClassList(true)); + String agentJar = prepareAgent(); + + TestCommon.test(appJar, getClassList(false), + "-javaagent:" + agentJar + "=ParallelClassTr.*", + prop, "ParallelLoad"); + } + + + private static String[] getClassList(boolean includeWatchdog) { + List<String> classList = + IntStream.range(0, ParallelClassesTransform.NUMBER_OF_CLASSES) + .mapToObj(i -> "ParallelClassTr" + i) + .collect(Collectors.toList()); + + classList.add("ParallelLoad"); + classList.add("ParallelLoadThread"); + if (includeWatchdog) + classList.add("ParallelLoadWatchdog"); + + return classList.toArray(new String[0]); + } + + + // Agent is the same for all test cases + private static String prepareAgent() throws Exception { + String agentClasses[] = { + "TransformerAgent", + "TransformerAgent$SimpleTransformer", + "TransformUtil" + }; + + String manifest = "../../../../testlibrary/jvmti/TransformerAgent.mf"; + + return ClassFileInstaller.writeJar("TransformerAgent.jar", + ClassFileInstaller.Manifest.fromSourceFile(manifest), + agentClasses); + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java new file mode 100644 index 00000000000..4addd099854 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Exercise initial transformation (class file loader hook) + * with CDS/AppCDS with Interface/Implementor pair + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes + * /test/hotspot/jtreg/runtime/appcds/jvmti /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability + * /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses + * /test/hotspot/jtreg/runtime/SharedArchiveFile /test/hotspot/jtreg/testlibrary/jvmti + * /test/hotspot/jtreg/runtime/appcds/customLoader + * /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * java.instrument + * @build TransformUtil TransformerAgent Interface Implementor + * @run main/othervm TransformRelatedClassesAppCDS Interface Implementor + */ diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java new file mode 100644 index 00000000000..1935715c09d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +// Structure of the test: +// TransformRelatedClassesAppCDS -- common main test driver +// Invoked from test driver classes: +// TransformInterfaceAndImplementor, TransformSuperAndSubClasses.java +// prepares test artifacts, launches tests, checks results +// SuperClazz, SubClass -- classes under test +// Interface, Implementor -- classes under test +// TransformerAgent -- an agent that is used when JVM-under-test is executed +// to transform specific strings inside specified classes +// TransformerAgent.mf - accompanies transformer agent +// CustomLoaderApp -- a test "application" that is used to load +// classes-under-test (Parent, Child) via custom class loader, using +// AppCDS-v2 mechanism (unregistered custom loaders, aka FP) +// This "app" is launched in a child process by this driver with sharing on. + +import java.io.File; +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; + +// This class is intended to test 2 parent-child relationships: +// 1. Base Class (parent) and Derived Class (child) +// 2. Interface (parent) and Implementor (child) +// Parameters to main(): parent, child + +public class TransformRelatedClassesAppCDS extends TransformRelatedClasses { + private static void log(String msg, Object... args) { + String msg0 = String.format(msg, args); + System.out.println("TransformRelatedClassesAppCDS: " + msg0); + } + + // Initial Test Matrix: + // (ParentTransformed = true/false, ChildTransformed = true/false) x + // (BootCDS - see open tests, AppCDS-v1, AppCDS-v2-unregistered) + // Total cases: 2 x 4 = 8 + public static void main(String args[]) throws Exception { + TransformRelatedClassesAppCDS test = + new TransformRelatedClassesAppCDS(args[0], args[1]); + + test.prepareAgent(agentClasses); + + // Test Table + // testCaseId | transformParent | tranformChild | isParentExpectedShared | isChildExpectedShared + ArrayList<TestEntry> testTable = new ArrayList<>(); + + // base case - no tranformation - all expected to be shared + testTable.add(new TestEntry(0, false, false, true, true)); + + // transform parent only - both parent and child should not be shared + testTable.add(new TestEntry(1, true, false, false, false)); + + // transform parent and child - both parent and child should not be shared + testTable.add(new TestEntry(2, true, true, false, false)); + + // transform child only - parent should still be shared, but not child + testTable.add(new TestEntry(3, false, true, true, false)); + + // run the tests + test.runWithAppLoader(testTable); + test.runWithCustomLoader(testTable); + } + + + public TransformRelatedClassesAppCDS(String parent, String child) { + super(parent, child); + + // a trick to get it compiled by jtreg + CustomLoaderApp.ping(); + } + + + private void prepareAgent(String[] agentClasses) throws Exception { + String manifest = "../../../../testlibrary/jvmti/TransformerAgent.mf"; + agentJar = ClassFileInstaller.writeJar("TransformerAgent.jar", + ClassFileInstaller.Manifest.fromSourceFile(manifest), + agentClasses); + } + + + private void runWithAppLoader(ArrayList<TestEntry> testTable) throws Exception { + String appJar = writeJar("app", testClasses); + + // create an archive + OutputAnalyzer out = TestCommon.dump(appJar, testClasses); + TestCommon.checkDump(out); + + // execute with archive + for (TestEntry entry : testTable) { + log("runTestWithAppLoader(): testCaseId = %d", entry.testCaseId); + String params = TransformTestCommon.getAgentParams(entry, parent, child); + String agentParam = String.format("-javaagent:%s=%s", agentJar, params); + out = TestCommon.execCommon("-Xlog:class+load=info", "-cp", appJar, + agentParam, child); + + TransformTestCommon.checkResults(entry, out, parent, child); + } + } + + + private String[] getCustomClassList(String loaderType, String customJar) { + String type = child + "-" + loaderType; + + switch (type) { + + case "SubClass-unregistered": + return new String[] { + "CustomLoaderApp", + "java/lang/Object id: 0", + parent + " id: 1 super: 0 source: " + customJar, + child + " id: 2 super: 1 source: " + customJar, + }; + + case "Implementor-unregistered": + return new String[] { + "CustomLoaderApp", + "java/lang/Object id: 0", + parent + " id: 1 super: 0 source: " + customJar, + child + " id: 2 super: 0 interfaces: 1 source: " + customJar, + }; + + default: + throw new IllegalArgumentException("getCustomClassList - wrong type: " + type); + } + } + + + private void runWithCustomLoader(ArrayList<TestEntry> testTable) throws Exception { + if (!TestCommon.isCustomLoaderSupported()) { + log("custom loader not supported for this platform" + + " - skipping test case for custom loader"); + return; + } + + String appClasses[] = { + "CustomLoaderApp", + }; + + String customClasses[] = { parent, child }; + + // create jar files: appJar, customJar (for custom loaders to load classes from) + String appJar = writeJar("custldr-app", appClasses); + String customJar = writeJar("custldr-custom", customClasses); + + for (TestEntry entry : testTable) { + log("runTestWithCustomLoader(): testCaseId = %d", entry.testCaseId); + // unregistered (aka FP) case + String[] classList = getCustomClassList("unregistered",customJar); + execAndCheckWithCustomLoader(entry, "unregistered", classList, + appJar, agentJar, customJar); + } + } + + + private void + execAndCheckWithCustomLoader(TestEntry entry, String loaderType, + String[] classList, String appJar, + String agentJar, String customJar) + throws Exception { + + OutputAnalyzer out = TestCommon.dump(appJar, classList); + TestCommon.checkDump(out); + + String agentParam = "-javaagent:" + agentJar + "=" + + TransformTestCommon.getAgentParams(entry, parent, child); + + out = TestCommon.execCommon("-Xlog:class+load=info", + "-cp", appJar, + "--add-opens=java.base/java.security=ALL-UNNAMED", + agentParam, + "CustomLoaderApp", + customJar, loaderType, child); + TransformTestCommon.checkResults(entry, out, parent, child); + } + + + private String writeJar(String type, String[] classes) + throws Exception { + String jarName = String.format("%s-%s.jar", child, type); + return ClassFileInstaller.writeJar(jarName, classes); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java new file mode 100644 index 00000000000..2c631b916e4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Exercise initial transformation (class file loader hook) + * with CDS/AppCDS with SubClass and SuperClass + * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes + * /test/hotspot/jtreg/runtime/appcds/jvmti /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability + * /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses + * /test/hotspot/jtreg/runtime/SharedArchiveFile /test/hotspot/jtreg/testlibrary/jvmti + * /test/hotspot/jtreg/runtime/appcds/customLoader + * /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.flavor != "minimal" + * @modules java.base/jdk.internal.misc + * jdk.jartool/sun.tools.jar + * java.management + * java.instrument + * @build TransformUtil TransformerAgent SubClass SuperClazz + * @run main/othervm TransformRelatedClassesAppCDS SuperClazz SubClass + */ diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java new file mode 100644 index 00000000000..943a5257557 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class RedefineBasic { + + public static String newB = + " class RedefineBasic$B { " + + " public static void okToCallBeforeRedefine() { " + + " throw new RuntimeException(\"newB: okToCallBeforeRedefine is " + + " called after redefinition, test failed\"); }" + + " public static void okToCallAfterRedefine() { " + + " System.out.println(\"newB: okToCallAfterRedefine\"); } " + + " } "; + + + static class B { + public static void okToCallBeforeRedefine() { + System.out.println("okToCallBeforeRedefine"); + } + public static void okToCallAfterRedefine() { + throw new RuntimeException( + "okToCallAfterRedefine is called before redefinition, test failed"); + } + } + + static class SubclassOfB extends B { + public static void testAfterRedefine() { + B.okToCallAfterRedefine(); + } + } + + class Subclass2OfB extends B { + public void testAfterRedefine() { + super.okToCallAfterRedefine(); + } + } + + // verify that a given class is shared, report error if necessary + public static void + verifyClassIsShared(WhiteBox wb, Class c) throws Exception { + if (!wb.isSharedClass(c)) { + throw new RuntimeException( + "This class should be shared but isn't: " + c.getName()); + } else { + System.out.println("The class is shared as expected: " + + c.getName()); + } + } + + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + + verifyClassIsShared(wb, RedefineBasic.class); + verifyClassIsShared(wb, B.class); + verifyClassIsShared(wb, SubclassOfB.class); + verifyClassIsShared(wb, Subclass2OfB.class); + + // (1) Test case: verify that original B works as expected + // and that redefined B is shared and works as expected, + // with new behavior + B.okToCallBeforeRedefine(); + RedefineClassHelper.redefineClass(B.class, newB); + verifyClassIsShared(wb, B.class); + B.okToCallAfterRedefine(); + + // Static subclass of the super: + // 1. Make sure it is still shared + // 2. and it calls the correct super (the redefined one) + verifyClassIsShared(wb, SubclassOfB.class); + SubclassOfB.testAfterRedefine(); + + // Same as above, but for non-static class + verifyClassIsShared(wb, Subclass2OfB.class); + RedefineBasic thisTest = new RedefineBasic(); + thisTest.testSubclass2OfB(); + } + + public void testSubclass2OfB() { + Subclass2OfB sub = new Subclass2OfB(); + sub.testAfterRedefine(); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java new file mode 100644 index 00000000000..396e2cba703 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Run /runtime/RedefineTests/RedefineRunningMethods in AppCDS mode to + * make sure class redefinition works with CDS. + * (Note: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/RedefineTests /test/hotspot/jtreg/runtime/appcds + * @modules java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * java.base/jdk.internal.misc + * java.management + * @run main RedefineClassHelper + * @build sun.hotspot.WhiteBox RedefineBasic + * @run main RedefineBasicTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class RedefineBasicTest { + public static String sharedClasses[] = { + "RedefineBasic", + "RedefineBasic$B", + "RedefineBasic$SubclassOfB", + "RedefineBasic$Subclass2OfB", + "RedefineClassHelper", + "jdk/test/lib/compiler/InMemoryJavaCompiler", + "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper", + "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1", + "jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject" + }; + + public static void main(String[] args) throws Exception { + String wbJar = + ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox"); + String appJar = + ClassFileInstaller.writeJar("RedefineBasic.jar", sharedClasses); + String useWb = "-Xbootclasspath/a:" + wbJar; + + OutputAnalyzer output; + TestCommon.testDump(appJar, sharedClasses, useWb); + + // redefineagent.jar is created by executing "@run main RedefineClassHelper" + // which should be called before executing RedefineBasicTest + output = TestCommon.exec(appJar, useWb, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-javaagent:redefineagent.jar", + "RedefineBasic"); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java new file mode 100644 index 00000000000..2c2782873a6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary Run /runtime/RedefineTests/RedefineRunningMethods in AppCDS mode to + * make sure class redefinition works with CDS. + * (Note: AppCDS does not support uncompressed oops) + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib /test/hotspot/jtreg/runtime/RedefineTests /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * java.instrument + * jdk.jartool/sun.tools.jar + * @run main RedefineClassHelper + * @build sun.hotspot.WhiteBox RedefineRunningMethods_SharedHelper + * @run main RedefineRunningMethods_Shared + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class RedefineRunningMethods_Shared { + public static String shared_classes[] = { + "RedefineRunningMethods_Shared", + "RedefineRunningMethods_SharedHelper", + "RedefineRunningMethods", + "RedefineRunningMethods$1", + "RedefineRunningMethods$2", + "RedefineRunningMethods$3", + "RedefineRunningMethods$B", + "RedefineClassHelper", + "jdk/test/lib/compiler/InMemoryJavaCompiler", + "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper", + "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1", + "jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject" + }; + + public static void main(String[] args) throws Exception { + String wbJar = ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox"); + String appJar = ClassFileInstaller.writeJar("RedefineRunningMethods_Shared.jar", shared_classes); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + OutputAnalyzer output; + TestCommon.testDump(appJar, shared_classes, + // command-line arguments ... + use_whitebox_jar); + + // RedefineRunningMethods.java contained this: + // @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+iklass+add=trace,redefine+class+iklass+purge=trace RedefineRunningMethods + output = TestCommon.exec(appJar, + // command-line arguments ... + use_whitebox_jar, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + // These arguments are expected by RedefineRunningMethods + "-javaagent:redefineagent.jar", + "-Xlog:redefine+class+iklass+add=trace,redefine+class+iklass+purge=trace", + "RedefineRunningMethods_SharedHelper"); + TestCommon.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java new file mode 100644 index 00000000000..ff1ffada27b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +/** + * This class is executed by RedefineRunningMethods_Shared.java in + * a sub-process. + */ +public class RedefineRunningMethods_SharedHelper { + public static void main(String[] args) throws Exception { + // (1) Validate that all classes used by RedefineRunningMethods are all shared. + WhiteBox wb = WhiteBox.getWhiteBox(); + for (String name : RedefineRunningMethods_Shared.shared_classes) { + name = name.replace('/', '.'); + Class c = Class.forName(name); + if (!wb.isSharedClass(c)) { + throw new RuntimeException("Test set-up problem. " + + "This class should be shared but isn't: " + name); + } else { + System.out.println("The class is shared as expected: " + name); + } + } + + // (2) Run the class redefinition test. + RedefineRunningMethods.main(args); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java new file mode 100644 index 00000000000..7d70f463b4e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Exercise GC with shared strings + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloStringGC sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ExerciseGC + */ +public class ExerciseGC { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJarAndWhiteBox("HelloStringGC"); + + SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("HelloStringGC"), + "SharedStringsBasic.txt"); + + SharedStringsUtils.runWithArchiveAndWhiteBox("HelloStringGC", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+VerifyBeforeGC"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt new file mode 100644 index 00000000000..5b9257d07e9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt @@ -0,0 +1,7 @@ +VERSION: 1.0 +@SECTION: Symbol +0 -1: +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +11 -1: linkMethod +18 -1: type can't be null +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java new file mode 100644 index 00000000000..951f2ec6447 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test relevant combinations of command line flags with shared strings + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloString + * @run main FlagCombo + */ + +import jdk.test.lib.BuildHelper; + +public class FlagCombo { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJar("HelloString"); + + SharedStringsUtils.dump(TestCommon.list("HelloString"), + "SharedStringsBasic.txt"); + + SharedStringsUtils.runWithArchive("HelloString", "-XX:+UseG1GC"); + + if (BuildHelper.isCommercialBuild()) { + SharedStringsUtils.runWithArchiveAuto("HelloString", "-XX:+UnlockCommercialFeatures", + "-XX:StartFlightRecording=dumponexit=true"); + } + + SharedStringsUtils.runWithArchive("HelloString", "-XX:+UnlockDiagnosticVMOptions", + "-XX:NativeMemoryTracking=detail", "-XX:+PrintNMTStatistics"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java new file mode 100644 index 00000000000..3c1cbbe4361 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class HelloString { + public static void main(String args[]) { + // Let's reference the string that is in the archive + // Make sure the string below is in the shared string data file (string list) + String testString = "shared_test_string_unique_14325"; + System.out.println("Hello String: " + testString); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java new file mode 100644 index 00000000000..14732626348 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class HelloStringGC { + public static String[] array01 = new String[1000]; + public static String[] array02 = new String[1000]; + + public static void main(String args[]) throws RuntimeException { + String testString1 = "shared_test_string_unique_14325"; + String testString2 = "test123"; + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (!wb.isShared(testString1) && !wb.areSharedStringsIgnored()) { + throw new RuntimeException("testString1 is not shared"); + } + + for (int i=0; i<5; i++) { + allocSomeStrings(testString1, testString2); + array01 = null; + array02 = null; + System.gc(); + sleep(300); + array01 = new String[1000]; + array02 = new String[1000]; + } + + wb.fullGC(); + + System.out.println("HelloStringGC: PASS"); + } + + private static void allocSomeStrings(String s1, String s2) { + for (int i = 0; i < 1000; i ++) { + array01[i] = new String(s1); + array02[i] = new String(s2); + } + } + + private static void sleep(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + } + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java new file mode 100644 index 00000000000..5129a10ba80 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +// A test class to be launched in AppCDS mode, has basic+ +// coverage of string operations + +import sun.hotspot.WhiteBox; + +public class HelloStringPlus { + public static void main(String args[]) { + // Let's reference the string that is in archive + String testString1 = "shared_test_string_unique_14325"; + System.out.println("Hello String: " + testString1); + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (!wb.isShared(testString1) && !wb.areSharedStringsIgnored()) { + throw new RuntimeException("testString1 is not shared"); + } + + // Check other basic string operations + // Interning and equality + String[] testArray = new String[] {"shared_", "test_", "string_", "intern_", "12345"}; + String toBeInterned = ""; + + StringBuilder sb = new StringBuilder(); + for (String s : testArray) { + sb.append(s); + } + toBeInterned = sb.toString(); + + System.out.println("About to intern a string: " + toBeInterned); + toBeInterned.intern(); + + // check equality + if (testString1.equals(toBeInterned)) + throw new RuntimeException("Equality test 1 failed"); + + if (!testString1.equals("shared_test_string" + '_' + "unique_14325")) + throw new RuntimeException("Equality test 2 failed"); + + // Chech the hash code functionality; no special assertions, just make sure + // no crashe or exception occurs + System.out.println("testString1.hashCode() = " + testString1.hashCode()); + + // Check intern() method for "" string + String empty = ""; + String empty_interned = empty.intern(); + if (wb.isShared(empty)) { + throw new RuntimeException("Empty string should not be shared"); + } + if (empty_interned != empty) { + throw new RuntimeException("Different string is returned from intern() for empty string"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java new file mode 100644 index 00000000000..6e71c88dcea --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test options that are incompatible with use of shared strings + * Also test mismatch in oops encoding between dump time and run time + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires (vm.gc=="null") + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloString + * @run main IncompatibleOptions + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; + +public class IncompatibleOptions { + static final String COOPS_DUMP_WARNING = + "Cannot dump shared archive when UseCompressedOops or UseCompressedClassPointers is off"; + static final String COOPS_EXEC_WARNING = + "UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces"; + static final String GC_WARNING = + "Archived java heap is not supported"; + static final String OBJ_ALIGNMENT_MISMATCH = + "The shared archive file's ObjectAlignmentInBytes of .* does not equal the current ObjectAlignmentInBytes of"; + static final String COMPACT_STRING_MISMATCH = + "The shared archive file's CompactStrings setting .* does not equal the current CompactStrings setting"; + + static String appJar; + + public static void main(String[] args) throws Exception { + appJar = JarBuilder.build("IncompatibleOptions", "HelloString"); + + // Uncompressed OOPs + testDump(1, "-XX:+UseG1GC", "-XX:-UseCompressedOops", COOPS_DUMP_WARNING, true); + + // incompatible GCs + testDump(2, "-XX:+UseParallelGC", "", GC_WARNING, false); + testDump(3, "-XX:+UseSerialGC", "", GC_WARNING, false); + testDump(4, "-XX:+UseConcMarkSweepGC", "", GC_WARNING, false); + + // ======= archive with compressed oops, run w/o + testDump(5, "-XX:+UseG1GC", "-XX:+UseCompressedOops", null, false); + testExec(5, "-XX:+UseG1GC", "-XX:-UseCompressedOops", + COOPS_EXEC_WARNING, true); + + // NOTE: No warning is displayed, by design + // Still run, to ensure no crash or exception + testExec(6, "-XX:+UseParallelGC", "", "", false); + testExec(7, "-XX:+UseSerialGC", "", "", false); + testExec(8, "-XX:+UseConcMarkSweepGC", "", "", false); + + // Test various oops encodings, by varying ObjectAlignmentInBytes and heap sizes + testDump(9, "-XX:+UseG1GC", "-XX:ObjectAlignmentInBytes=8", null, false); + testExec(9, "-XX:+UseG1GC", "-XX:ObjectAlignmentInBytes=16", + OBJ_ALIGNMENT_MISMATCH, true); + + // See JDK-8081416 - Oops encoding mismatch with shared strings + // produces unclear or incorrect warning + // Correct the test case once the above is fixed + // @ignore JDK-8081416 - for tracking purposes + // for now, run test as is until the proper behavior is determined + testDump(10, "-XX:+UseG1GC", "-Xmx1g", null, false); + testExec(10, "-XX:+UseG1GC", "-Xmx32g", null, true); + + // CompactStrings must match between dump time and run time + testDump(11, "-XX:+UseG1GC", "-XX:-CompactStrings", null, false); + testExec(11, "-XX:+UseG1GC", "-XX:+CompactStrings", + COMPACT_STRING_MISMATCH, true); + testDump(12, "-XX:+UseG1GC", "-XX:+CompactStrings", null, false); + testExec(12, "-XX:+UseG1GC", "-XX:-CompactStrings", + COMPACT_STRING_MISMATCH, true); + } + + static void testDump(int testCaseNr, String collectorOption, String extraOption, + String expectedWarning, boolean expectedToFail) throws Exception { + + System.out.println("Testcase: " + testCaseNr); + OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello"), + "-XX:+UseCompressedOops", + collectorOption, + "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("SharedStringsBasic.txt"), + extraOption); + + if (expectedWarning != null) + output.shouldContain(expectedWarning); + + if (expectedToFail) { + Asserts.assertNE(output.getExitValue(), 0, + "JVM is expected to fail, but did not"); + } + } + + static void testExec(int testCaseNr, String collectorOption, String extraOption, + String expectedWarning, boolean expectedToFail) throws Exception { + + OutputAnalyzer output; + System.out.println("Testcase: " + testCaseNr); + + // needed, otherwise system considers empty extra option as a + // main class param, and fails with "Could not find or load main class" + if (!extraOption.isEmpty()) { + output = TestCommon.exec(appJar, "-XX:+UseCompressedOops", + collectorOption, extraOption, "HelloString"); + } else { + output = TestCommon.exec(appJar, "-XX:+UseCompressedOops", + collectorOption, "HelloString"); + } + + if (expectedWarning != null) + output.shouldMatch(expectedWarning); + + if (expectedToFail) + Asserts.assertNE(output.getExitValue(), 0); + else + SharedStringsUtils.checkExec(output); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java new file mode 100644 index 00000000000..d64643257d0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test shared strings together with string intern operation + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @compile InternStringTest.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main InternSharedString + */ + +public class InternSharedString { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJarAndWhiteBox("InternStringTest"); + + SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("InternStringTest"), + "ExtraSharedInput.txt"); + + String[] extraMatches = new String[] { + InternStringTest.passed_output1, + InternStringTest.passed_output2, + InternStringTest.passed_output3 }; + + SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatches, "InternStringTest"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java new file mode 100644 index 00000000000..cc95cdf6cbe --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class InternStringTest { + public static String passed_output1 = "Found shared string."; + public static String passed_output2 = "Shared strings are equal."; + public static String passed_output3 = "Found shared string containing latin1 supplement chars."; + public static String passed_output4 = "Found shared string containing non-western chars."; + public static final String latin1Sup = "XXXX \u00a3 YYYY"; // \u00a3 = The pound sign + public static final String nonWestern = "XXXX \u5678 YYYY"; // \u5678 = Unicode Han Character 'ton (metric or English)' + + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + + // All string literals are shared. + String shared1 = "LiveOak"; + String interned1 = shared1.intern(); + if (wb.areSharedStringsIgnored() || wb.isShared(interned1)) { + System.out.println(passed_output1); + } else { + throw new RuntimeException("Failed: String is not shared."); + } + + // Test 2: shared_string1.intern() == shared_string2.intern() + String shared2 = "LiveOak"; + String interned2 = shared2.intern(); + if (interned1 == interned2) { + System.out.println(passed_output2); + } else { + throw new RuntimeException("Not equal!"); + } + + // Test 3: interned strings with a char in latin1 supplement block [\u0080-\u00ff] + { + String a = "X" + latin1Sup.substring(1); + String b = a.intern(); + + if (wb.areSharedStringsIgnored() || wb.isShared(b)) { + System.out.println(passed_output3); + } else { + throw new RuntimeException("Failed: expected shared string with latin1-supplement chars."); + } + } + + // Test 5: interned strings with non-western characters + { + String a = "X" + nonWestern.substring(1); + String b = a.intern(); + if (wb.areSharedStringsIgnored() || wb.isShared(b)) { + System.out.println(passed_output4); + } else { + throw new RuntimeException("Failed: expected shared string with non-western chars."); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java new file mode 100644 index 00000000000..b781f345eed --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Check most common errors in file format + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloString + * @run main InvalidFileFormat + */ + +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; + +// Checking most common error use cases +// This file is not an exhastive test of various shared data file corruption +// Note on usability intent: the shared data file is created and handled by +// the previledge person in the server environment. +public class InvalidFileFormat { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJar("HelloString"); + + test("NonExistentFile.txt", "Unable to get hashtable dump file size"); + test("InvalidHeader.txt", "wrong version of hashtable dump file"); + test("InvalidVersion.txt", "wrong version of hashtable dump file"); + test("CorruptDataLine.txt", "Unknown data type. Corrupted at line 2"); + test("InvalidSymbol.txt", "Unexpected character. Corrupted at line 2"); + test("InvalidSymbolFormat.txt", "Unrecognized format. Corrupted at line 9"); + test("OverflowPrefix.txt", "Num overflow. Corrupted at line 4"); + test("UnrecognizedPrefix.txt", "Unrecognized format. Corrupted at line 5"); + test("TruncatedString.txt", "Truncated. Corrupted at line 3"); + } + + private static void + test(String dataFileName, String expectedWarning) throws Exception { + System.out.println("Filename for testcase: " + dataFileName); + + OutputAnalyzer out = SharedStringsUtils.dumpWithoutChecks(TestCommon.list("HelloString"), + "invalidFormat" + File.separator + dataFileName); + + if (!TestCommon.isUnableToMap(out)) + out.shouldContain(expectedWarning).shouldHaveExitValue(1); + } + +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java new file mode 100644 index 00000000000..a234da4a9df --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Basic shared string test with large pages + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloString + * @run main LargePages + */ +public class LargePages { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJar("HelloString"); + + SharedStringsUtils.dump(TestCommon.list("HelloString"), + "SharedStringsBasic.txt", "-XX:+UseLargePages"); + SharedStringsUtils.runWithArchive("HelloString", "-XX:+UseLargePages"); + + SharedStringsUtils.dump(TestCommon.list("HelloString"), + "SharedStringsBasic.txt", + "-XX:+UseLargePages", "-XX:+UseLargePagesInMetaspace"); + SharedStringsUtils.runWithArchive("HelloString", + "-XX:+UseLargePages", "-XX:+UseLargePagesInMetaspace"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java new file mode 100644 index 00000000000..921361ec96a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Test locking on shared strings + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @compile LockStringTest.java LockStringValueTest.java + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main LockSharedStrings + */ + +public class LockSharedStrings { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJarAndWhiteBox("LockStringTest", "LockStringValueTest"); + + SharedStringsUtils.dumpWithWhiteBox( + TestCommon.list("LockStringTest", "LockStringValueTest"), + "ExtraSharedInput.txt"); + + String[] extraMatch = new String[] {"LockStringTest: PASS"}; + SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatch, "LockStringTest"); + + extraMatch = new String[] {"LockStringValueTest: PASS"}; + SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatch, "LockStringValueTest", + "--add-opens=java.base/java.lang=ALL-UNNAMED"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java new file mode 100644 index 00000000000..daecd0a93f8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class LockStringTest extends Thread { + static String lock = "StringLock"; + static boolean done = false; + + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.areSharedStringsIgnored()) { + System.out.println("The shared strings are ignored"); + System.out.println("LockStringTest: PASS"); + return; + } + + if (!wb.isShared(lock)) { + throw new RuntimeException("Failed: String is not shared."); + } + + new LockStringTest().start(); + + synchronized(lock) { + while (!done) { + lock.wait(); + } + } + System.gc(); + System.out.println("LockStringTest: PASS"); + } + + public void run() { + String shared = "LiveOak"; + synchronized (lock) { + for (int i = 0; i < 100; i++) { + new String(shared); + System.gc(); + try { + sleep(5); + } catch (InterruptedException e) {} + } + done = true; + lock.notify(); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java new file mode 100644 index 00000000000..4d1e2c1a808 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.lang.reflect.*; +import sun.hotspot.WhiteBox; + +/* + * Lock the 'value' field of a known shared string, java.lang.Object + */ +public class LockStringValueTest { + public static void main(String args[]) { + String s = "LiveOak"; + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (wb.areSharedStringsIgnored()) { + System.out.println("The shared strings are ignored"); + System.out.println("LockStringValueTest: PASS"); + return; + } + + if (!wb.isShared(s)) { + throw new RuntimeException("LockStringValueTest Failed: String is not shared."); + } + + Class c = s.getClass(); + try { + Field f = c.getDeclaredField("value"); + f.setAccessible(true); + Object v = f.get(s); + lock(v); + } catch (NoSuchFieldException nfe) { + } catch (IllegalAccessException iae) {} + } + + public static void lock(Object o) { + synchronized (o) { + System.out.println("LockStringValueTest: PASS"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java new file mode 100644 index 00000000000..7d9623aa4b7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Basic test for shared strings + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloString + * @run main SharedStringsBasic + */ +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +// This test does not use SharedStringsUtils intentionally: +// - in order to demonstrate the basic use of the functionality +// - to provide sanity check and catch potential problems in the utils +public class SharedStringsBasic { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.build("SharedStringsBasic", "HelloString"); + + String sharedArchiveConfigFile = + TestCommon.getSourceFile("SharedStringsBasic.txt").toString(); + + ProcessBuilder dumpPb = ProcessTools.createJavaProcessBuilder(true, + "-XX:+UseAppCDS", + "-XX:+UseCompressedOops", + "-XX:+UseG1GC", + "-cp", appJar, + "-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile, + "-XX:SharedArchiveFile=./SharedStringsBasic.jsa", + "-Xshare:dump", + "-Xlog:cds,cds+hashtables"); + + TestCommon.executeAndLog(dumpPb, "dump") + .shouldContain("Shared string table stats") + .shouldHaveExitValue(0); + + ProcessBuilder runPb = ProcessTools.createJavaProcessBuilder(true, + "-XX:+UseAppCDS", + "-XX:+UseCompressedOops", + "-XX:+UseG1GC", + "-cp", appJar, + "-XX:SharedArchiveFile=./SharedStringsBasic.jsa", + "-Xshare:auto", + "-showversion", + "HelloString"); + + TestCommon.executeAndLog(runPb, "run").shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt new file mode 100644 index 00000000000..a43dabaaea9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt @@ -0,0 +1,60 @@ +VERSION: 1.0 +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +@SECTION: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java new file mode 100644 index 00000000000..93209142709 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Basic plus test for shared strings + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build HelloStringPlus sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main SharedStringsBasicPlus + */ + +public class SharedStringsBasicPlus { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJarAndWhiteBox("HelloStringPlus"); + + SharedStringsUtils.dumpWithWhiteBox( TestCommon.list("HelloStringPlus"), + "SharedStringsBasic.txt"); + + SharedStringsUtils.runWithArchiveAndWhiteBox("HelloStringPlus"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java new file mode 100644 index 00000000000..db531617a88 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017, 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 + * @summary Write a lots of shared strings. + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/hotspot/jtreg/runtime/appcds /test/lib + * @modules jdk.jartool/sun.tools.jar + * @build HelloString + * @run main SharedStringsStress + */ +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class SharedStringsStress { + public static void main(String[] args) throws Exception { + String appJar = JarBuilder.build("SharedStringsStress", "HelloString"); + + String sharedArchiveConfigFile = System.getProperty("user.dir") + File.separator + "SharedStringsStress_gen.txt"; + try (FileOutputStream fos = new FileOutputStream(sharedArchiveConfigFile)) { + PrintWriter out = new PrintWriter(new OutputStreamWriter(fos)); + out.println("VERSION: 1.0"); + out.println("@SECTION: String"); + out.println("31: shared_test_string_unique_14325"); + for (int i=0; i<100000; i++) { + String s = "generated_string " + i; + out.println(s.length() + ": " + s); + } + out.close(); + } + + // Set NewSize to 8m due to dumping could fail in hs-tier6 testing with + // the vm options: -XX:+UnlockCommercialFeatures -XX:+UseDeterministicG1GC + // resulting in vm initialization error: + // "GC triggered before VM initialization completed. Try increasing NewSize, current value 1331K." + OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list("HelloString"), "-XX:NewSize=8m", + "-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile); + TestCommon.checkDump(dumpOutput); + OutputAnalyzer execOutput = TestCommon.exec(appJar, "HelloString"); + TestCommon.checkExec(execOutput); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java new file mode 100644 index 00000000000..5121ae5924e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.process.OutputAnalyzer; + +// A helper/utility class for testing shared strings +public class SharedStringsUtils { + public static final String TEST_JAR_NAME = "test"; + public static final String TEST_JAR_NAME_FULL = "test.jar"; + public static final String WHITEBOX_JAR_NAME = "whitebox"; + + public static String getWbParam() { + return "-Xbootclasspath/a:" + TestCommon.getTestJar(WHITEBOX_JAR_NAME + ".jar"); + } + + // build the test jar + public static void buildJar(String... classes) throws Exception { + JarBuilder.build(TEST_JAR_NAME, classes); + } + + // build the test jar and a whitebox jar + public static void buildJarAndWhiteBox(String... classes) throws Exception { + JarBuilder.build(true, WHITEBOX_JAR_NAME, "sun/hotspot/WhiteBox"); + buildJar(classes); + } + + // execute the "dump" operation, but do not check the output + public static OutputAnalyzer dumpWithoutChecks(String appClasses[], + String sharedDataFile, String... extraOptions) throws Exception { + + String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); + String[] args = + TestCommon.concat(extraOptions, "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:SharedArchiveConfigFile=" + + TestCommon.getSourceFile(sharedDataFile)); + + return TestCommon.dump(appJar, appClasses, args); + } + + // execute the dump operation and check the output + public static OutputAnalyzer dump(String appClasses[], + String sharedDataFile, String... extraOptions) throws Exception { + OutputAnalyzer output = dumpWithoutChecks(appClasses, sharedDataFile, extraOptions); + checkDump(output); + return output; + } + + public static OutputAnalyzer dumpWithWhiteBox(String appClasses[], + String sharedDataFile, String... extraOptions) throws Exception { + return dump(appClasses, sharedDataFile, + TestCommon.concat(extraOptions, getWbParam()) ); + } + + // execute/run test with shared archive + public static OutputAnalyzer runWithArchiveAuto(String className, + String... extraOptions) throws Exception { + + String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); + String[] args = TestCommon.concat(extraOptions, + "-cp", appJar, "-XX:+UseCompressedOops", "-XX:+UseG1GC", className); + + OutputAnalyzer output = TestCommon.execAuto(args); + checkExecAuto(output); + return output; + } + + public static OutputAnalyzer runWithArchive(String className, + String... extraOptions) throws Exception { + + return runWithArchive(new String[0], className, extraOptions); + } + + public static OutputAnalyzer runWithArchive(String[] extraMatches, + String className, String... extraOptions) throws Exception { + + String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL); + String[] args = TestCommon.concat(extraOptions, + "-XX:+UseCompressedOops", "-XX:+UseG1GC", className); + + OutputAnalyzer output = TestCommon.exec(appJar, args); + checkExec(output, extraMatches); + return output; + } + + + // execute/run test with shared archive and white box + public static OutputAnalyzer runWithArchiveAndWhiteBox(String className, + String... extraOptions) throws Exception { + + return runWithArchive(className, + TestCommon.concat(extraOptions, getWbParam(), + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI") ); + } + + public static OutputAnalyzer runWithArchiveAndWhiteBox(String[] extraMatches, + String className, String... extraOptions) throws Exception { + + return runWithArchive(extraMatches, className, + TestCommon.concat(extraOptions, getWbParam(), + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI") ); + } + + + public static void checkDump(OutputAnalyzer output) throws Exception { + output.shouldContain("Shared string table stats"); + TestCommon.checkDump(output); + } + + public static void checkExec(OutputAnalyzer output) throws Exception { + TestCommon.checkExec(output, new String[0]); + } + + public static void checkExecAuto(OutputAnalyzer output) throws Exception { + CDSOptions opts = (new CDSOptions()).setXShareMode("auto"); + TestCommon.checkExec(output, opts); + } + + public static void checkExec(OutputAnalyzer output, String[] extraMatches) throws Exception { + TestCommon.checkExec(output, extraMatches); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java new file mode 100644 index 00000000000..3bd8eeb6152 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class SharedStringsWb { + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + String s = "shared_test_string_unique_14325"; + s = s.intern(); + if (wb.areSharedStringsIgnored() || wb.isShared(s)) { + System.out.println("Found shared string."); + } else { + throw new RuntimeException("String is not shared."); + } + } +} + + diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java new file mode 100644 index 00000000000..fe3f05350a7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @summary White box test for shared strings + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * jdk.jartool/sun.tools.jar + * @build sun.hotspot.WhiteBox SharedStringsWb + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main SharedStringsWbTest + */ + +import java.io.*; +import sun.hotspot.WhiteBox; + +public class SharedStringsWbTest { + public static void main(String[] args) throws Exception { + SharedStringsUtils.buildJarAndWhiteBox("SharedStringsWb"); + + SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("SharedStringsWb"), + "SharedStringsBasic.txt"); + + SharedStringsUtils.runWithArchiveAndWhiteBox("SharedStringsWb"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java new file mode 100644 index 00000000000..c96a75da7e1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @summary Regression test for JDK-8098821 + * @bug 8098821 + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /test/lib /test/hotspot/jtreg/runtime/appcds + * @modules java.base/jdk.internal.misc + * @modules java.management + * @run main SysDictCrash + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class SysDictCrash { + public static void main(String[] args) throws Exception { + // SharedBaseAddress=0 puts the archive at a very high address on solaris, + // which provokes the crash. + ProcessBuilder dumpPb = ProcessTools.createJavaProcessBuilder(true, + "-XX:+UseG1GC", "-XX:MaxRAMPercentage=12.5", + "-XX:+UseAppCDS", + "-cp", ".", + "-XX:SharedBaseAddress=0", "-XX:SharedArchiveFile=./SysDictCrash.jsa", + "-Xshare:dump", + "-showversion", "-Xlog:cds,cds+hashtables"); + + TestCommon.checkDump(TestCommon.executeAndLog(dumpPb, "dump")); + + ProcessBuilder runPb = ProcessTools.createJavaProcessBuilder(true, + "-XX:+UseG1GC", "-XX:MaxRAMPercentage=12.5", + "-XX:+UseAppCDS", + "-XX:SharedArchiveFile=./SysDictCrash.jsa", + "-Xshare:on", + "-version"); + + TestCommon.checkExec(TestCommon.executeAndLog(runPb, "exec")); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt new file mode 100644 index 00000000000..fe5fb328c1e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt @@ -0,0 +1,60 @@ +VERSION: 1.0 +SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +#DATATYPE: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt new file mode 100644 index 00000000000..d19db9e4999 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt @@ -0,0 +1,60 @@ +VERSION: 1.0 +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +#DATATYPE: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt new file mode 100644 index 00000000000..02ff35b7246 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt @@ -0,0 +1,60 @@ +Garbage Header x0935#%0sl +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +#DATATYPE: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt new file mode 100644 index 00000000000..104785cc934 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt @@ -0,0 +1,6 @@ +VERSION: 1.0 +@SECTION: String +31: shred_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 + diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt new file mode 100644 index 00000000000..bf4fe475ad7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt @@ -0,0 +1,60 @@ +VERSION: 1.0 +@SECTION: String +0: +5:: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +#DATATYPE: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt new file mode 100644 index 00000000000..7da06b825eb --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt @@ -0,0 +1,12 @@ +VERSION: 1.0 +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +8: cyrillic +@SECTION: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMet%%%hod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt new file mode 100644 index 00000000000..affa466d4e9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt @@ -0,0 +1,11 @@ +VERSION: 1.0 +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +@SECTION: Symbol +41: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt new file mode 100644 index 00000000000..2d917344c6e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt @@ -0,0 +1,60 @@ +VERSION: 0.0 +@SECTION: String +0: +5: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +1: * +1: - +1: . +1: / +1: : +1: C +1: I +1: J +1: U +1: Z +1: _ +8: segments +1: | +5: cp850 +5: cp852 +5: cp855 +5: cp857 +5: cp858 +5: cp862 +5: cp866 +11: ISO_8859_13 +11: ISO_8859_15 +5: cp874 +47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER +7: CHECKED +3: zip +10: waitStatus +33: java.lang.invoke.MethodHandleImpl +7: .jimage +5: cp912 +5: cp914 +5: cp915 +5: cp920 +5: cp923 +5: cp936 +5: euccn +5: eucjp +11: permissions +5: euckr +6: SIGNAL +5: cp737 +17: java.library.path +5: cp775 +13: classValueMap +4: utf8 +9: PROPAGATE +9: baseCount +7: cskoi8r +8: cyrillic +#DATATYPE: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt new file mode 100644 index 00000000000..8da872dca23 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt @@ -0,0 +1,11 @@ +VERSION: 1.0 +@SECTION: String +0: +2147483648: cp819 +31: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +@SECTION: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt new file mode 100644 index 00000000000..849f8b5ddfd --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt @@ -0,0 +1,10 @@ +VERSION: 1.0 +@SECTION: String +2147483647: s +5: cp819 +31: shared_test_string_intern_12345 +7: test123 +@SECTION: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt new file mode 100644 index 00000000000..e979c3d8910 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt @@ -0,0 +1,11 @@ +VERSION: 1.0 +@SECTION: String +0: +5: cp819 +3E: shared_test_string_unique_14325 +31: shared_test_string_intern_12345 +7: test123 +@SECTION: Symbol +41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V +10 -1: linkMethod +20 -1: isAlphaNumericString diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java new file mode 100644 index 00000000000..d6ac26e960a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import java.util.*; + +// This is a test case executed by DumpClassList.java to load classes +// from various places to ensure that they are not written to the class list. +public class ArrayListTest { + public static void main(String args[]) throws Exception { + // The following lambda usage should generate various classes like + // java.lang.invoke.LambdaForm$MH/1146743572. All of them should be excluded from + // the class list. + List<String> a = new ArrayList<>(); + a.add("hello world."); + a.forEach(str -> System.out.println(str)); + + System.out.println(Class.forName("java.lang.NewClass")); // should be excluded from the class list. + System.out.println(Class.forName("boot.append.Foo")); // should be excluded from the class list. + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java new file mode 100644 index 00000000000..79cd2805ee3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class BootClassPathAppendHelper { + public static void main(String[] args) throws ClassNotFoundException { + Class cls = Class.forName("Hello"); + + if (cls == null) { + throw new java.lang.RuntimeException("Cannot find Hello.class"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (!wb.isSharedClass(cls)) { + System.out.println("Hello.class is not in shared space as expected."); + } else { + throw new java.lang.RuntimeException("Hello.class shouldn't be in shared space."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java b/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java new file mode 100644 index 00000000000..86201cd4e34 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +package sealed.pkg; + +public class C1 { +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java b/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java new file mode 100644 index 00000000000..ad0026fbc53 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +package pkg; + +public class C2 { +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java new file mode 100644 index 00000000000..6e919ad5bce --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class CheckIfShared { + public static void main(String args[]) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + if ("true".equals(args[0])) { + if (!wb.isSharedClass(CheckIfShared.class)) { + throw new RuntimeException("wb.isSharedClass(CheckIfShared.class) should be true"); + } + } else { + if (wb.isSharedClass(CheckIfShared.class)) { + throw new RuntimeException("wb.isSharedClass(CheckIfShared.class) should be false"); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java new file mode 100644 index 00000000000..8a3684e15a6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class Child extends Super {} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java new file mode 100644 index 00000000000..5006870cd6d --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class CpAttr1 { + public static void main(String args[]) { + System.out.println("2"); CpAttr2.doit(); // Only the version of this class defined in CpAttr2.java will not throw exception. + System.out.println("3"); CpAttr3.doit(); // Only the version of this class defined in CpAttr3.java will not throw exception. + System.out.println("4"); CpAttr4.doit(); // Only the version of this class defined in CpAttr4.java will not throw exception. + System.out.println("5"); CpAttr5.doit(); // Only the version of this class defined in CpAttr5.java will not throw exception. + System.out.println("Test passed"); + } +} + +class CpAttr2 { static void doit() {throw new RuntimeException("");} } +class CpAttr3 { static void doit() {throw new RuntimeException("");} } +class CpAttr4 { static void doit() {throw new RuntimeException("");} } +class CpAttr5 { static void doit() {throw new RuntimeException("");} } diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java new file mode 100644 index 00000000000..4777e1caf9e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +class CpAttr2 { static void doit() {} } diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java new file mode 100644 index 00000000000..96b19d0424c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +class CpAttr2 { static void doit() {throw new RuntimeException("");} } +class CpAttr3 { static void doit() {} } diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java new file mode 100644 index 00000000000..9711148f877 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +class CpAttr2 { static void doit() {throw new RuntimeException("");} } +class CpAttr3 { static void doit() {throw new RuntimeException("");} } +class CpAttr4 { static void doit() {} } +class CpAttr5 { static void doit() {throw new RuntimeException("");} } diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java new file mode 100644 index 00000000000..94812653c48 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +class CpAttr5 { static void doit() {} } diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java new file mode 100644 index 00000000000..56ffa75bd29 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.lang.*; +import java.lang.reflect.*; +import sun.hotspot.WhiteBox; + +public class DummyClassHelper { + public static void main(String[] args) throws Exception { + String[] classNames = {args[0], args[1]}; + Class cls = null; + if (args.length == 2) { + for (int i = 0; i < classNames.length; i++) { + Method m = null; + cls = Class.forName(classNames[i]); + try { + m = cls.getMethod("thisClassIsDummy"); + throw new java.lang.RuntimeException(classNames[i] + + " should be loaded from the jimage and should not have the thisClassIsDummy() method."); + } catch(NoSuchMethodException ex) { + System.out.println(ex.toString()); + } + } + } else { + WhiteBox wb = WhiteBox.getWhiteBox(); + for (int i = 0; i < classNames.length; i++) { + cls = Class.forName(classNames[i]); + if (!wb.isSharedClass(cls)) { + System.out.println(classNames[i] + ".class" + " is not in shared space as expected."); + } else { + throw new java.lang.RuntimeException(classNames[i] + + ".class shouldn't be in shared space."); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java new file mode 100644 index 00000000000..86805214617 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.lang.*; +import java.lang.reflect.*; +import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.SharedSecrets; + +class EmptyClassHelper { + static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + static final String USE_APP = "useAppLoader"; + public static void main(String[] args) throws Exception { + Class cls = null; + Method m = null; + ClassLoader appLoader = ClassLoader.getSystemClassLoader(); + String className = "com.sun.tools.javac.Main"; + if (args[0].equals(USE_APP)) { + cls = appLoader.loadClass(className); + System.out.println("appLoader loaded class"); + try { + m = cls.getMethod("main", String[].class); + System.out.println("appLoader found method main"); + } catch(NoSuchMethodException ex) { + System.out.println(ex.toString()); + } + } else { + cls = jla.findBootstrapClassOrNull(appLoader, className); + System.out.println("bootLoader loaded class"); + System.out.println("cls = " + cls); + try { + m = cls.getMethod("main", String[].class); + System.out.println("bootLoader found method main"); + } catch(NoSuchMethodException ex) { + System.out.println(ex.toString()); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java new file mode 100644 index 00000000000..70808d8af64 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +public class FieldAnnotationsApp { + @MyAnnotation(name="myField1", value="myValue1") + public String myField1 = null; + + @MyAnnotation(name="myField2", value="myValue2") + public String myField2 = null; + + public static void main(String args[]) throws Exception { + for (int i=1; i<=2; i++) { + Field field = FieldAnnotationsApp.class.getField("myField" + i); + Annotation[] annotations = field.getDeclaredAnnotations(); + + for (Annotation anno : annotations){ + if (anno instanceof MyAnnotation){ + MyAnnotation myAnno = (MyAnnotation) anno; + String name = myAnno.name(); + String value = myAnno.value(); + + System.out.println("Field : " + field.getName()); + System.out.println(" myAnno.name : " + name); + System.out.println(" myAnno.value: " + value); + + if (!(name.equals("myField" + i) && value.equals("myValue" + i))) { + throw new Exception("Unexpected annotation values: " + i + " = " + value); + } + } + } + } + System.out.println("Field annotations are OK."); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java new file mode 100644 index 00000000000..b313af1ccc7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class ForNameTest { + public static void main(String[] args) throws Throwable { + // Hello is on the bootclasspath. The defining classloader is + // the NULL classloader. See AppCDSClassLoaderTest. + Class c = Class.forName("Hello"); + ClassLoader cl = c.getClassLoader(); + if (cl != null) { + throw new RuntimeException( + "Test Failed. Wrong classloader is used. Expect the NULL classloader."); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (!wb.isSharedClass(c)) { + System.out.println("As expected, Hello.class is not in shared space."); + } else { + throw new java.lang.RuntimeException("Hello.class shouldn't be in shared space."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java new file mode 100644 index 00000000000..174e97ac205 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class Greet { + + public String Greeting() { + return new String(", how are you?"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java new file mode 100644 index 00000000000..dc134771ba5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class Hello { + public static void main(String args[]) { + System.out.println("Hello World"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java new file mode 100644 index 00000000000..bd11763271a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class HelloExt { + public static void main(String[] args) throws Throwable { + + String className = "org.omg.CORBA.ORB"; + Class cls = Class.forName(className); + + ClassLoader loader = cls.getClassLoader(); + if (loader != ClassLoader.getPlatformClassLoader()) { + throw new java.lang.RuntimeException(className + " should be load by PlatformClassLoader but it is loaded by " + loader); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(cls)) { + System.out.println("As expected, " + className + " is in shared space."); + } else { + throw new java.lang.RuntimeException(className + " is not in shared space."); + } + + className = "[Ljava.lang.Comparable;"; + cls = Class.forName(className); + loader = cls.getClassLoader(); + if (loader != null) { + throw new java.lang.RuntimeException(className + " should be load by the NULL class loader but it is loaded by " + loader); + } + + if (wb.isSharedClass(cls)) { + System.out.println("As expected, " + className + " is in shared space."); + } else { + throw new java.lang.RuntimeException(className + " is not in shared space."); + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java new file mode 100644 index 00000000000..a4dd73f390b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class HelloExtApp { + public static void main(String args[]) { + System.out.println("Hello World Ext: " + HelloExtExt.class.getProtectionDomain()); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java new file mode 100644 index 00000000000..d74901c782a --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class HelloExtExt { + +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java new file mode 100644 index 00000000000..dee1b239177 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class HelloMore { + public static void main(String args[]) { + Hello.main(args); + System.out.println("Hello World ... More"); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java new file mode 100644 index 00000000000..92c0d9e02fa --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class HelloWB { + public static void main(String[] args) throws Throwable { + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(HelloWB.class)) { + System.out.println("As expected, HelloWB.class is in shared space."); + } else { + throw new java.lang.RuntimeException("HelloWB.class should be in shared space."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java new file mode 100644 index 00000000000..8250c323a8b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class Hi extends Greet { + public static void main(String args[]) { + Greet g = new Greet(); + MyClass.doit(g.Greeting()); + } + public static class MyClass { + public static void doit(String greeting) { + System.out.println("Hi" + greeting); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm new file mode 100644 index 00000000000..90d31f9fbf6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class Iloadw + version 51: 0 +{ + public static Method run:"()I" + stack 1 locals 400 + { + iconst_0; + istore_w 300; + iinc_w 300,1; + iload_w 300; + ireturn; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java new file mode 100644 index 00000000000..315e00a2b38 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +public class IloadwMain { + public static void main(String args[]) { + int result = Iloadw.run(); + if (result != 1) { + throw new RuntimeException( + "Failed. Result is " + result + ", expect 1."); + } else { + System.out.println("Passed."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java new file mode 100644 index 00000000000..f9358a1e28f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class JimageClassPackage { + public static void main(String args[]) throws Throwable { + // Test Package for boot/app/ext module classes from the "modules" jimage. + // The following classes are archived. See runtime/AppCDS/Package.java. + // java.util.Dictionary (testcase 0), + // sun.tools.javac.Main (testcase 1), + // jdk.nio.zipfs.ZipInfo (testcase 2), + // java.net.URL (testcase 3), + // sun.rmi.rmic.Main (testcase 4), + // com.sun.jndi.dns.DnsName (testcase 5) + String testcases[][] = + {{"Loading shared boot module class first", "java.util", + "java.util.Dictionary", "java.util.ServiceConfigurationError"}, + + {"Loading shared app module class first", "sun.tools.javac", + "sun.tools.javac.Main", "sun.tools.javac.BatchParser"}, + + {"Loading shared ext module class first", "jdk.nio.zipfs", + "jdk.nio.zipfs.ZipInfo", "jdk.nio.zipfs.ZipPath"}, + + {"Loading non-shared boot module class first", "java.net", + "java.net.HttpCookie", "java.net.URL"}, + + {"Loading non-shared app module class first", "sun.rmi.rmic", + "sun.rmi.rmic.RMIGenerator", "sun.rmi.rmic.Main"}, + + {"Loading non-shared ext module class first", "com.sun.jndi.dns", + "com.sun.jndi.dns.Resolver", "com.sun.jndi.dns.DnsName"}}; + + JimageClassPackage test = new JimageClassPackage(); + for (int i = 0; i < testcases.length; i++) { + System.out.println("Testcase " + i + ": " + testcases[i][0]); + test.testPackage(testcases[i][1], testcases[i][2], testcases[i][3]); + } + } + + private void testPackage (String pkg, + String shared, + String nonShared) throws Throwable { + Class c1 = Class.forName(shared); + ClassLoader cl = c1.getClassLoader(); + Package pkg_from_loader; + if (cl != null) { + pkg_from_loader = cl.getDefinedPackage(pkg); + } else { + pkg_from_loader = Package.getPackage(pkg); + } + + Package pkg_from_shared_class = c1.getPackage(); + + Class c2 = Class.forName(nonShared); + Package pkg_from_nonshared_class = c2.getPackage(); + + if (pkg_from_loader != null && + pkg_from_shared_class != null && + pkg_from_loader == pkg_from_shared_class && + pkg_from_shared_class == pkg_from_nonshared_class && + pkg_from_shared_class.getName().equals(pkg)) { + System.out.println("Expected package: " + pkg_from_shared_class.toString()); + } else { + System.out.println("Unexpected package" + pkg_from_shared_class); + System.exit(1); + } + if (pkg_from_shared_class.isSealed()) { + System.out.println("Package is sealed"); + } else { + System.out.println("Package is not sealed"); + System.exit(1); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java new file mode 100644 index 00000000000..e47aaaa8a36 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class JimageClassProtDomain { + public static void main(String args[]) throws Throwable { + // Test ProtectionDomain for boot/app/ext module classes from the "modules" jimage. + // The following classes are archived. See runtime/AppCDS/ProtectionDomain.java. + // java.util.Dictionary (testcase 0), + // sun.tools.javac.Main (testcase 1), + // jdk.nio.zipfs.ZipInfo (testcase 2), + // java.net.URL (testcase 3), + // sun.rmi.rmic.Main (testcase 4), + // com.sun.jndi.dns.DnsName (testcase 5) + String testcases[][] = + {{"Loading shared boot module class first", + "java.util.Dictionary", "java.util.ServiceConfigurationError"}, + + {"Loading shared app module class first", + "sun.tools.javac.Main", "sun.tools.javac.BatchParser"}, + + {"Loading shared ext module class first", + "jdk.nio.zipfs.ZipInfo", "jdk.nio.zipfs.ZipPath"}, + + {"Loading non-shared boot module class first", + "java.net.HttpCookie", "java.net.URL"}, + + {"Loading non-shared app module class first", + "sun.rmi.rmic.RMIGenerator", "sun.rmi.rmic.Main"}, + + {"Loading non-shared ext module class first", + "com.sun.jndi.dns.Resolver", "com.sun.jndi.dns.DnsName"}}; + for (int i = 0; i < testcases.length; i++) { + System.out.println("Testcase " + i + ": " + testcases[i][0]); + JimageClassProtDomain.testProtectionDomain(testcases[i][1], testcases[i][2]); + } + } + + private static void testProtectionDomain(String shared, String nonShared) + throws Throwable { + Class c1 = Class.forName(shared); + Class c2 = Class.forName(nonShared); + if (c1.getProtectionDomain() != c2.getProtectionDomain()) { + System.out.println("Failed: Protection Domains do not match!"); + System.out.println(c1.getProtectionDomain()); + System.out.println(c1.getProtectionDomain().getCodeSource()); + System.out.println(c2.getProtectionDomain()); + System.out.println(c2.getProtectionDomain().getCodeSource()); + System.exit(1); + } else { + System.out.println("Passed: Protection Domains match."); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java new file mode 100644 index 00000000000..e0c0ea55fb6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +public class JvmtiApp { + static Class forname() { + try { + return Class.forName("Hello"); + } catch (Throwable t) { + return null; + } + } + + static void failed(String msg) { + System.out.println("TEST FAILED: " + msg); + System.exit(1); + } + + // See ../JvmtiAddPath.java for how the classpaths are configured. + public static void main(String args[]) { + if (args[0].equals("noadd")) { + if (forname() != null) { + failed("Hello class was loaded unexpectedly"); + } + // We use -verbose:class to verify that Extra.class IS loaded by AppCDS if + // the boot classpath HAS NOT been appended. + ExtraClass.doit(); + System.exit(0); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + + if (args[0].equals("bootonly")) { + wb.addToBootstrapClassLoaderSearch(args[1]); + Class cls = forname(); + if (cls == null) { + failed("Cannot find Hello class"); + } + if (cls.getClassLoader() != null) { + failed("Hello class not loaded by boot classloader"); + } + } else if (args[0].equals("apponly")) { + wb.addToSystemClassLoaderSearch(args[1]); + Class cls = forname(); + if (cls == null) { + failed("Cannot find Hello class"); + } + if (cls.getClassLoader() != JvmtiApp.class.getClassLoader()) { + failed("Hello class not loaded by app classloader"); + } + } else if (args[0].equals("noadd-appcds")) { + Class cls = forname(); + if (cls == null) { + failed("Cannot find Hello class"); + } + if (cls.getClassLoader() != JvmtiApp.class.getClassLoader()) { + failed("Hello class not loaded by app classloader"); + } + } else if (args[0].equals("appandboot")) { + wb.addToBootstrapClassLoaderSearch(args[1]); + wb.addToSystemClassLoaderSearch(args[2]); + Class cls = forname(); + if (cls == null) { + failed("Cannot find Hello class"); + } + if (cls.getClassLoader() != null) { + failed("Hello class not loaded by boot classloader"); + } + } else { + failed("unknown option " + args[0]); + } + + // We use -verbose:class to verify that Extra.class IS NOT loaded by AppCDS if + // the boot classpath HAS been appended. + ExtraClass.doit(); + + System.out.println("Test passed: " + args[0]); + } +} + +class ExtraClass { + static void doit() {} +} \ No newline at end of file diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm new file mode 100644 index 00000000000..c9c17a50473 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +/* +WAS: + +class MethodNoReturn { + void badMethod() {} +} +*/ + +super class MethodNoReturn + version 52:0 +{ + + +Method "<init>":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."<init>":"()V"; + return; +} + +Method badMethod:"()V" + stack 0 locals 1 +{ + /* + should be: + return; + */ + + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + pop; + iconst_1; + iconst_1; + iconst_1; + iconst_1; + iconst_1; + iconst_1; + iconst_1; + iconst_1; + pop; + pop; + pop; + pop; + pop; + pop; + pop; + pop; + // no return here -- so this class will fail verification +} + +} // end Class MethodNoReturn diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java new file mode 100644 index 00000000000..ef47a7cb9e4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class MissingSuper { + public static void main(String args[]) { + try { + new MissingSuperSub(); + } catch (NoClassDefFoundError e) { + System.out.println("Expected NoClassDefFoundError:"); + e.printStackTrace(System.out); + } + + try { + new MissingSuperImpl(); + } catch (NoClassDefFoundError e) { + System.out.println("Expected NoClassDefFoundError:"); + e.printStackTrace(System.out); + } + } +} + +class MissingSuperSup {} // This class will be deleted from missing_super.jar before dumping + +class MissingSuperSub extends MissingSuperSup {} + +interface MissingSuperIntf {} // This interface will be deleted from missing_super.jar before dumping + +class MissingSuperImpl implements MissingSuperIntf {} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java new file mode 100644 index 00000000000..cf67318a24e --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import sun.hotspot.WhiteBox; + +// This class should be loaded from a shared archive. +public class MultiProcClass { + private static String instanceLabel; + + public static void main(String args[]) throws Exception { + instanceLabel = args[0]; + String checkPmap = args[1]; + + long pid = ProcessHandle.current().pid(); + System.out.println(inst("========================== Starting MultiProcClass")); + System.out.println(inst("My PID: " + pid )); + System.out.println(inst("checkPmap = <" + checkPmap + ">" )); + + if ("true".equals(checkPmap)) { + if (runPmap(pid, true) != 0) + System.out.println("MultiProcClass: Pmap failed"); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (!wb.isSharedClass(MultiProcClass.class)) { + throw new RuntimeException(inst("MultiProcClass should be shared but is not.")); + } + + System.out.println(inst("========================== Leaving MultiProcClass")); + } + + // A convenience method to append process instance label + private static String inst(String msg) { + return "process-" + instanceLabel + " : " + msg; + } + + // Use on Linux-only; requires jdk-9 for Process.pid() + public static int runPmap(long pid, boolean inheritIO) throws Exception { + ProcessBuilder pmapPb = new ProcessBuilder("pmap", "" + pid); + if (inheritIO) + pmapPb.inheritIO(); + + return pmapPb.start().waitFor(); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java new file mode 100644 index 00000000000..cbec72aecf6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) + +public @interface MyAnnotation { + public String name(); + public String value(); +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java new file mode 100644 index 00000000000..a1e8ea0a234 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.lang.Package; + +public class PackageSealingTest { + public static void main(String args[]) { + try { + Class c1 = PackageSealingTest.class.forName("sealed.pkg.C1"); + Class c2 = PackageSealingTest.class.forName("pkg.C2"); + Package p1 = c1.getPackage(); + System.out.println("Package 1: " + p1.toString()); + Package p2 = c2.getPackage(); + System.out.println("Package 2: " + p2.toString()); + + if (!p1.isSealed()) { + System.out.println("Failed: sealed.pkg is not sealed."); + System.exit(0); + } + + if (p2.isSealed()) { + System.out.println("Failed: pkg is sealed."); + System.exit(0); + } + + System.out.println("OK"); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java new file mode 100644 index 00000000000..f5f1f15014b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +package p; + +public class PackageTest { + public static void main(String args[]) { + (new PackageTest()).test(); + } + + private void test() { + ClassLoader cl = PackageTest.class.getClassLoader(); + Package pkg_from_loader; + if (cl != null) { + pkg_from_loader = cl.getDefinedPackage("p"); + } else { + pkg_from_loader = Package.getPackage("p"); + } + + Package pkg = PackageTest.class.getPackage(); + if (pkg_from_loader != null && pkg == pkg_from_loader && + pkg.getName().equals("p")) { + System.out.println("Expected package: " + pkg); + } else { + System.out.println("Unexpected package: " + pkg); + System.exit(1); + } + if (pkg.isSealed()) { + System.out.println("Package is sealed"); + System.exit(1); + } else { + System.out.println("Package is not sealed"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java new file mode 100644 index 00000000000..a4d0520f235 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +class ParallelClass0 {} +class ParallelClass1 {} +class ParallelClass2 {} +class ParallelClass3 {} +class ParallelClass4 {} +class ParallelClass5 {} +class ParallelClass6 {} +class ParallelClass7 {} +class ParallelClass8 {} +class ParallelClass9 {} +class ParallelClass10 {} +class ParallelClass11 {} +class ParallelClass12 {} +class ParallelClass13 {} +class ParallelClass14 {} +class ParallelClass15 {} +class ParallelClass16 {} +class ParallelClass17 {} +class ParallelClass18 {} +class ParallelClass19 {} +class ParallelClass20 {} +class ParallelClass21 {} +class ParallelClass22 {} +class ParallelClass23 {} +class ParallelClass24 {} +class ParallelClass25 {} +class ParallelClass26 {} +class ParallelClass27 {} +class ParallelClass28 {} +class ParallelClass29 {} +class ParallelClass30 {} +class ParallelClass31 {} +class ParallelClass32 {} +class ParallelClass33 {} +class ParallelClass34 {} +class ParallelClass35 {} +class ParallelClass36 {} +class ParallelClass37 {} +class ParallelClass38 {} +class ParallelClass39 {} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java new file mode 100644 index 00000000000..d47c3343845 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import java.io.*; +import java.net.*; +import java.lang.reflect.Field; + + +// This test helper is parameterized by: +// - class transformation mode: property "appcds.parallel.transform.mode" +// - class loader test types +// +// In the case of transformMode == "cflh", the transformation is performed +// by AppCDS/jvmti/TransformerAgent.java. The classes to be transformed, such as +// ParallelClassTr0, are defined in ./jvmti/parallelLoad/ParallelClasses.java + +public class ParallelLoad { + public static int MAX_CLASSES = 40; + public static int NUM_THREADS = 4; + + public final static int SYSTEM_LOADER = 0; + public final static int SINGLE_CUSTOM_LOADER = 1; + public final static int MULTI_CUSTOM_LOADER = 2; + + public static final int FINGERPRINT_MODE = 1; + public static final int API_MODE = 2; + + public static int loaderType = SYSTEM_LOADER; + public static ClassLoader classLoaders[]; + public static int mode = FINGERPRINT_MODE; + + public static float timeoutFactor = + Float.parseFloat(System.getProperty("test.timeout.factor", "1.0")); + + public static void main(String args[]) throws Throwable { + run(args, null); + } + public static void run(String args[], ClassLoader loaders[]) throws Throwable { + String customJar = null; + System.out.println("ParallelLoad: timeoutFactor = " + timeoutFactor); + + if (args.length >= 1) { + if ("SINGLE_CUSTOM_LOADER".equals(args[0])) { + loaderType = SINGLE_CUSTOM_LOADER; + customJar = args[2]; + } else if ("MULTI_CUSTOM_LOADER".equals(args[0])) { + loaderType = MULTI_CUSTOM_LOADER; + customJar = args[2]; + } else if ("SYSTEM_LOADER".equals(args[0])) { + loaderType = SYSTEM_LOADER; + } else { + throw new RuntimeException("Unexpected loaderType" + args[0]); + } + } + + if (customJar != null) { + if ("FINGERPRINT_MODE".equals(args[1])) { + mode = FINGERPRINT_MODE; + classLoaders = new ClassLoader[NUM_THREADS]; + for (int i=0; i<NUM_THREADS; i++) { + URL url = new File(customJar).toURI().toURL(); + URL[] urls = new URL[] {url}; + classLoaders[i] = new URLClassLoader(urls); + } + } else { + // Loaders must be supplied by caller of the run() method + mode = API_MODE; + classLoaders = loaders; + } + } + + System.out.println("Start Parallel Load ..."); + + Thread thread[] = new Thread[NUM_THREADS]; + for (int i=0; i<NUM_THREADS; i++) { + Thread t = new ParallelLoadThread(i); + t.start(); + thread[i] = t; + } + + Thread watchdog = new ParallelLoadWatchdog(); + watchdog.setDaemon(true); + watchdog.start(); + + for (int i=0; i<NUM_THREADS; i++) { + thread[i].join(); + } + System.out.println("Parallel Load ... done"); + System.exit(0); + } +} + + +class ParallelLoadWatchdog extends Thread { + public void run() { + try { + long timeout = (long) (20 * 1000 * ParallelLoad.timeoutFactor); + Thread.sleep(timeout); + System.out.println("ParallelLoadWatchdog: Timeout reached: timeout(ms) = " + timeout); + System.exit(1); + } catch (Throwable t) { + t.printStackTrace(); + System.exit(1); + } + } +}; + + +class ParallelLoadThread extends Thread { + static int num_ready[] = new int[ParallelLoad.MAX_CLASSES]; + static Object lock = new Object(); + static String transformMode = + System.getProperty("appcds.parallel.transform.mode", "none"); + + int thread_id; + ParallelLoadThread(int thread_id) { + this.thread_id = thread_id; + } + + public void run() { + try { + run0(); + } catch (Throwable t) { + t.printStackTrace(); + System.exit(1); + } + } + + private static void log(String msg, Object... args) { + String msg0 = "ParallelLoadThread: " + String.format(msg, args); + System.out.println(msg0); + } + + private void run0() throws Throwable { + for (int i=0; i<ParallelLoad.MAX_CLASSES; i++) { + synchronized(lock) { + num_ready[i] ++; + while (num_ready[i] < ParallelLoad.NUM_THREADS) { + lock.wait(); + } + lock.notifyAll(); + } + log("this = %s %d", this, i); + String className = "ParallelClass" + i; + if (transformMode.equals("cflh")) + className = "ParallelClassTr" + i; + + Class clazz = null; + + switch (ParallelLoad.loaderType) { + case ParallelLoad.SYSTEM_LOADER: + clazz = Class.forName(className); + break; + case ParallelLoad.SINGLE_CUSTOM_LOADER: + clazz = ParallelLoad.classLoaders[0].loadClass(className); + break; + case ParallelLoad.MULTI_CUSTOM_LOADER: + clazz = ParallelLoad.classLoaders[thread_id].loadClass(className); + break; + } + + log("clazz = %s", clazz); + testTransformation(clazz); + } + } + + private void testTransformation(Class c) throws Exception { + if (transformMode.equals("none")) + return; + + // currently only cflh transform mode is supported + if (!transformMode.equals("cflh")) { + String msg = "wrong transform mode: " + transformMode; + throw new IllegalArgumentException(msg); + } + + Field[] fields = c.getFields(); + boolean fieldFound = false; + for (Field f : fields) { + if (f.getName().equals("testString")) { + checkTransformationString(c, (String) f.get(null)); + fieldFound = true; + } + } + + if (!fieldFound) + throw new RuntimeException ("Expected field 'testString' not found"); + } + + private void checkTransformationString(Class c, String actual) throws Exception { + String expected = "class-transform-check: this-has-been--transformed"; + if (!actual.equals(expected)) { + String msg1 = "Transformation failed for class" + c.getName(); + String msg2 = String.format("Expected: %s, actual: %s", expected, actual); + throw new RuntimeException(msg1 + "\n" + msg2); + } + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm new file mode 100644 index 00000000000..175462b46a7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package java/lang; + +public class Prohibited + version 51:0 +{ +} // end class Prohibited diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java new file mode 100644 index 00000000000..ea22b2041ed --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +public class ProhibitedHelper { + public static void main(String[] args) throws Throwable { + + String className = "java.lang.Prohibited"; + ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); + try { + Module unnamedModule = sysLoader.getUnnamedModule(); + Class cls = Class.forName(unnamedModule, className); + System.out.println("cls " + cls); + throw new java.lang.RuntimeException(className + + "in a prohibited package shouldn't be loaded"); + } catch (Exception e) { + e.printStackTrace(); + if (!(e instanceof java.lang.SecurityException)) { + throw new java.lang.RuntimeException( + "SecurityException is expected to be thrown while loading " + className); + } + } + + try { + Class cls = Class.forName(className, false, sysLoader); + System.out.println("cls " + cls); + throw new java.lang.RuntimeException(className + + "in a prohibited package shouldn't be loaded"); + } catch (Exception e) { + e.printStackTrace(); + if (!(e instanceof java.lang.ClassNotFoundException)) { + throw new java.lang.RuntimeException( + "ClassNotFoundException is expected to be thrown while loading " + className); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java new file mode 100644 index 00000000000..fe88bfb1bb0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.security.ProtectionDomain; + +// See ../AppCDSProtectionDomain.java +// +// ProtDomain is stored in CDS archive. +// ProtDomainOther is NOT stored in CDS archive. +// +// However, they should have the same ProtectionDomain instance. +public class ProtDomain { + public static void main(String args[]) { + System.out.println("Testing ProtDomain"); + ProtectionDomain mine = ProtDomain.class.getProtectionDomain(); + ProtectionDomain his = ProtDomainOther.class.getProtectionDomain(); + + System.out.println("mine = " + mine); + System.out.println("his = " + his); + + if (mine == his) { + System.out.println("Protection Domains match"); + } else { + System.out.println("Protection Domains do not match!"); + System.exit(1); + } + } +} + +class ProtDomainOther { + +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java new file mode 100644 index 00000000000..01a36ccf706 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +import java.security.ProtectionDomain; + +// See ../AppCDSProtectionDomain.java +// +// ProtDomainB is NOT stored in CDS archive. +// ProtDomainBOther is stored in CDS archive. +// +// However, they should have the same ProtectionDomain instance. +public class ProtDomainB { + public static void main(String args[]) { + System.out.println("Testing ProtDomainB"); + ProtectionDomain mine = ProtDomainB.class.getProtectionDomain(); + ProtectionDomain his = ProtDomainBOther.class.getProtectionDomain(); + + System.out.println("mine = " + mine); + System.out.println("his = " + his); + + if (mine == his) { + System.out.println("Protection Domains match"); + } else { + System.out.println("Protection Domains do not match!"); + System.exit(1); + } + } +} + +class ProtDomainBOther { + +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java new file mode 100644 index 00000000000..2b1b0892a39 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class ReportMyLoader { + public static void main(String args[]) { + System.out.println("ReportMyLoader's loader = " + ReportMyLoader.class.getClassLoader()); + System.out.println("TestClassLoader.called = " + TestClassLoader.called); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java b/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java new file mode 100644 index 00000000000..787b51fa93c --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.File; +import sun.hotspot.WhiteBox; + +public class RewriteBytecodes { + public static void main(String args[]) throws Throwable { + String from = "___xxx___"; + String to = "___yyy___"; + File clsFile = new File(args[0]); + Class superClass = Util.defineModifiedClass(RewriteBytecodes.class.getClassLoader(), clsFile, from, to); + + Child child = new Child(); + + if (child.getClass().getSuperclass() != superClass) { + throw new RuntimeException("Mismatched super class"); + } + // Even if the Super class is not loaded from the CDS archive, make sure the Child class + // can still be loaded successfully, and properly inherits from the rewritten version + // of Super. + if (!child.toString().equals(to)) { + throw new RuntimeException("Wrong output, expected: " + to + ", but got: " + child.toString()); + } + + WhiteBox wb = WhiteBox.getWhiteBox(); + if (wb.isSharedClass(superClass)) { + throw new RuntimeException("wb.isSharedClass(superClass) should be false"); + } + if (wb.isSharedClass(child.getClass())) { + throw new RuntimeException("wb.isSharedClass(child.getClass()) should be false"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java new file mode 100644 index 00000000000..ba698c616bf --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +public class Super { + public String toString() { + return "___xxx___"; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java new file mode 100644 index 00000000000..29507956a27 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +// This is a class loader that simply delegates all calls to its parent loader. + +public class TestClassLoader extends ClassLoader { + static boolean called = false; + ClassLoader parent; + public TestClassLoader(ClassLoader parent) { + super(parent); + this.parent = parent; + } + + public Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + called = true; + System.out.println("TestClassLoader: loadClass(\"" + name + "\", " + resolve + ")"); + return (super.loadClass(name, resolve)); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java new file mode 100644 index 00000000000..48a455debd0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +public class TrySwitchMyLoader { + public static void main(String args[]) { + System.out.println("TrySwitchMyLoader's loader = " + ReportMyLoader.class.getClassLoader()); + System.setProperty("java.system.class.loader", "TestClassLoader"); + + // This should still report the same loader as TrySwitchMyLoader.class.getClassLoader(), + // as setting the java.system.class.loader after main method has been executed + // has no effect. + ReportMyLoader.main(args); + } +} + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java new file mode 100644 index 00000000000..4289c765257 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015, 2017, 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. + * + */ + +import java.io.*; +import java.lang.reflect.*; +import java.util.jar.*; + +public class Util { + /** + * Invoke the loader.defineClass() class method to define the class stored in clsFile, + * with the following modification: + * <ul> + * <li> All ASCII strings in the class file bytes that matches fromString will be replaced with toString. + * NOTE: the two strings must be the exact same length. + * </ul> + */ + public static Class defineModifiedClass(ClassLoader loader, File clsFile, String fromString, String toString) + throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, + InvocationTargetException + { + DataInputStream dis = new DataInputStream(new FileInputStream(clsFile)); + byte[] buff = new byte[(int)clsFile.length()]; + dis.readFully(buff); + replace(buff, fromString, toString); + + System.out.println("Loading from: " + clsFile + " (" + buff.length + " bytes)"); + + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", + buff.getClass(), int.class, int.class); + defineClass.setAccessible(true); + + // We directly call into ClassLoader.defineClass() to define the "Super" class. Also, + // rewrite its classfile so that it returns ___yyy___ instead of ___xxx___. Changing the + // classfile will guarantee that this class will NOT be loaded from the CDS archive. + Class cls = (Class)defineClass.invoke(loader, buff, new Integer(0), new Integer(buff.length)); + System.out.println("Loaded : " + cls); + + return cls; + } + + /** + * @return the number of occurrences of the <code>from</code> string that + * have been replaced. + */ + public static int replace(byte buff[], String from, String to) { + if (to.length() != from.length()) { + throw new RuntimeException("bad strings"); + } + byte f[] = asciibytes(from); + byte t[] = asciibytes(to); + byte f0 = f[0]; + + int numReplaced = 0; + int max = buff.length - f.length; + for (int i=0; i<max; ) { + if (buff[i] == f0 && replace(buff, f, t, i)) { + i += f.length; + numReplaced ++; + } else { + i++; + } + } + return numReplaced; + } + + public static boolean replace(byte buff[], byte f[], byte t[], int i) { + for (int x=0; x<f.length; x++) { + if (buff[x+i] != f[x]) { + return false; + } + } + for (int x=0; x<f.length; x++) { + buff[x+i] = t[x]; + } + return true; + } + + static byte[] asciibytes(String s) { + byte b[] = new byte[s.length()]; + for (int i=0; i<b.length; i++) { + b[i] = (byte)s.charAt(i); + } + return b; + } + + public static Class defineClassFromJAR(ClassLoader loader, File jarFile, String className) + throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + return defineClassFromJAR(loader, jarFile, className, null, null); + } + + /** + * Invoke the loader.defineClass() class method to define the named class stored in a JAR file. + * + * If a class exists both in the classpath, as well as in the list of URLs of a URLClassLoader, + * by default, the URLClassLoader will not define the class, and instead will delegate to the + * app loader. This method is an easy way to force the class to be defined by the URLClassLoader. + * + * Optionally, you can modify the contents of the classfile buffer. See comments in + * defineModifiedClass. + */ + public static Class defineClassFromJAR(ClassLoader loader, File jarFile, String className, + String fromString, String toString) + throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, + InvocationTargetException + { + byte[] buff = getClassFileFromJar(jarFile, className); + + if (fromString != null) { + replace(buff, fromString, toString); + } + + //System.out.println("Loading from: " + ent + " (" + buff.length + " bytes)"); + + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", + buff.getClass(), int.class, int.class); + defineClass.setAccessible(true); + Class cls = (Class)defineClass.invoke(loader, buff, new Integer(0), new Integer(buff.length)); + + //System.out.println("Loaded : " + cls); + return cls; + } + + public static byte[] getClassFileFromJar(File jarFile, String className) throws FileNotFoundException, IOException { + JarFile jf = new JarFile(jarFile); + JarEntry ent = jf.getJarEntry(className.replace('.', '/') + ".class"); + + DataInputStream dis = new DataInputStream(jf.getInputStream(ent)); + byte[] buff = new byte[(int)ent.getSize()]; + dis.readFully(buff); + dis.close(); + + return buff; + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java b/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java new file mode 100644 index 00000000000..ec60232a229 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 2017, 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. + * + */ + +/* + * Note: verifier_test_tmp.jar will be processed by ../AppCDSVerifierTest.java to + * create verifier_test.jar, which contains invalid versions of UnverifiableBase + * and UnverifiableIntf. + */ + +public class VerifierTest0 { + public static void main(String args[]) { + boolean good = true; + good &= mustBeInvalid("VerifierTestA"); + good &= mustBeInvalid("VerifierTestB"); + good &= mustBeInvalid("VerifierTestC"); + good &= mustBeInvalid("VerifierTestD"); + good &= mustBeInvalid("VerifierTestE"); + if (!good) { + System.out.println("VerifierTest0 failed"); + System.exit(1); + } + } + + /** @return false means error */ + static boolean mustBeInvalid(String className) { + System.out.println("Testing: " + className); + try { + Class.forName(className); + System.out.println("ERROR: class " + className + " was loaded unexpectedly."); + return false; + } catch (Throwable t) { + System.out.println("Expected exception:"); + t.printStackTrace(); + return true; + } + } +} + +class UnverifiableBase { + static final VerifierTest0 x = new VerifierTest0(); // <- this static initializer will be made unverifiable by type mismatch +} + +interface UnverifiableIntf { + static final VerifierTest0 x = new VerifierTest0(); // <- this static initializer will be made unverifiable by type mismatch +} + +interface UnverifiableIntfSub extends UnverifiableIntf {} + +class VerifierTestA extends UnverifiableBase {} +class VerifierTestB extends VerifierTestA {} +class VerifierTestC implements UnverifiableIntf {} +class VerifierTestD extends VerifierTestC {} +class VerifierTestE implements UnverifiableIntfSub {} + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm new file mode 100644 index 00000000000..aa70583fd4b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package com/sun/tools/javac; + +public class Main + version 51:0 +{ +} // end class Main diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf new file mode 100644 index 00000000000..79b4c2dd5ab --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: cpattr2.jar +Created-By: 1.9.0-internal (Oracle Corporation) diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf new file mode 100644 index 00000000000..923535d8ab4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Class-Path: zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.ja + r zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz + .jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.j + ar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar + zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar z + zzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzz + zzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzz + zz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz + .jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.j + ar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzz + z.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz. + jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.ja + r zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar + zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar cpattr2.jar cpattr1_long.j + ar +Created-By: 1.9.0-internal (Oracle Corporation) + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf new file mode 100644 index 00000000000..e638d1cec10 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Class-Path: cpattr3.jar cpattr5_123456789_223456789_323456789_42345678 + 9_523456789_623456789.jar +Created-By: 1.9.0-internal (Oracle Corporation) diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf new file mode 100644 index 00000000000..8cf6b52433f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Created-By: 1.9.0-internal (Oracle Corporation) diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf new file mode 100644 index 00000000000..8cf6b52433f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Created-By: 1.9.0-internal (Oracle Corporation) diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf new file mode 100644 index 00000000000..30d6d40f684 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Created-By: 1.9.0-internal (Oracle Corporation) + diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm new file mode 100644 index 00000000000..6ef9b92f41f --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package java/net; + +public class HttpCookie + version 51:0 +{ + +public Method thisClassIsDummy:"()V" + stack 0 locals 0 +{ + return; +} + +} // end class HttpCookie diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm new file mode 100644 index 00000000000..1436fe9b4ab --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package javax/activation; + +public class MimeType + version 51:0 +{ + +public Method thisClassIsDummy:"()V" + stack 0 locals 0 +{ + return; +} + +} // end class MimeType diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm new file mode 100644 index 00000000000..5f2683903da --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package javax/transaction; + +public class InvalidTransactionException + version 51:0 +{ + +public Method thisClassIsDummy:"()V" + stack 0 locals 0 +{ + return; +} + +} // end class InvalidTransactionException diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm new file mode 100644 index 00000000000..88544dc74de --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +package jdk/dynalink; + +public class DynamicLinker + version 51:0 +{ + +public Method thisClassIsDummy:"()V" + stack 0 locals 2 +{ + return; +} + +} // end class DynamicLinker diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf new file mode 100644 index 00000000000..6e43a3f801b --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Created-By: 1.9.0-internal (Oracle Corporation) + +Name: sealed/pkg/ +Sealed: true + From 2f50ac48200fffbd620f81aa5094e78227850abf Mon Sep 17 00:00:00 2001 From: Jini George <jgeorge@openjdk.org> Date: Tue, 28 Nov 2017 14:16:24 +0530 Subject: [PATCH 069/165] 8191961: SA: Remove left over quarantined SA tests due to 8184042 from ProblemList.txt Unquarantine SA tests quarantined due to OSX failures from 8184042 Reviewed-by: sspitsyn, sballal --- test/hotspot/jtreg/ProblemList.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index f79739c5ffc..be874cd589f 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -84,12 +84,7 @@ runtime/SharedArchiveFile/DefaultUseWithClient.java 8154204 generic-all serviceability/jdwp/AllModulesCommandTest.java 8170541 generic-all serviceability/sa/sadebugd/SADebugDTest.java 8163805 generic-all serviceability/jvmti/ModuleAwareAgents/ClassFileLoadHook/MAAClassFileLoadHook.java 8173936 generic-all -serviceability/sa/JhsdbThreadInfoTest.java 8184042 macosx-all -serviceability/sa/TestInstanceKlassSize.java 8184042 macosx-all -serviceability/sa/TestInstanceKlassSizeForInterface.java 8184042 macosx-all -serviceability/sa/TestPrintMdo.java 8184042 macosx-all serviceability/sa/TestRevPtrsForInvokeDynamic.java 8191270 generic-all -serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java 8184042 macosx-all serviceability/sa/TestJhsdbJstackLock.java 8191914 windows-all ############################################################################# From ded2479840ae63512c557fecda8b9008a0cd4241 Mon Sep 17 00:00:00 2001 From: Derek White <drwhite@openjdk.org> Date: Tue, 28 Nov 2017 12:23:03 +0100 Subject: [PATCH 070/165] 8188877: Improper synchronization in offer_termination Reviewed-by: kbarrett, tschatzl, aph --- src/hotspot/share/gc/shared/taskqueue.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index ef1e9688bd4..439d81e6e23 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -425,7 +425,7 @@ class ParallelTaskTerminator: public StackObj { private: uint _n_threads; TaskQueueSetSuper* _queue_set; - uint _offered_termination; + volatile uint _offered_termination; #ifdef TRACESPINNING static uint _total_yields; From a43a8363ff05683714ee58bf34f267e7f9acf8f5 Mon Sep 17 00:00:00 2001 From: Dmitry Chuyko <dchuyko@openjdk.org> Date: Tue, 28 Nov 2017 15:42:15 +0300 Subject: [PATCH 071/165] 8191769: AARCH64: Fix hint instructions encoding Reviewed-by: adinn --- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index ab1bcd55817..2a9766f5143 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -985,12 +985,33 @@ public: } void hint(int imm) { - system(0b00, 0b011, 0b0010, imm, 0b000); + system(0b00, 0b011, 0b0010, 0b0000, imm); } void nop() { hint(0); } + + void yield() { + hint(1); + } + + void wfe() { + hint(2); + } + + void wfi() { + hint(3); + } + + void sev() { + hint(4); + } + + void sevl() { + hint(5); + } + // we only provide mrs and msr for the special purpose system // registers where op1 (instr[20:19]) == 11 and, (currently) only // use it for FPSR n.b msr has L (instr[21]) == 0 mrs has L == 1 From 6d566ccf629f48def5beb60252f07e54b7cccf23 Mon Sep 17 00:00:00 2001 From: Volker Simonis <simonis@openjdk.org> Date: Tue, 28 Nov 2017 14:11:43 +0100 Subject: [PATCH 072/165] 8191770: [ppc64] Fix CDS: don't rewrite invokefinal if DumpSharedSpaces Reviewed-by: mdoerr, jiangli --- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 282ffeb218b..c7fdc9d7e23 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3371,7 +3371,7 @@ void TemplateTable::invokevirtual(int byte_no) { __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift); __ bfalse(CCR0, LnotFinal); - if (RewriteBytecodes && !UseSharedSpaces) { + if (RewriteBytecodes && !UseSharedSpaces && !DumpSharedSpaces) { patch_bytecode(Bytecodes::_fast_invokevfinal, Rnew_bc, R12_scratch2); } invokevfinal_helper(Rvtableindex_or_method, Rflags, R11_scratch1, R12_scratch2); From 800d7ffc3e201899d7a9e5b6c838394d3950ba1e Mon Sep 17 00:00:00 2001 From: Volker Simonis <simonis@openjdk.org> Date: Tue, 28 Nov 2017 14:20:15 +0100 Subject: [PATCH 073/165] 8191863: [s390] Fix CDS: some bytecode rewriting doesn't depend on RewriteControl Reviewed-by: mdoerr, goetz, jiangli --- src/hotspot/cpu/s390/templateTable_s390.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 7c1e458b9d6..972bb7d9c59 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -2884,12 +2884,12 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr // ztos BTB_BEGIN(is_Bool, bsize, "putfield_or_static:is_Bool"); __ pop(ztos); - if (do_rewrite) { + if (!is_static) { pop_and_check_object(obj); } __ z_nilf(Z_tos, 0x1); __ z_stc(Z_tos, field); - if (!is_static) { + if (do_rewrite) { patch_bytecode(Bytecodes::_fast_zputfield, bc, Z_ARG5, true, byte_no); } __ z_bru(Done); From 46f665881f73050ef07fc7fc268f1ad9161fea9b Mon Sep 17 00:00:00 2001 From: Harsha Wardhana B <hb@openjdk.org> Date: Tue, 28 Nov 2017 21:04:42 +0530 Subject: [PATCH 074/165] 5016517: Replace plaintext passwords by hashed passwords for out-of-the-box JMX Agent Reviewed-by: rriggs, dfuchs, mchung --- .../jmx/remote/security/FileLoginModule.java | 84 +-- .../security/HashedPasswordManager.java | 333 ++++++++++++ .../security/JMXPluggableAuthenticator.java | 16 +- .../jmxremote/ConnectorBootstrap.java | 23 +- .../share/conf/jmxremote.password.template | 99 +++- .../share/conf/management.properties | 11 + .../security/HashedPasswordFileTest.java | 514 ++++++++++++++++++ 7 files changed, 992 insertions(+), 88 deletions(-) create mode 100644 src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java create mode 100644 test/jdk/javax/management/security/HashedPasswordFileTest.java diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java index 9c5ebb6ec85..79b6dcf3b43 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2017, 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 @@ -22,22 +22,17 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package com.sun.jmx.remote.security; import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; -import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FilePermission; import java.io.IOException; import java.security.AccessControlException; import java.security.AccessController; import java.util.Arrays; -import java.util.Hashtable; import java.util.Map; -import java.util.Properties; import javax.security.auth.*; import javax.security.auth.callback.*; @@ -59,10 +54,7 @@ import com.sun.jmx.remote.util.EnvHelp; * the access control file for JMX remote management or in a Java security * policy. * - * <p> The password file comprises a list of key-value pairs as specified in - * {@link Properties}. The key represents a user's name and the value is its - * associated cleartext password. By default, the following password file is - * used: + * By default, the following password file is used: * <pre> * ${java.home}/conf/management/jmxremote.password * </pre> @@ -105,6 +97,11 @@ import com.sun.jmx.remote.util.EnvHelp; * <dd> if <code>true</code>, this module clears the username and password * stored in the module's shared state after both phases of authentication * (login and commit) have completed.</dd> + * + * <dt> <code>hashPasswords</code> </dt> + * <dd> if <code>true</code>, this module replaces each clear text password + * with its hash, if present. </dd> + * * </dl> */ public class FileLoginModule implements LoginModule { @@ -135,6 +132,7 @@ public class FileLoginModule implements LoginModule { private boolean tryFirstPass = false; private boolean storePass = false; private boolean clearPass = false; + private boolean hashPasswords = false; // Authentication status private boolean succeeded = false; @@ -154,7 +152,7 @@ public class FileLoginModule implements LoginModule { private String passwordFileDisplayName; private boolean userSuppliedPasswordFile; private boolean hasJavaHomePermission; - private Properties userCredentials; + private HashedPasswordManager hashPwdMgr; /** * Initialize this <code>LoginModule</code>. @@ -186,6 +184,8 @@ public class FileLoginModule implements LoginModule { "true".equalsIgnoreCase((String)options.get("storePass")); clearPass = "true".equalsIgnoreCase((String)options.get("clearPass")); + hashPasswords + = "true".equalsIgnoreCase((String) options.get("hashPasswords")); passwordFile = (String)options.get("passwordFile"); passwordFileDisplayName = passwordFile; @@ -221,17 +221,28 @@ public class FileLoginModule implements LoginModule { public boolean login() throws LoginException { try { - loadPasswordFile(); + synchronized (this) { + if (hashPwdMgr == null) { + hashPwdMgr = new HashedPasswordManager(passwordFile, hashPasswords); + } + } + hashPwdMgr.loadPasswords(); } catch (IOException ioe) { LoginException le = new LoginException( "Error: unable to load the password file: " + passwordFileDisplayName); throw EnvHelp.initCause(le, ioe); - } - - if (userCredentials == null) { - throw new LoginException - ("Error: unable to locate the users' credentials."); + } catch (SecurityException e) { + if (userSuppliedPasswordFile || hasJavaHomePermission) { + throw e; + } else { + final FilePermission fp + = new FilePermission(passwordFileDisplayName, "read"); + AccessControlException ace = new AccessControlException( + "access denied " + fp.toString()); + ace.initCause(e); + throw ace; + } } if (logger.debugOn()) { @@ -437,12 +448,7 @@ public class FileLoginModule implements LoginModule { // get the username and password getUsernamePassword(usePasswdFromSharedState); - String localPassword; - - // userCredentials is initialized in login() - if (((localPassword = userCredentials.getProperty(username)) == null) || - (! localPassword.equals(new String(password)))) { - + if (!hashPwdMgr.authenticate(username, password)) { // username not found or passwords do not match if (logger.debugOn()) { logger.debug("login", "Invalid username or password"); @@ -468,38 +474,6 @@ public class FileLoginModule implements LoginModule { } } - /* - * Read the password file. - */ - private void loadPasswordFile() throws IOException { - FileInputStream fis; - try { - fis = new FileInputStream(passwordFile); - } catch (SecurityException e) { - if (userSuppliedPasswordFile || hasJavaHomePermission) { - throw e; - } else { - final FilePermission fp = - new FilePermission(passwordFileDisplayName, "read"); - AccessControlException ace = new AccessControlException( - "access denied " + fp.toString()); - ace.setStackTrace(e.getStackTrace()); - throw ace; - } - } - try { - final BufferedInputStream bis = new BufferedInputStream(fis); - try { - userCredentials = new Properties(); - userCredentials.load(bis); - } finally { - bis.close(); - } - } finally { - fis.close(); - } - } - /** * Get the username and password. * This method does not return any value. diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java new file mode 100644 index 00000000000..1404e2d458e --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2017, 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. 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. + */ +package com.sun.jmx.remote.security; + +import com.sun.jmx.remote.util.ClassLogger; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileLock; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * HashedPasswordManager loads passwords from the password file and optionally + * hashes them. + * <p> + * <p> + * This class accepts Unicode UTF-8 encoded file + * <p> + * <p> + * Each entry in the password file contains a username followed by a password. + * Password can be in clear text or as a hash. Hashed passwords must follow the + * below format. hashedPassword = base64_encoded_64_byte_salt W + * base64_encoded_hash W hash_algorithm where, W = spaces, + * base64_encoded_64_byte_salt = 64 byte random salt, base64_encoded_hash = + * hash_algorithm(password + salt), hash_algorithm = Algorithm string as + * specified in + * <a href="{@docRoot}/../specs/security/standard-names.html#messagedigest-algorithms"> + * </a> + * hash_algorithm is an optional field. If not specified, SHA3-512 will be + * assumed. + * <p> + * <p> + * If passwords are in clear, they will be over-written by their hash if hashing + * is requested by setting com.sun.management.jmxremote.password.toHashes + * property to true in the management.properties file and if the password file + * is writable and if the system security policy allows writing into the + * password file, if a security manager is configured + * <p> + * <p> + * In order to change the password for a role, replace the hashed password entry + * with a new clear text password or a new hashed password. If the new password + * is in clear, it will be replaced with its hash when a new login attempt is + * made. + * <p> + * <p> + * A given role should have at most one entry in this file. If a role has no + * entry, it has no access. If multiple entries are found for the same role + * name, then the last one will be used. + * <p> + * <p> + * <p> + * A user generated hashed password file can also be used instead of a + * clear-text password file. If generated by the user, hashed passwords must + * follow the format specified above. + */ +final public class HashedPasswordManager { + + private static final class UserCredentials { + + private final String userName; + private final String hashAlgorithm; + private final String b64Salt; + private final String b64Hash; + + public UserCredentials(String userName, String hashAlgorithm, String b64Salt, String b64Hash) { + this.userName = userName; + this.hashAlgorithm = hashAlgorithm; + this.b64Salt = b64Salt; + this.b64Hash = b64Hash; + } + + @Override + public String toString() { + return userName + " " + b64Salt + " " + b64Hash + " " + hashAlgorithm; + } + } + + private static final String DefaultHashAlgorithm = "SHA3-512"; + private static final int DefaultSaltLength = 64; + + private final SecureRandom random = new SecureRandom(); + private final Map<String, UserCredentials> userCredentialsMap = new HashMap<>(); + private final String passwordFile; + private final boolean shouldHashPasswords; + private boolean isLogged = false; + + private static final ClassLogger logger + = new ClassLogger("javax.management.remote.misc", + "HashedPasswordManager"); + + /** + * Creates a new password manager for the input password file + * + * @param filename UTF-8 encoded input file to read passwords from + * @param shouldHashPasswords Request for clear passwords to be hashed + */ + public HashedPasswordManager(String filename, boolean shouldHashPasswords) { + this.passwordFile = filename; + this.shouldHashPasswords = shouldHashPasswords; + } + + private String[] getHash(String algorithm, String password) { + try { + byte[] salt = new byte[DefaultSaltLength]; + random.nextBytes(salt); + + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.reset(); + digest.update(salt); + byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); + String saltStr = Base64.getEncoder().encodeToString(salt); + String hashStr = Base64.getEncoder().encodeToString(hash); + + return new String[]{saltStr, hashStr}; + } catch (NoSuchAlgorithmException ex) { + if (logger.debugOn()) { + logger.debug("getHash", "Invalid algorithm : " + algorithm); + } + // We should never reach here as default Hash Algorithm + // must be always present + return new String[]{"", ""}; + } + } + + private String[] readPasswordFile() throws IOException { + synchronized (HashedPasswordManager.class) { + byte[] data; + File f = new File(passwordFile); + try (FileInputStream fin = new FileInputStream(f); + FileLock lock = fin.getChannel().lock(0L, Long.MAX_VALUE, true)) { + data = new byte[(int) f.length()]; + int read = fin.read(data); + if (read != data.length) { + throw new IOException("Failed to read data from the password file"); + } + lock.release(); + } + String str = new String(data, StandardCharsets.UTF_8); + return str.split("\\r?\\n"); + } + } + + private void writePasswordFile(String input) throws IOException { + synchronized (HashedPasswordManager.class) { + try (FileOutputStream fout = new FileOutputStream(passwordFile); + OutputStreamWriter out = new OutputStreamWriter(fout, StandardCharsets.UTF_8); + FileLock lock = fout.getChannel().lock()) { + out.write(input); + lock.release(); + } + } + } + + /** + * Authenticate the supplied credentials against the one present in the file + * + * @param userName Input username + * @param inputPassword Input password + * @return true if authentication succeeds, false otherwise + */ + public synchronized boolean authenticate(String userName, char[] inputPassword) { + if (userCredentialsMap.containsKey(userName)) { + try { + UserCredentials us = userCredentialsMap.get(userName); + byte[] salt = Base64.getDecoder().decode(us.b64Salt); + byte[] targetHash = Base64.getDecoder().decode(us.b64Hash); + MessageDigest digest = MessageDigest.getInstance(us.hashAlgorithm); + digest.reset(); + digest.update(salt); + ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(CharBuffer.wrap(inputPassword)); + byte[] passwordBytes = new byte[byteBuffer.limit()]; + byteBuffer.get(passwordBytes); + byte[] hash = digest.digest(passwordBytes); + return Arrays.equals(hash, targetHash); + } catch (NoSuchAlgorithmException ex) { + if (logger.debugOn()) { + logger.debug("authenticate", "Unrecognized hash algorithm : " + + userCredentialsMap.get(userName).hashAlgorithm + + " - for user : " + userName); + } + return false; + } + } else { + if (logger.debugOn()) { + logger.debug("authenticate", "Unknown user : " + userName); + } + return false; + } + } + + /** + * Load passwords from the password file. + * <p> + * <p> + * This method should be called for every login attempt to load new/changed + * credentials, if any. + * <p> + * <p> + * If hashing is requested, clear passwords will be over-written with their + * SHA3-512 hash + * + * @throws IOException If unable to access the file + * @throws SecurityException If read/write file permissions are not granted + */ + public synchronized void loadPasswords() + throws IOException, SecurityException { + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(passwordFile); + } + + AtomicBoolean hasClearPasswords = new AtomicBoolean(false); + StringBuilder sbuf = new StringBuilder(); + final String header = "# The passwords in this file are hashed.\n" + + "# In order to change the password for a role, replace the hashed " + + "password entry\n" + + "# with a clear text password or a new hashed password. " + + "If the new password is in clear,\n# it will be replaced with its " + + "hash when a new login attempt is made.\n\n"; + + userCredentialsMap.clear(); + Arrays.stream(readPasswordFile()).forEach(line -> { + if (line.trim().startsWith("#")) { // Ignore comments + sbuf.append(line).append("\n"); + return; + } + String[] tokens = line.split("\\s+"); + switch (tokens.length) { + case 2: { + // Password is in clear + String[] b64str = getHash(DefaultHashAlgorithm, tokens[1]); + UserCredentials us = new UserCredentials(tokens[0], DefaultHashAlgorithm, b64str[0], b64str[1]); + sbuf.append(us.userName).append(" ").append(us.b64Salt). + append(" ").append(us.b64Hash).append(" "). + append(us.hashAlgorithm).append("\n"); + if (userCredentialsMap.get(tokens[0]) != null) { + if (logger.debugOn()) { + logger.debug("loadPasswords", "Ignoring entry for role : " + tokens[0]); + } + } + userCredentialsMap.put(tokens[0], us); + hasClearPasswords.set(true); + if (logger.debugOn()) { + logger.debug("loadPasswords", + "Found atleast one clear password"); + } + break; + } + case 3: + case 4: { + // Passwords are hashed + UserCredentials us = new UserCredentials(tokens[0], (tokens.length == 4 ? tokens[3] : DefaultHashAlgorithm), + tokens[1], tokens[2]); + sbuf.append(line).append("\n"); + if (userCredentialsMap.get(tokens[0]) != null) { + if (logger.debugOn()) { + logger.debug("loadPasswords", "Ignoring entry for role : " + tokens[0]); + } + } + userCredentialsMap.put(tokens[0], us); + break; + } + default: + sbuf.append(line).append("\n"); + break; + } + }); + + if (!shouldHashPasswords && hasClearPasswords.get()) { + if (logger.debugOn()) { + logger.debug("loadPasswords", + "Passwords in " + passwordFile + " are in clear but are requested " + + "not to be hashed !!!"); + } + } + + // Check if header needs to be inserted + if (sbuf.indexOf("# The passwords in this file are hashed") != 0) { + sbuf.insert(0, header); + } + + // Even if we are unable to write hashed passwords to password file, + // passwords will be hashed in memory so that jvm heap dump should not + // give away clear passwords + if (shouldHashPasswords && hasClearPasswords.get()) { + if (new File(passwordFile).canWrite()) { + writePasswordFile(sbuf.toString()); + if (logger.debugOn()) { + logger.debug("loadPasswords", + "Wrote hashed passwords to file : " + passwordFile); + } + } else if (logger.debugOn() && !isLogged) { + isLogged = true; + logger.debug("loadPasswords", + "Passwords in " + passwordFile + " are in clear and password file is read-only. " + + "Passwords cannot be hashed !!!!"); + } + } + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java index f7ab5d70fb6..dadef33e5dc 100644 --- a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java +++ b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2017, 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 @@ -34,8 +34,6 @@ import java.security.PrivilegedExceptionAction; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Properties; -import javax.management.remote.JMXPrincipal; import javax.management.remote.JMXAuthenticator; import javax.security.auth.AuthPermission; import javax.security.auth.Subject; @@ -91,10 +89,12 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator { String loginConfigName = null; String passwordFile = null; + String hashPasswords = null; if (env != null) { loginConfigName = (String) env.get(LOGIN_CONFIG_PROP); passwordFile = (String) env.get(PASSWORD_FILE_PROP); + hashPasswords = (String) env.get(HASH_PASSWORDS); } try { @@ -114,6 +114,7 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator { } final String pf = passwordFile; + final String hashPass = hashPasswords; try { loginContext = AccessController.doPrivileged( new PrivilegedExceptionAction<LoginContext>() { @@ -122,7 +123,7 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator { LOGIN_CONFIG_NAME, null, new JMXCallbackHandler(), - new FileLoginConfig(pf)); + new FileLoginConfig(pf, hashPass)); } }); } catch (PrivilegedActionException pae) { @@ -250,6 +251,8 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator { private static final String LOGIN_CONFIG_NAME = "JMXPluggableAuthenticator"; private static final String PASSWORD_FILE_PROP = "jmx.remote.x.password.file"; + private static final String HASH_PASSWORDS = + "jmx.remote.x.password.toHashes"; private static final ClassLogger logger = new ClassLogger("javax.management.remote.misc", LOGIN_CONFIG_NAME); @@ -303,19 +306,22 @@ private static class FileLoginConfig extends Configuration { // The option that identifies the password file to use private static final String PASSWORD_FILE_OPTION = "passwordFile"; + private static final String HASH_PASSWORDS = "hashPasswords"; /** * Creates an instance of <code>FileLoginConfig</code> * * @param passwordFile A filepath that identifies the password file to use. * If null then the default password file is used. + * @param hashPasswords Flag to indicate if the password file needs to be hashed */ - public FileLoginConfig(String passwordFile) { + public FileLoginConfig(String passwordFile, String hashPasswords) { Map<String, String> options; if (passwordFile != null) { options = new HashMap<String, String>(1); options.put(PASSWORD_FILE_OPTION, passwordFile); + options.put(HASH_PASSWORDS, hashPasswords); } else { options = Collections.emptyMap(); } diff --git a/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java b/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java index dae6b95c8bf..8d2031f888b 100644 --- a/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java +++ b/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -70,10 +70,8 @@ import javax.net.ssl.TrustManagerFactory; import javax.rmi.ssl.SslRMIClientSocketFactory; import javax.rmi.ssl.SslRMIServerSocketFactory; import javax.security.auth.Subject; - import com.sun.jmx.remote.internal.rmi.RMIExporter; import com.sun.jmx.remote.security.JMXPluggableAuthenticator; - import jdk.internal.agent.Agent; import jdk.internal.agent.AgentConfigurationError; import static jdk.internal.agent.AgentConfigurationError.*; @@ -102,6 +100,7 @@ public final class ConnectorBootstrap { public static final String USE_REGISTRY_SSL = "false"; public static final String USE_AUTHENTICATION = "true"; public static final String PASSWORD_FILE_NAME = "jmxremote.password"; + public static final String HASH_PASSWORDS = "true"; public static final String ACCESS_FILE_NAME = "jmxremote.access"; public static final String SSL_NEED_CLIENT_AUTH = "false"; } @@ -129,6 +128,8 @@ public final class ConnectorBootstrap { "com.sun.management.jmxremote.authenticate"; public static final String PASSWORD_FILE_NAME = "com.sun.management.jmxremote.password.file"; + public static final String HASH_PASSWORDS + = "com.sun.management.jmxremote.password.toHashes"; public static final String ACCESS_FILE_NAME = "com.sun.management.jmxremote.access.file"; public static final String LOGIN_CONFIG_NAME = @@ -412,6 +413,7 @@ public final class ConnectorBootstrap { String loginConfigName = null; String passwordFileName = null; + boolean shouldHashPasswords = true; String accessFileName = null; // Initialize settings when authentication is active @@ -426,6 +428,11 @@ public final class ConnectorBootstrap { passwordFileName = props.getProperty(PropertyNames.PASSWORD_FILE_NAME, getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME)); + String hashPasswords + = props.getProperty(PropertyNames.HASH_PASSWORDS, + DefaultValues.HASH_PASSWORDS); + shouldHashPasswords = Boolean.parseBoolean(hashPasswords); + checkPasswordFile(passwordFileName); } @@ -474,7 +481,7 @@ public final class ConnectorBootstrap { sslConfigFileName, enabledCipherSuitesList, enabledProtocolsList, sslNeedClientAuth, useAuthentication, loginConfigName, - passwordFileName, accessFileName, bindAddress, jmxRmiFilter); + passwordFileName, shouldHashPasswords, accessFileName, bindAddress, jmxRmiFilter); cs = data.jmxConnectorServer; url = data.jmxRemoteURL; config("startRemoteConnectorServer", @@ -569,6 +576,10 @@ public final class ConnectorBootstrap { throw new AgentConfigurationError(PASSWORD_FILE_NOT_READABLE, passwordFileName); } + if(!file.canWrite() && PropertyNames.HASH_PASSWORDS.equalsIgnoreCase("true")) { + logger.log(Level.WARNING, ""); + } + FileSystem fs = FileSystem.open(); try { if (fs.supportsFileSecurity(file)) { @@ -729,6 +740,7 @@ public final class ConnectorBootstrap { boolean useAuthentication, String loginConfigName, String passwordFileName, + boolean shouldHashPasswords, String accessFileName, String bindAddress, String jmxRmiFilter) @@ -761,6 +773,9 @@ public final class ConnectorBootstrap { if (passwordFileName != null) { env.put("jmx.remote.x.password.file", passwordFileName); } + if (shouldHashPasswords) { + env.put("jmx.remote.x.password.toHashes", "true"); + } env.put("jmx.remote.x.access.file", accessFileName); diff --git a/src/jdk.management.agent/share/conf/jmxremote.password.template b/src/jdk.management.agent/share/conf/jmxremote.password.template index 13dfd2578bb..c98a0ad253a 100644 --- a/src/jdk.management.agent/share/conf/jmxremote.password.template +++ b/src/jdk.management.agent/share/conf/jmxremote.password.template @@ -3,11 +3,12 @@ # # o Copy this template to jmxremote.password # o Set the user/password entries in jmxremote.password -# o Change the permission of jmxremote.password to read-only -# by the owner. +# o Change the permission of jmxremote.password to be accessible +# only by the owner. +# o The jmxremote.passwords file will be re-written by the server +# to replace all plain text passwords with hashed passwords when +# the file is read by the server. # -# See below for the location of jmxremote.password file. -# ---------------------------------------------------------------------- ############################################################## # Password File for Remote JMX Monitoring @@ -24,41 +25,91 @@ # the management config file $JRE/conf/management/management.properties # or by specifying a system property (See that file for details). - ############################################################## -# File permissions of the jmxremote.password file +# File format of the jmxremote.password file ############################################################## -# Since there are cleartext passwords stored in this file, -# this file must be readable by ONLY the owner, -# otherwise the program will exit with an error. # -# The file format for password and access files is syntactically the same -# as the Properties file format. The syntax is described in the Javadoc -# for java.util.Properties.load. -# Typical password file has multiple lines, where each line is blank, +# The file contains multiple lines where each line is blank, # a comment (like this one), or a password entry. # +# password entry follows the below syntax +# role_name W [clearPassword|hashedPassword] # -# A password entry consists of a role name and an associated -# password. The role name is any string that does not itself contain -# spaces or tabs. The password is again any string that does not -# contain spaces or tabs. Note that passwords appear in the clear in -# this file, so it is a good idea not to use valuable passwords. +# role_name is any string that does not itself contain spaces or tabs. +# W = spaces or tabs +# +# Passwords can be specified via clear text or via a hash. Clear text password +# is any string that does not contain spaces or tabs. Hashed passwords must +# follow the below format. +# hashedPassword = base64_encoded_64_byte_salt W base64_encoded_hash W hash_algorithm +# where, +# base64_encoded_64_byte_salt = 64 byte random salt +# base64_encoded_hash = Hash_algorithm(password + salt) +# W = spaces or tabs +# hash_algorithm = Algorithm string specified using the format below +# https://docs.oracle.com/javase/9/docs/specs/security/standard-names.html#messagedigest-algorithms +# This is an optional field. If not specified, SHA3-512 will be assumed. +# +# If passwords are in clear, they will be overwritten by their hash if all of +# the below criteria are met. +# * com.sun.management.jmxremote.password.toHashes property is set to true in +# management.properties file +# * the password file is writable +# * the system security policy allows writing into the password file, if a +# security manager is configured +# +# In order to change the password for a role, replace the hashed password entry +# with a new clear text password or a new hashed password. If the new password +# is in clear, it will be replaced with its hash when a new login attempt is made. # # A given role should have at most one entry in this file. If a role # has no entry, it has no access. # If multiple entries are found for the same role name, then the last one # is used. # -# In a typical installation, this file can be read by anybody on the +# A user generated hashed password file can also be used instead of clear-text +# password file. If generated by the user, hashed passwords must follow the +# format specified above. +# +# Caution: It is recommended not to edit the password file while the +# agent is running, as edits could be lost if a client connection triggers the +# hashing of the password file at the same time that the file is externally modified. +# The integrity of the file is guaranteed, but any external edits made to the +# file during the short period between the time that the agent reads the file +# and the time that it writes it back might get lost + +############################################################## +# File permissions of the jmxremote.password file +############################################################## +# This file must be made accessible by ONLY the owner, +# otherwise the program will exit with an error. +# +# In a typical installation, this file can be accessed by anybody on the # local machine, and possibly by people on other machines. -# For # security, you should either restrict the access to this file, +# For security, you should either restrict the access to this file except for owner, # or specify another, less accessible file in the management config file # as described above. # -# Following are two commented-out entries. The "measureRole" role has -# password "QED". The "controlRole" role has password "R&D". +# In order to prevent inadverent edits to the password file in the +# production environment, it is recommended to deploy a read-only +# hashed password file. The hashed entries for clear passwords can be generated +# in advance by running the JMX agent. # -# monitorRole QED -# controlRole R&D +############################################################## +# Sample of the jmxremote.password file +############################################################## +# Following are two commented-out entries. The "monitorRole" role has +# password "QED". The "controlRole" role has password "R&D". This is an example +# of specifying passwords in the clear +# +# monitorRole QED +# controlRole R&D +# +# Once a login attempt is made, passwords will be hashed and the file will have +# below entries with clear passwords overwritten by their respective +# SHA3-512 hash +# +# monitorRole trilby APzBTt34rV2l+OMbuvbnOQ4si8UZmfRCVbIY1+fAofV5CkQzXS/FDMGteQQk/R3q1wtt104qImzJEA7gCwl6dw== 4EeTdSJ7X6Imu0Mb+dWqIns7a7QPIBoM3NB/XlpMQSPSicE7PnlALVWn2pBY3Q3pGDHyAb32Hd8GUToQbUhAjA== SHA3-512 +# controlRole roHEJSbRqSSTII4Z4+NOCV2OJaZVQ/dw153Fy2u4ILDP9XiZ426GwzCzc3RtpoqNMwqYIcfdd74xWXSMrWtGaA== w9qDsekgKn0WOVJycDyU0kLBa081zbStcCjUAVEqlfon5Sgx7XHtaodbmzpLegA1jT7Ag36T0zHaEWRHJe2fdA== SHA3-512 +# \ No newline at end of file diff --git a/src/jdk.management.agent/share/conf/management.properties b/src/jdk.management.agent/share/conf/management.properties index 3bf88daf630..1f8d86e1730 100644 --- a/src/jdk.management.agent/share/conf/management.properties +++ b/src/jdk.management.agent/share/conf/management.properties @@ -300,6 +300,17 @@ # For a non-default password file location use the following line # com.sun.management.jmxremote.password.file=filepath +# +# ################# Hash passwords in password file ############## +# com.sun.management.jmxremote.password.toHashes = true|false +# Default for this property is true. +# Specifies if passwords in the password file should be hashed or not. +# If this property is true, and if the password file is writable, and if the +# system security policy allows writing into the password file, +# all the clear passwords in the password file will be replaced by +# their SHA3-512 hash when the file is read by the server +# + # # ################ RMI Access file location ##################### # diff --git a/test/jdk/javax/management/security/HashedPasswordFileTest.java b/test/jdk/javax/management/security/HashedPasswordFileTest.java new file mode 100644 index 00000000000..cae574c36a6 --- /dev/null +++ b/test/jdk/javax/management/security/HashedPasswordFileTest.java @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2017, 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 5016517 + * @summary Test Hashed passwords + * @library /test/lib + * @modules java.management + * @build HashedPasswordFileTest + * @run testng/othervm HashedPasswordFileTest + * + */ + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFilePermission; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.*; +import javax.management.MBeanServer; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.AfterClass; + +import jdk.test.lib.Utils; +import jdk.test.lib.process.ProcessTools; + +@Test +public class HashedPasswordFileTest { + + private final String[] randomWords = {"accost", "savoie", "bogart", "merest", + "azuela", "hoodie", "bursal", "lingua", "wincey", "trilby", "egesta", + "wester", "gilgai", "weinek", "ochone", "sanest", "gainst", "defang", + "ranket", "mayhem", "tagger", "timber", "eggcup", "mhren", "colloq", + "dreamy", "hattie", "rootle", "bloody", "helyne", "beater", "cosine", + "enmity", "outbox", "issuer", "lumina", "dekker", "vetoed", "dennis", + "strove", "gurnet", "talkie", "bennie", "behove", "coates", "shiloh", + "yemeni", "boleyn", "coaxal", "irne"}; + + private final String[] hashAlgs = { + "MD2", + "MD5", + "SHA-1", + "SHA-224", + "SHA-256", + "SHA-384", + "SHA-512/224", + "SHA-512/256", + "SHA3-224", + "SHA3-256", + "SHA3-384", + "SHA3-512" + }; + + private final Random rnd = new Random(); + private final Random random = Utils.getRandomInstance(); + + private JMXConnectorServer cs; + + private String randomWord() { + int idx = rnd.nextInt(randomWords.length); + return randomWords[idx]; + } + + private String[] getHash(String algorithm, String password) { + try { + byte[] salt = new byte[64]; + random.nextBytes(salt); + + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.reset(); + digest.update(salt); + byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8)); + + String saltStr = Base64.getEncoder().encodeToString(salt); + String hashStr = Base64.getEncoder().encodeToString(hash); + + return new String[]{saltStr, hashStr}; + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + } + + private String getPasswordFilePath() { + String testDir = System.getProperty("test.src"); + String testFileName = "jmxremote.password"; + return testDir + File.separator + testFileName; + } + + private File createNewPasswordFile() throws IOException { + File file = new File(getPasswordFilePath()); + if (file.exists()) { + file.delete(); + } + file.createNewFile(); + return file; + } + + private Map<String, String> generateClearTextPasswordFile() throws IOException { + File file = createNewPasswordFile(); + Map<String, String> props = new HashMap<>(); + BufferedWriter br; + try (FileWriter fw = new FileWriter(file)) { + br = new BufferedWriter(fw); + int numentries = rnd.nextInt(5) + 3; + for (int i = 0; i < numentries; i++) { + String username = randomWord(); + String password = randomWord(); + props.put(username, password); + br.write(username + " " + password + "\n"); + } + br.flush(); + } + br.close(); + return props; + } + + private boolean isPasswordFileHashed() throws IOException { + BufferedReader br; + boolean result; + try (FileReader fr = new FileReader(getPasswordFilePath())) { + br = new BufferedReader(fr); + result = br.lines().anyMatch(line -> { + if (line.startsWith("#")) { + return false; + } + String[] tokens = line.split("\\s+"); + return tokens.length == 3 || tokens.length == 4; + }); + } + br.close(); + return result; + } + + private Map<String, String> generateHashedPasswordFile() throws IOException { + File file = createNewPasswordFile(); + Map<String, String> props = new HashMap<>(); + BufferedWriter br; + try (FileWriter fw = new FileWriter(file)) { + br = new BufferedWriter(fw); + int numentries = rnd.nextInt(5) + 3; + for (int i = 0; i < numentries; i++) { + String username = randomWord(); + String password = randomWord(); + String alg = hashAlgs[rnd.nextInt(hashAlgs.length)]; + String[] b64str = getHash(alg, password); + br.write(username + " " + b64str[0] + " " + b64str[1] + " " + alg + "\n"); + props.put(username, password); + } + br.flush(); + } + br.close(); + return props; + } + + private JMXServiceURL createServerSide(boolean useHash) + throws IOException { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.x.password.file", getPasswordFilePath()); + env.put("jmx.remote.x.password.toHashes", useHash ? "true" : "false"); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + cs.start(); + return cs.getAddress(); + } + + @Test + public void testClearTextPasswordFile() throws IOException { + Boolean[] bvals = new Boolean[]{true, false}; + for (boolean bval : bvals) { + try { + Map<String, String> credentials = generateClearTextPasswordFile(); + JMXServiceURL serverUrl = createServerSide(bval); + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) { + cc.getMBeanServerConnection(); + } + } + Assert.assertEquals(isPasswordFileHashed(), bval); + } finally { + cs.stop(); + } + } + } + + @Test + public void testReadOnlyPasswordFile() throws IOException { + Boolean[] bvals = new Boolean[]{true, false}; + for (boolean bval : bvals) { + try { + Map<String, String> credentials = generateClearTextPasswordFile(); + File file = new File(getPasswordFilePath()); + file.setReadOnly(); + JMXServiceURL serverUrl = createServerSide(bval); + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) { + cc.getMBeanServerConnection(); + } + } + Assert.assertEquals(isPasswordFileHashed(), false); + } finally { + cs.stop(); + } + } + } + + @Test + public void testHashedPasswordFile() throws IOException { + Boolean[] bvals = new Boolean[]{true, false}; + for (boolean bval : bvals) { + try { + Map<String, String> credentials = generateHashedPasswordFile(); + JMXServiceURL serverUrl = createServerSide(bval); + Assert.assertEquals(isPasswordFileHashed(), true); + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) { + cc.getMBeanServerConnection(); + } + } + } finally { + cs.stop(); + } + } + } + + private static class SimpleJMXClient implements Callable { + private final JMXServiceURL url; + private final Map<String, String> credentials; + + public SimpleJMXClient(JMXServiceURL url, Map<String, String> credentials) { + this.url = url; + this.credentials = credentials; + } + + @Override + public Object call() throws Exception { + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(url, env)) { + cc.getMBeanServerConnection(); + } + } + return null; + } + } + + @Test + public void testMultipleClients() throws Throwable { + Map<String, String> credentials = generateClearTextPasswordFile(); + JMXServiceURL serverUrl = createServerSide(true); + Assert.assertEquals(isPasswordFileHashed(), false); + // create random number of clients + int numClients = rnd.nextInt(20) + 10; + List<Future> futures = new ArrayList<>(); + ExecutorService executor = Executors.newFixedThreadPool(numClients); + for (int i = 0; i < numClients; i++) { + Future future = executor.submit(new SimpleJMXClient(serverUrl, credentials)); + futures.add(future); + } + try { + for (Future future : futures) { + future.get(); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } catch (ExecutionException ex) { + throw ex.getCause(); + } finally { + executor.shutdown(); + } + + Assert.assertEquals(isPasswordFileHashed(), true); + } + + @Test + public void testPasswordChange() throws IOException { + try { + Map<String, String> credentials = generateClearTextPasswordFile(); + JMXServiceURL serverUrl = createServerSide(true); + Assert.assertEquals(isPasswordFileHashed(), false); + + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) { + cc.getMBeanServerConnection(); + } + } + Assert.assertEquals(isPasswordFileHashed(), true); + + // Read the file back. Add new entries. Change passwords for few + BufferedReader br = new BufferedReader(new FileReader(getPasswordFilePath())); + String line; + StringBuilder sbuild = new StringBuilder(); + while ((line = br.readLine()) != null) { + if (line.trim().startsWith("#")) { + sbuild.append(line).append("\n"); + continue; + } + String[] tokens = line.split("\\s+"); + // Change password for random entries + if ((tokens.length == 4 || tokens.length == 3) && rnd.nextBoolean()) { + String password = randomWord(); + credentials.put(tokens[0], password); + sbuild.append(tokens[0]).append(" ").append(password).append("\n"); + } else { + sbuild.append(line).append("\n"); + } + } + + // Add new entries in clear + int newentries = rnd.nextInt(2) + 3; + for (int i = 0; i < newentries; i++) { + String username = randomWord(); + String password = randomWord(); + credentials.put(username, password); + sbuild.append(username).append(" ").append(password).append("\n"); + } + + // Add new entries as a hash + int numentries = rnd.nextInt(2) + 3; + for (int i = 0; i < numentries; i++) { + String username = randomWord(); + String password = randomWord(); + String alg = hashAlgs[rnd.nextInt(hashAlgs.length)]; + String[] b64str = getHash(alg, password); + credentials.put(username, password); + sbuild.append(username).append(" ").append(b64str[0]) + .append(" ").append(b64str[1]).append(" ") + .append(alg).append("\n"); + } + + try (BufferedWriter bw = new BufferedWriter(new FileWriter(getPasswordFilePath()))) { + bw.write(sbuild.toString()); + } + + for (Map.Entry<String, String> entry : credentials.entrySet()) { + HashMap<String, Object> env = new HashMap<>(); + env.put("jmx.remote.credentials", + new String[]{entry.getKey(), entry.getValue()}); + try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) { + cc.getMBeanServerConnection(); + } + } + } finally { + cs.stop(); + } + } + + @Test + public void testDefaultAgent() throws Exception { + List<String> pbArgs = new ArrayList<>(); + int port = Utils.getFreePort(); + generateClearTextPasswordFile(); + + // This will run only on a POSIX compliant system + if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { + return; + } + + // Make sure only owner is able to read/write the file or else + // default agent will fail to start + File file = new File(getPasswordFilePath()); + Set<PosixFilePermission> perms = new HashSet<>(); + perms.add(PosixFilePermission.OWNER_READ); + perms.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(file.toPath(), perms); + + pbArgs.add("-cp"); + pbArgs.add(System.getProperty("test.class.path")); + + pbArgs.add("-Dcom.sun.management.jmxremote.port=" + port); + pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true"); + pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath()); + pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false"); + pbArgs.add(TestApp.class.getSimpleName()); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + pbArgs.toArray(new String[0])); + Process process = ProcessTools.startProcess( + TestApp.class.getSimpleName(), + pb); + + if (process.waitFor() != 0) { + throw new RuntimeException("Test Failed : Error starting default agent"); + } + Assert.assertEquals(isPasswordFileHashed(), true); + } + + @Test + public void testDefaultAgentNoHash() throws Exception { + List<String> pbArgs = new ArrayList<>(); + int port = Utils.getFreePort(); + generateClearTextPasswordFile(); + + // This will run only on a POSIX compliant system + if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { + return; + } + + // Make sure only owner is able to read/write the file or else + // default agent will fail to start + File file = new File(getPasswordFilePath()); + Set<PosixFilePermission> perms = new HashSet<>(); + perms.add(PosixFilePermission.OWNER_READ); + perms.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(file.toPath(), perms); + + pbArgs.add("-cp"); + pbArgs.add(System.getProperty("test.class.path")); + + pbArgs.add("-Dcom.sun.management.jmxremote.port=" + port); + pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true"); + pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath()); + pbArgs.add("-Dcom.sun.management.jmxremote.password.toHashes=false"); + pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false"); + pbArgs.add(TestApp.class.getSimpleName()); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + pbArgs.toArray(new String[0])); + Process process = ProcessTools.startProcess( + TestApp.class.getSimpleName(), + pb); + + if (process.waitFor() != 0) { + throw new RuntimeException("Test Failed : Error starting default agent"); + } + Assert.assertEquals(isPasswordFileHashed(), false); + } + + @AfterClass + public void cleanUp() { + File file = new File(getPasswordFilePath()); + if (file.exists()) { + file.delete(); + } + } +} + +class TestApp { + + public static void main(String[] args) throws IOException { + try { + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + + System.getProperty("com.sun.management.jmxremote.port") + "/jmxrmi"); + Map<String, Object> env = new HashMap<>(1); + // any dummy credentials will do. We just have to trigger password hashing + env.put("jmx.remote.credentials", new String[]{"a", "a"}); + try (JMXConnector cc = JMXConnectorFactory.connect(url, env)) { + cc.getMBeanServerConnection(); + } + } catch (SecurityException ex) { + // Catch authentication failure here + } + } +} From 386e258e20eba9af1ad6cf89ae40390566998560 Mon Sep 17 00:00:00 2001 From: Roland Westrelin <roland@openjdk.org> Date: Tue, 28 Nov 2017 11:59:16 +0100 Subject: [PATCH 075/165] 8186027: C2: loop strip mining Reviewed-by: kvn, neliasso --- src/hotspot/share/gc/g1/g1Arguments.cpp | 10 + src/hotspot/share/opto/c2_globals.hpp | 8 + src/hotspot/share/opto/cfgnode.cpp | 12 + src/hotspot/share/opto/classes.hpp | 2 + src/hotspot/share/opto/compile.cpp | 10 + src/hotspot/share/opto/ifnode.cpp | 1 + src/hotspot/share/opto/loopPredicate.cpp | 12 +- src/hotspot/share/opto/loopTransform.cpp | 95 ++- src/hotspot/share/opto/loopUnswitch.cpp | 40 +- src/hotspot/share/opto/loopnode.cpp | 603 +++++++++++++++++- src/hotspot/share/opto/loopnode.hpp | 82 ++- src/hotspot/share/opto/loopopts.cpp | 440 +++++++++---- src/hotspot/share/opto/macro.cpp | 9 +- src/hotspot/share/opto/node.hpp | 14 +- src/hotspot/share/opto/superword.cpp | 3 +- src/hotspot/share/runtime/arguments.cpp | 15 + .../UseCountedLoopSafepointsTest.java | 3 +- 17 files changed, 1150 insertions(+), 209 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index bfe27a1cfae..9fcb602e0ce 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -92,6 +92,16 @@ void G1Arguments::initialize_flags() { } log_trace(gc)("MarkStackSize: %uk MarkStackSizeMax: %uk", (unsigned int) (MarkStackSize / K), (uint) (MarkStackSizeMax / K)); + +#ifdef COMPILER2 + // Enable loop strip mining to offer better pause time guarantees + if (FLAG_IS_DEFAULT(UseCountedLoopSafepoints)) { + FLAG_SET_DEFAULT(UseCountedLoopSafepoints, true); + } + if (UseCountedLoopSafepoints && FLAG_IS_DEFAULT(LoopStripMiningIter)) { + FLAG_SET_DEFAULT(LoopStripMiningIter, 1000); + } +#endif } CollectedHeap* G1Arguments::create_heap() { diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index e1c859c3af9..9f105299402 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -740,6 +740,14 @@ \ develop(bool, RenumberLiveNodes, true, \ "Renumber live nodes") \ + \ + product(uintx, LoopStripMiningIter, 0, \ + "Number of iterations in strip mined loop") \ + range(0, max_juint) \ + \ + product(uintx, LoopStripMiningIterShortLoop, 0, \ + "Loop with fewer iterations are not strip mined") \ + range(0, max_juint) \ C2_FLAGS(DECLARE_DEVELOPER_FLAG, \ DECLARE_PD_DEVELOPER_FLAG, \ diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 272c1340fbb..e2269104bf7 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -571,6 +571,18 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { return NULL; } else if (can_reshape) { // Optimization phase - remove the node PhaseIterGVN *igvn = phase->is_IterGVN(); + // Strip mined (inner) loop is going away, remove outer loop. + if (is_CountedLoop() && + as_Loop()->is_strip_mined()) { + Node* outer_sfpt = as_CountedLoop()->outer_safepoint(); + Node* outer_out = as_CountedLoop()->outer_loop_exit(); + if (outer_sfpt != NULL && outer_out != NULL) { + Node* in = outer_sfpt->in(0); + igvn->replace_node(outer_out, in); + LoopNode* outer = as_CountedLoop()->outer_loop(); + igvn->replace_input_of(outer, LoopNode::LoopBackControl, igvn->C->top()); + } + } Node *parent_ctrl; if( cnt == 0 ) { assert( req() == 1, "no inputs expected" ); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index b95178b8879..235021f8660 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -133,6 +133,8 @@ macro(ConvL2F) macro(ConvL2I) macro(CountedLoop) macro(CountedLoopEnd) +macro(OuterStripMinedLoop) +macro(OuterStripMinedLoopEnd) macro(CountLeadingZerosI) macro(CountLeadingZerosL) macro(CountTrailingZerosI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6fe374640b6..efb7000e37b 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3244,9 +3244,11 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { break; case Op_Loop: case Op_CountedLoop: + case Op_OuterStripMinedLoop: if (n->as_Loop()->is_inner_loop()) { frc.inc_inner_loop_count(); } + n->as_Loop()->verify_strip_mined(0); break; case Op_LShiftI: case Op_RShiftI: @@ -3525,6 +3527,14 @@ bool Compile::final_graph_reshaping() { record_method_not_compilable("infinite loop"); return true; // Found unvisited kid; must be unreach } + + // Here so verification code in final_graph_reshaping_walk() + // always see an OuterStripMinedLoopEnd + if (n->is_OuterStripMinedLoopEnd()) { + IfNode* init_iff = n->as_If(); + Node* iff = new IfNode(init_iff->in(0), init_iff->in(1), init_iff->_prob, init_iff->_fcnt); + n->subsume_by(iff, this); + } } // If original bytecodes contained a mixture of floats and doubles diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 7865e3ca882..3437dfed372 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -117,6 +117,7 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { // No intervening control, like a simple Call Node *r = iff->in(0); if( !r->is_Region() ) return NULL; + if (r->is_Loop() && r->in(LoopNode::LoopBackControl)->is_top()) return NULL; // going away anyway if( phi->region() != r ) return NULL; // No other users of the cmp/bool if (b->outcnt() != 1 || cmp->outcnt() != 1) { diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 0de17405ccc..9de54bb372a 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -515,8 +515,8 @@ class Invariance : public StackObj { _visited(area), _invariant(area), _stack(area, 10 /* guess */), _clone_visited(area), _old_new(area) { - Node* head = _lpt->_head; - Node* entry = head->in(LoopNode::EntryControl); + LoopNode* head = _lpt->_head->as_Loop(); + Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl); if (entry->outcnt() != 1) { // If a node is pinned between the predicates and the loop // entry, we won't be able to move any node in the loop that @@ -801,6 +801,10 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { return false; } + if (head->is_OuterStripMinedLoop()) { + return false; + } + CountedLoopNode *cl = NULL; if (head->is_valid_counted_loop()) { cl = head->as_CountedLoop(); @@ -812,7 +816,7 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { cl = NULL; } - Node* entry = head->in(LoopNode::EntryControl); + Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl); ProjNode *predicate_proj = NULL; // Loop limit check predicate should be near the loop. predicate_proj = find_predicate_insertion_point(entry, Deoptimization::Reason_loop_limit_check); @@ -1007,6 +1011,8 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { } #endif + head->verify_strip_mined(1); + return hoisted; } diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 2bad280129a..28a270ef4c4 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -67,6 +67,16 @@ void IdealLoopTree::record_for_igvn() { Node *n = _body.at(i); _phase->_igvn._worklist.push(n); } + // put body of outer strip mined loop on igvn work list as well + if (_head->is_CountedLoop() && _head->as_Loop()->is_strip_mined()) { + CountedLoopNode* l = _head->as_CountedLoop(); + _phase->_igvn._worklist.push(l->outer_loop()); + _phase->_igvn._worklist.push(l->outer_loop_tail()); + _phase->_igvn._worklist.push(l->outer_loop_end()); + _phase->_igvn._worklist.push(l->outer_safepoint()); + Node* cle_out = _head->as_CountedLoop()->loopexit()->proj_out(false); + _phase->_igvn._worklist.push(cle_out); + } } //------------------------------compute_exact_trip_count----------------------- @@ -494,7 +504,7 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) { loop->dump_head(); } #endif - Node* head = loop->_head; + LoopNode* head = loop->_head->as_Loop(); bool counted_loop = head->is_CountedLoop(); if (counted_loop) { CountedLoopNode *cl = head->as_CountedLoop(); @@ -514,7 +524,7 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) { // Step 1: Clone the loop body. The clone becomes the peeled iteration. // The pre-loop illegally has 2 control users (old & new loops). - clone_loop( loop, old_new, dom_depth(head) ); + clone_loop(loop, old_new, dom_depth(head->skip_strip_mined()), ControlAroundStripMined); // Step 2: Make the old-loop fall-in edges point to the peeled iteration. // Do this by making the old-loop fall-in edges act as if they came @@ -523,8 +533,8 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) { // the pre-loop with only 1 user (the new peeled iteration), but the // peeled-loop backedge has 2 users. Node* new_entry = old_new[head->in(LoopNode::LoopBackControl)->_idx]; - _igvn.hash_delete(head); - head->set_req(LoopNode::EntryControl, new_entry); + _igvn.hash_delete(head->skip_strip_mined()); + head->skip_strip_mined()->set_req(LoopNode::EntryControl, new_entry); for (DUIterator_Fast jmax, j = head->fast_outs(jmax); j < jmax; j++) { Node* old = head->fast_out(j); if (old->in(0) == loop->_head && old->req() == 3 && old->is_Phi()) { @@ -1009,8 +1019,6 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ CountedLoopEndNode *main_end = main_head->loopexit(); guarantee(main_end != NULL, "no loop exit node"); assert( main_end->outcnt() == 2, "1 true, 1 false path only" ); - uint dd_main_head = dom_depth(main_head); - uint max = main_head->outcnt(); Node *pre_header= main_head->in(LoopNode::EntryControl); Node *init = main_head->init_trip(); @@ -1043,7 +1051,16 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ // Step B1: Clone the loop body. The clone becomes the pre-loop. The main // loop pre-header illegally has 2 control users (old & new loops). - clone_loop( loop, old_new, dd_main_head ); + LoopNode* outer_main_head = main_head; + IdealLoopTree* outer_loop = loop; + if (main_head->is_strip_mined()) { + main_head->verify_strip_mined(1); + outer_main_head = main_head->outer_loop(); + outer_loop = loop->_parent; + assert(outer_loop->_head == outer_main_head, "broken loop tree"); + } + uint dd_main_head = dom_depth(outer_main_head); + clone_loop(loop, old_new, dd_main_head, ControlAroundStripMined); CountedLoopNode* pre_head = old_new[main_head->_idx]->as_CountedLoop(); CountedLoopEndNode* pre_end = old_new[main_end ->_idx]->as_CountedLoopEnd(); pre_head->set_pre_loop(main_head); @@ -1058,7 +1075,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ IfFalseNode *new_pre_exit = new IfFalseNode(pre_end); _igvn.register_new_node_with_optimizer( new_pre_exit ); set_idom(new_pre_exit, pre_end, dd_main_head); - set_loop(new_pre_exit, loop->_parent); + set_loop(new_pre_exit, outer_loop->_parent); // Step B2: Build a zero-trip guard for the main-loop. After leaving the // pre-loop, the main-loop may not execute at all. Later in life this @@ -1075,22 +1092,22 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ IfNode *min_iff = new IfNode( new_pre_exit, min_bol, PROB_ALWAYS, COUNT_UNKNOWN ); _igvn.register_new_node_with_optimizer( min_iff ); set_idom(min_iff, new_pre_exit, dd_main_head); - set_loop(min_iff, loop->_parent); + set_loop(min_iff, outer_loop->_parent); // Plug in the false-path, taken if we need to skip main-loop _igvn.hash_delete( pre_exit ); pre_exit->set_req(0, min_iff); set_idom(pre_exit, min_iff, dd_main_head); - set_idom(pre_exit->unique_out(), min_iff, dd_main_head); + set_idom(pre_exit->unique_ctrl_out(), min_iff, dd_main_head); // Make the true-path, must enter the main loop Node *min_taken = new IfTrueNode( min_iff ); _igvn.register_new_node_with_optimizer( min_taken ); set_idom(min_taken, min_iff, dd_main_head); - set_loop(min_taken, loop->_parent); + set_loop(min_taken, outer_loop->_parent); // Plug in the true path - _igvn.hash_delete( main_head ); - main_head->set_req(LoopNode::EntryControl, min_taken); - set_idom(main_head, min_taken, dd_main_head); + _igvn.hash_delete(outer_main_head); + outer_main_head->set_req(LoopNode::EntryControl, min_taken); + set_idom(outer_main_head, min_taken, dd_main_head); Arena *a = Thread::current()->resource_area(); VectorSet visited(a); @@ -1102,7 +1119,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ if( main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0 ) { Node *pre_phi = old_new[main_phi->_idx]; Node *fallpre = clone_up_backedge_goo(pre_head->back_control(), - main_head->init_control(), + main_head->skip_strip_mined()->in(LoopNode::EntryControl), pre_phi->in(LoopNode::LoopBackControl), visited, clones); _igvn.hash_delete(main_phi); @@ -1305,16 +1322,24 @@ void PhaseIdealLoop::insert_scalar_rced_post_loop(IdealLoopTree *loop, Node_List Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, CountedLoopNode *main_head, CountedLoopEndNode *main_end, Node *incr, Node *limit, CountedLoopNode *&post_head) { + IfNode* outer_main_end = main_end; + IdealLoopTree* outer_loop = loop; + if (main_head->is_strip_mined()) { + main_head->verify_strip_mined(1); + outer_main_end = main_head->outer_loop_end(); + outer_loop = loop->_parent; + assert(outer_loop->_head == main_head->in(LoopNode::EntryControl), "broken loop tree"); + } //------------------------------ // Step A: Create a new post-Loop. - Node* main_exit = main_end->proj_out(false); + Node* main_exit = outer_main_end->proj_out(false); assert(main_exit->Opcode() == Op_IfFalse, ""); int dd_main_exit = dom_depth(main_exit); // Step A1: Clone the loop body of main. The clone becomes the post-loop. // The main loop pre-header illegally has 2 control users (old & new loops). - clone_loop(loop, old_new, dd_main_exit); + clone_loop(loop, old_new, dd_main_exit, ControlAroundStripMined); assert(old_new[main_end->_idx]->Opcode() == Op_CountedLoopEnd, ""); post_head = old_new[main_head->_idx]->as_CountedLoop(); post_head->set_normal_loop(); @@ -1325,10 +1350,10 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, post_end->_prob = PROB_FAIR; // Build the main-loop normal exit. - IfFalseNode *new_main_exit = new IfFalseNode(main_end); + IfFalseNode *new_main_exit = new IfFalseNode(outer_main_end); _igvn.register_new_node_with_optimizer(new_main_exit); - set_idom(new_main_exit, main_end, dd_main_exit); - set_loop(new_main_exit, loop->_parent); + set_idom(new_main_exit, outer_main_end, dd_main_exit); + set_loop(new_main_exit, outer_loop->_parent); // Step A2: Build a zero-trip guard for the post-loop. After leaving the // main-loop, the post-loop may not execute at all. We 'opaque' the incr @@ -1346,7 +1371,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, IfNode *zer_iff = new IfNode(new_main_exit, zer_bol, PROB_FAIR, COUNT_UNKNOWN); _igvn.register_new_node_with_optimizer(zer_iff); set_idom(zer_iff, new_main_exit, dd_main_exit); - set_loop(zer_iff, loop->_parent); + set_loop(zer_iff, outer_loop->_parent); // Plug in the false-path, taken if we need to skip this post-loop _igvn.replace_input_of(main_exit, 0, zer_iff); @@ -1356,7 +1381,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, Node *zer_taken = new IfTrueNode(zer_iff); _igvn.register_new_node_with_optimizer(zer_taken); set_idom(zer_taken, zer_iff, dd_main_exit); - set_loop(zer_taken, loop->_parent); + set_loop(zer_taken, outer_loop->_parent); // Plug in the true path _igvn.hash_delete(post_head); post_head->set_req(LoopNode::EntryControl, zer_taken); @@ -1431,7 +1456,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad // if rounds of unroll,optimize are making progress loop_head->set_node_count_before_unroll(loop->_body.size()); - Node *ctrl = loop_head->in(LoopNode::EntryControl); + Node *ctrl = loop_head->skip_strip_mined()->in(LoopNode::EntryControl); Node *limit = loop_head->limit(); Node *init = loop_head->init_trip(); Node *stride = loop_head->stride(); @@ -1610,7 +1635,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad // represents the odd iterations; since the loop trips an even number of // times its backedge is never taken. Kill the backedge. uint dd = dom_depth(loop_head); - clone_loop( loop, old_new, dd ); + clone_loop(loop, old_new, dd, IgnoreStripMined); // Make backedges of the clone equal to backedges of the original. // Make the fall-in from the original come from the fall-out of the clone. @@ -1653,6 +1678,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad } loop->record_for_igvn(); + loop_head->clear_strip_mined(); #ifndef PRODUCT if (C->do_vector_loop() && (PrintOpto && (VerifyLoopOptimizations || TraceLoopOpts))) { @@ -2047,7 +2073,7 @@ int PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { } // Need to find the main-loop zero-trip guard - Node *ctrl = cl->in(LoopNode::EntryControl); + Node *ctrl = cl->skip_strip_mined()->in(LoopNode::EntryControl); Node *iffm = ctrl->in(0); Node *opqzm = iffm->in(1)->in(1)->in(2); assert(opqzm->in(1) == main_limit, "do not understand situation"); @@ -2413,7 +2439,6 @@ bool PhaseIdealLoop::multi_version_post_loops(IdealLoopTree *rce_loop, IdealLoop _igvn.register_new_node_with_optimizer(cur_min); Node *cmp_node = rce_loop_end->cmp_node(); _igvn.replace_input_of(cmp_node, 2, cur_min); - set_idom(cmp_node, cur_min, dom_depth(ctrl)); set_ctrl(cur_min, ctrl); set_loop(cur_min, rce_loop->_parent); @@ -2519,7 +2544,7 @@ void IdealLoopTree::adjust_loop_exit_prob( PhaseIdealLoop *phase ) { #ifdef ASSERT static CountedLoopNode* locate_pre_from_main(CountedLoopNode *cl) { - Node *ctrl = cl->in(LoopNode::EntryControl); + Node *ctrl = cl->skip_strip_mined()->in(LoopNode::EntryControl); assert(ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, ""); Node *iffm = ctrl->in(0); assert(iffm->Opcode() == Op_If, ""); @@ -2558,7 +2583,7 @@ void IdealLoopTree::remove_main_post_loops(CountedLoopNode *cl, PhaseIdealLoop * } assert(locate_pre_from_main(main_head) == cl, "bad main loop"); - Node* main_iff = main_head->in(LoopNode::EntryControl)->in(0); + Node* main_iff = main_head->skip_strip_mined()->in(LoopNode::EntryControl)->in(0); // Remove the Opaque1Node of the pre loop and make it execute all iterations phase->_igvn.replace_input_of(pre_cmp, 2, pre_cmp->in(2)->in(2)); @@ -2619,7 +2644,7 @@ bool IdealLoopTree::policy_do_remove_empty_loop( PhaseIdealLoop *phase ) { } if (needs_guard) { // Check for an obvious zero trip guard. - Node* inctrl = PhaseIdealLoop::skip_loop_predicates(cl->in(LoopNode::EntryControl)); + Node* inctrl = PhaseIdealLoop::skip_loop_predicates(cl->skip_strip_mined()->in(LoopNode::EntryControl)); if (inctrl->Opcode() == Op_IfTrue || inctrl->Opcode() == Op_IfFalse) { bool maybe_swapped = (inctrl->Opcode() == Op_IfFalse); // The test should look like just the backedge of a CountedLoop @@ -3167,6 +3192,8 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { return false; } + head->verify_strip_mined(1); + // Check that the body only contains a store of a loop invariant // value that is indexed by the loop phi. Node* store = NULL; @@ -3288,6 +3315,16 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { } */ + if (head->is_strip_mined()) { + // Inner strip mined loop goes away so get rid of outer strip + // mined loop + Node* outer_sfpt = head->outer_safepoint(); + Node* in = outer_sfpt->in(0); + Node* outer_out = head->outer_loop_exit(); + lazy_replace(outer_out, in); + _igvn.replace_input_of(outer_sfpt, 0, C->top()); + } + // Redirect the old control and memory edges that are outside the loop. // Sometimes the memory phi of the head is used as the outgoing // state of the loop. It's safe in this case to replace it with the diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index 72201c2e282..9940b333fe0 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -132,11 +132,11 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { head->as_CountedLoop()->set_normal_loop(); } - ProjNode* proj_true = create_slow_version_of_loop(loop, old_new, unswitch_iff->Opcode()); + ProjNode* proj_true = create_slow_version_of_loop(loop, old_new, unswitch_iff->Opcode(), CloneIncludesStripMined); #ifdef ASSERT Node* uniqc = proj_true->unique_ctrl_out(); - Node* entry = head->in(LoopNode::EntryControl); + Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl); Node* predicate = find_predicate(entry); if (predicate != NULL && UseLoopPredicate) { // We may have two predicates, find first. @@ -145,7 +145,8 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { } if (predicate != NULL) predicate = predicate->in(0); assert(proj_true->is_IfTrue() && - (predicate == NULL && uniqc == head || + (predicate == NULL && uniqc == head && !head->is_strip_mined() || + predicate == NULL && uniqc == head->in(LoopNode::EntryControl) && head->is_strip_mined() || predicate != NULL && uniqc == predicate), "by construction"); #endif // Increment unswitch count @@ -223,13 +224,16 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { // Return control projection of the entry to the fast version. ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop, Node_List &old_new, - int opcode) { + int opcode, + CloneLoopMode mode) { LoopNode* head = loop->_head->as_Loop(); bool counted_loop = head->is_CountedLoop(); - Node* entry = head->in(LoopNode::EntryControl); + Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl); _igvn.rehash_node_delayed(entry); IdealLoopTree* outer_loop = loop->_parent; + head->verify_strip_mined(1); + Node *cont = _igvn.intcon(1); set_ctrl(cont, C->root()); Node* opq = new Opaque1Node(C, cont); @@ -247,19 +251,21 @@ ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop, // Clone the loop body. The clone becomes the fast loop. The // original pre-header will (illegally) have 3 control users // (old & new loops & new if). - clone_loop(loop, old_new, dom_depth(head), iff); + clone_loop(loop, old_new, dom_depth(head->skip_strip_mined()), mode, iff); assert(old_new[head->_idx]->is_Loop(), "" ); // Fast (true) control Node* iffast_pred = clone_loop_predicates(entry, iffast, !counted_loop); - _igvn.replace_input_of(head, LoopNode::EntryControl, iffast_pred); - set_idom(head, iffast_pred, dom_depth(head)); // Slow (false) control Node* ifslow_pred = clone_loop_predicates(entry, ifslow, !counted_loop); - LoopNode* slow_head = old_new[head->_idx]->as_Loop(); - _igvn.replace_input_of(slow_head, LoopNode::EntryControl, ifslow_pred); - set_idom(slow_head, ifslow_pred, dom_depth(slow_head)); + + Node* l = head->skip_strip_mined(); + _igvn.replace_input_of(l, LoopNode::EntryControl, iffast_pred); + set_idom(l, iffast_pred, dom_depth(l)); + LoopNode* slow_l = old_new[head->_idx]->as_Loop()->skip_strip_mined(); + _igvn.replace_input_of(slow_l, LoopNode::EntryControl, ifslow_pred); + set_idom(slow_l, ifslow_pred, dom_depth(l)); recompute_dom_depth(); @@ -270,9 +276,9 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co Node_List old_new; LoopNode* head = loop->_head->as_Loop(); bool counted_loop = head->is_CountedLoop(); - Node* entry = head->in(LoopNode::EntryControl); + Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl); _igvn.rehash_node_delayed(entry); - IdealLoopTree* outer_loop = loop->_parent; + IdealLoopTree* outer_loop = head->is_strip_mined() ? loop->_parent->_parent : loop->_parent; ConINode* const_1 = _igvn.intcon(1); set_ctrl(const_1, C->root()); @@ -286,7 +292,7 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co // Clone the loop body. The clone becomes the fast loop. The // original pre-header will (illegally) have 3 control users // (old & new loops & new if). - clone_loop(loop, old_new, dom_depth(head), iff); + clone_loop(loop, old_new, dom_depth(head), CloneIncludesStripMined, iff); assert(old_new[head->_idx]->is_Loop(), "" ); LoopNode* slow_head = old_new[head->_idx]->as_Loop(); @@ -303,9 +309,9 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co #endif // Fast (true) control - _igvn.replace_input_of(head, LoopNode::EntryControl, iffast); + _igvn.replace_input_of(head->skip_strip_mined(), LoopNode::EntryControl, iffast); // Slow (false) control - _igvn.replace_input_of(slow_head, LoopNode::EntryControl, ifslow); + _igvn.replace_input_of(slow_head->skip_strip_mined(), LoopNode::EntryControl, ifslow); recompute_dom_depth(); @@ -394,7 +400,7 @@ bool CountedLoopReserveKit::create_reserve() { return false; } - Node* ifslow_pred = _lp_reserved->as_CountedLoop()->in(LoopNode::EntryControl); + Node* ifslow_pred = _lp_reserved->skip_strip_mined()->in(LoopNode::EntryControl); if (!ifslow_pred->is_IfFalse()) { return false; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 5f003d304ff..1e5af607642 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -261,8 +261,68 @@ void PhaseIdealLoop::set_subtree_ctrl( Node *n ) { set_early_ctrl( n ); } +// Create a skeleton strip mined outer loop: a Loop head before the +// inner strip mined loop, a safepoint and an exit condition guarded +// by an opaque node after the inner strip mined loop with a backedge +// to the loop head. The inner strip mined loop is left as it is. Only +// once loop optimizations are over, do we adjust the inner loop exit +// condition to limit its number of iterations, set the outer loop +// exit condition and add Phis to the outer loop head. Some loop +// optimizations that operate on the inner strip mined loop need to be +// aware of the outer strip mined loop: loop unswitching needs to +// clone the outer loop as well as the inner, unrolling needs to only +// clone the inner loop etc. No optimizations need to change the outer +// strip mined loop as it is only a skeleton. +IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control, + IdealLoopTree* loop, float cl_prob, float le_fcnt, + Node*& entry_control, Node*& iffalse) { + Node* outer_test = _igvn.intcon(0); + set_ctrl(outer_test, C->root()); + Node *orig = iffalse; + iffalse = iffalse->clone(); + _igvn.register_new_node_with_optimizer(iffalse); + set_idom(iffalse, idom(orig), dom_depth(orig)); + + IfNode *outer_le = new OuterStripMinedLoopEndNode(iffalse, outer_test, cl_prob, le_fcnt); + Node *outer_ift = new IfTrueNode (outer_le); + Node* outer_iff = orig; + _igvn.replace_input_of(outer_iff, 0, outer_le); + + LoopNode *outer_l = new OuterStripMinedLoopNode(C, init_control, outer_ift); + entry_control = outer_l; + + IdealLoopTree* outer_ilt = new IdealLoopTree(this, outer_l, outer_ift); + IdealLoopTree* parent = loop->_parent; + IdealLoopTree* sibling = parent->_child; + if (sibling == loop) { + parent->_child = outer_ilt; + } else { + while (sibling->_next != loop) { + sibling = sibling->_next; + } + sibling->_next = outer_ilt; + } + outer_ilt->_next = loop->_next; + outer_ilt->_parent = parent; + outer_ilt->_child = loop; + outer_ilt->_nest = loop->_nest; + loop->_parent = outer_ilt; + loop->_next = NULL; + loop->_nest++; + + set_loop(iffalse, outer_ilt); + register_control(outer_le, outer_ilt, iffalse); + register_control(outer_ift, outer_ilt, outer_le); + set_idom(outer_iff, outer_le, dom_depth(outer_le)); + _igvn.register_new_node_with_optimizer(outer_l); + set_loop(outer_l, outer_ilt); + set_idom(outer_l, init_control, dom_depth(init_control)+1); + + return outer_ilt; +} + //------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { +bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) { PhaseGVN *gvn = &_igvn; // Counted loop head must be a good RegionNode with only 3 not NULL @@ -280,7 +340,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { // Allow funny placement of Safepoint if (back_control->Opcode() == Op_SafePoint) { - if (UseCountedLoopSafepoints) { + if (LoopStripMiningIter != 0) { // Leaving the safepoint on the backedge and creating a // CountedLoop will confuse optimizations. We can't move the // safepoint around because its jvm state wouldn't match a new @@ -600,7 +660,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { } set_subtree_ctrl( limit ); - if (!UseCountedLoopSafepoints) { + if (LoopStripMiningIter == 0) { // Check for SafePoint on backedge and remove Node *sfpt = x->in(LoopNode::LoopBackControl); if (sfpt->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt)) { @@ -683,8 +743,20 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { assert(iff->outcnt() == 0, "should be dead now"); lazy_replace( iff, le ); // fix 'get_ctrl' + Node *sfpt2 = le->in(0); + + Node* entry_control = init_control; + bool strip_mine_loop = LoopStripMiningIter > 1 && loop->_child == NULL && + sfpt2->Opcode() == Op_SafePoint && !loop->_has_call; + IdealLoopTree* outer_ilt = NULL; + if (strip_mine_loop) { + outer_ilt = create_outer_strip_mined_loop(test, cmp, init_control, loop, + cl_prob, le->_fcnt, entry_control, + iffalse); + } + // Now setup a new CountedLoopNode to replace the existing LoopNode - CountedLoopNode *l = new CountedLoopNode(init_control, back_control); + CountedLoopNode *l = new CountedLoopNode(entry_control, back_control); l->set_unswitch_count(x->as_Loop()->unswitch_count()); // Preserve // The following assert is approximately true, and defines the intention // of can_be_counted_loop. It fails, however, because phase->type @@ -696,12 +768,19 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { // Fix all data nodes placed at the old loop head. // Uses the lazy-update mechanism of 'get_ctrl'. lazy_replace( x, l ); - set_idom(l, init_control, dom_depth(x)); + set_idom(l, entry_control, dom_depth(entry_control) + 1); - if (!UseCountedLoopSafepoints) { + if (LoopStripMiningIter == 0 || strip_mine_loop) { // Check for immediately preceding SafePoint and remove - Node *sfpt2 = le->in(0); - if (sfpt2->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt2)) { + if (sfpt2->Opcode() == Op_SafePoint && (LoopStripMiningIter != 0 || is_deleteable_safept(sfpt2))) { + if (strip_mine_loop) { + Node* outer_le = outer_ilt->_tail->in(0); + Node* sfpt = sfpt2->clone(); + sfpt->set_req(0, iffalse); + outer_le->set_req(0, sfpt); + register_control(sfpt, outer_ilt, iffalse); + set_idom(outer_le, sfpt, dom_depth(sfpt)); + } lazy_replace( sfpt2, sfpt2->in(TypeFunc::Control)); if (loop->_safepts != NULL) { loop->_safepts->yank(sfpt2); @@ -730,6 +809,13 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { // bounds l->phi()->as_Phi()->set_type(l->phi()->Value(&_igvn)); + if (strip_mine_loop) { + l->mark_strip_mined(); + l->verify_strip_mined(1); + outer_ilt->_head->as_Loop()->verify_strip_mined(1); + loop = outer_ilt; + } + return true; } @@ -776,12 +862,93 @@ Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) { // Return a node which is more "ideal" than the current node. // Attempt to convert into a counted-loop. Node *LoopNode::Ideal(PhaseGVN *phase, bool can_reshape) { - if (!can_be_counted_loop(phase)) { + if (!can_be_counted_loop(phase) && !is_OuterStripMinedLoop()) { phase->C->set_major_progress(); } return RegionNode::Ideal(phase, can_reshape); } +void LoopNode::verify_strip_mined(int expect_skeleton) const { +#ifdef ASSERT + const OuterStripMinedLoopNode* outer = NULL; + const CountedLoopNode* inner = NULL; + if (is_strip_mined()) { + assert(is_CountedLoop(), "no Loop should be marked strip mined"); + inner = as_CountedLoop(); + outer = inner->in(LoopNode::EntryControl)->as_OuterStripMinedLoop(); + } else if (is_OuterStripMinedLoop()) { + outer = this->as_OuterStripMinedLoop(); + inner = outer->unique_ctrl_out()->as_CountedLoop(); + assert(!is_strip_mined(), "outer loop shouldn't be marked strip mined"); + } + if (inner != NULL || outer != NULL) { + assert(inner != NULL && outer != NULL, "missing loop in strip mined nest"); + Node* outer_tail = outer->in(LoopNode::LoopBackControl); + Node* outer_le = outer_tail->in(0); + assert(outer_le->Opcode() == Op_OuterStripMinedLoopEnd, "tail of outer loop should be an If"); + Node* sfpt = outer_le->in(0); + assert(sfpt->Opcode() == Op_SafePoint, "where's the safepoint?"); + Node* inner_out = sfpt->in(0); + if (inner_out->outcnt() != 1) { + ResourceMark rm; + Unique_Node_List wq; + + for (DUIterator_Fast imax, i = inner_out->fast_outs(imax); i < imax; i++) { + Node* u = inner_out->fast_out(i); + if (u == sfpt) { + continue; + } + wq.clear(); + wq.push(u); + bool found_sfpt = false; + for (uint next = 0; next < wq.size() && !found_sfpt; next++) { + Node *n = wq.at(next); + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && !found_sfpt; i++) { + Node* u = n->fast_out(i); + if (u == sfpt) { + found_sfpt = true; + } + if (!u->is_CFG()) { + wq.push(u); + } + } + } + assert(found_sfpt, "no node in loop that's not input to safepoint"); + } + } + CountedLoopEndNode* cle = inner_out->in(0)->as_CountedLoopEnd(); + assert(cle == inner->loopexit(), "mismatch"); + bool has_skeleton = outer_le->in(1)->bottom_type()->singleton() && outer_le->in(1)->bottom_type()->is_int()->get_con() == 0; + if (has_skeleton) { + assert(expect_skeleton == 1 || expect_skeleton == -1, "unexpected skeleton node"); + assert(outer->outcnt() == 2, "only phis"); + } else { + assert(expect_skeleton == 0 || expect_skeleton == -1, "no skeleton node?"); + uint phis = 0; + for (DUIterator_Fast imax, i = inner->fast_outs(imax); i < imax; i++) { + Node* u = inner->fast_out(i); + if (u->is_Phi()) { + phis++; + } + } + for (DUIterator_Fast imax, i = outer->fast_outs(imax); i < imax; i++) { + Node* u = outer->fast_out(i); + assert(u == outer || u == inner || u->is_Phi(), "nothing between inner and outer loop"); + } + uint stores = 0; + for (DUIterator_Fast imax, i = inner_out->fast_outs(imax); i < imax; i++) { + Node* u = inner_out->fast_out(i); + if (u->is_Store()) { + stores++; + } + } + assert(outer->outcnt() >= phis + 2 && outer->outcnt() <= phis + 2 + stores + 1, "only phis"); + } + assert(sfpt->outcnt() == 1, "no data node"); + assert(outer_tail->outcnt() == 1 || !has_skeleton, "no data node"); + } +#endif +} //============================================================================= //------------------------------Ideal------------------------------------------ @@ -802,6 +969,7 @@ void CountedLoopNode::dump_spec(outputStream *st) const { if (is_pre_loop ()) st->print("pre of N%d" , _main_idx); if (is_main_loop()) st->print("main of N%d", _idx); if (is_post_loop()) st->print("post of N%d", _main_idx); + if (is_strip_mined()) st->print(" strip mined"); } #endif @@ -990,6 +1158,365 @@ Node* CountedLoopNode::match_incr_with_optional_truncation( return NULL; } +LoopNode* CountedLoopNode::skip_strip_mined(int expect_opaq) { + if (is_strip_mined()) { + verify_strip_mined(expect_opaq); + return in(EntryControl)->as_Loop(); + } + return this; +} + +OuterStripMinedLoopNode* CountedLoopNode::outer_loop() const { + assert(is_strip_mined(), "not a strip mined loop"); + Node* c = in(EntryControl); + if (c == NULL || c->is_top() || !c->is_OuterStripMinedLoop()) { + return NULL; + } + return c->as_OuterStripMinedLoop(); +} + +IfTrueNode* OuterStripMinedLoopNode::outer_loop_tail() const { + Node* c = in(LoopBackControl); + if (c == NULL || c->is_top()) { + return NULL; + } + return c->as_IfTrue(); +} + +IfTrueNode* CountedLoopNode::outer_loop_tail() const { + LoopNode* l = outer_loop(); + if (l == NULL) { + return NULL; + } + return l->outer_loop_tail(); +} + +OuterStripMinedLoopEndNode* OuterStripMinedLoopNode::outer_loop_end() const { + IfTrueNode* proj = outer_loop_tail(); + if (proj == NULL) { + return NULL; + } + Node* c = proj->in(0); + if (c == NULL || c->is_top() || c->outcnt() != 2) { + return NULL; + } + return c->as_OuterStripMinedLoopEnd(); +} + +OuterStripMinedLoopEndNode* CountedLoopNode::outer_loop_end() const { + LoopNode* l = outer_loop(); + if (l == NULL) { + return NULL; + } + return l->outer_loop_end(); +} + +IfFalseNode* OuterStripMinedLoopNode::outer_loop_exit() const { + IfNode* le = outer_loop_end(); + if (le == NULL) { + return NULL; + } + Node* c = le->proj_out(false); + if (c == NULL) { + return NULL; + } + return c->as_IfFalse(); +} + +IfFalseNode* CountedLoopNode::outer_loop_exit() const { + LoopNode* l = outer_loop(); + if (l == NULL) { + return NULL; + } + return l->outer_loop_exit(); +} + +SafePointNode* OuterStripMinedLoopNode::outer_safepoint() const { + IfNode* le = outer_loop_end(); + if (le == NULL) { + return NULL; + } + Node* c = le->in(0); + if (c == NULL || c->is_top()) { + return NULL; + } + assert(c->Opcode() == Op_SafePoint, "broken outer loop"); + return c->as_SafePoint(); +} + +SafePointNode* CountedLoopNode::outer_safepoint() const { + LoopNode* l = outer_loop(); + if (l == NULL) { + return NULL; + } + return l->outer_safepoint(); +} + +void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) { + // Look for the outer & inner strip mined loop, reduce number of + // iterations of the inner loop, set exit condition of outer loop, + // construct required phi nodes for outer loop. + CountedLoopNode* inner_cl = unique_ctrl_out()->as_CountedLoop(); + assert(inner_cl->is_strip_mined(), "inner loop should be strip mined"); + Node* inner_iv_phi = inner_cl->phi(); + if (inner_iv_phi == NULL) { + return; + } + CountedLoopEndNode* inner_cle = inner_cl->loopexit(); + + int stride = inner_cl->stride_con(); + jlong scaled_iters_long = ((jlong)LoopStripMiningIter) * ABS(stride); + int scaled_iters = (int)scaled_iters_long; + int short_scaled_iters = LoopStripMiningIterShortLoop* ABS(stride); + const TypeInt* inner_iv_t = igvn->type(inner_iv_phi)->is_int(); + jlong iter_estimate = (jlong)inner_iv_t->_hi - (jlong)inner_iv_t->_lo; + assert(iter_estimate > 0, "broken"); + if ((jlong)scaled_iters != scaled_iters_long || iter_estimate <= short_scaled_iters) { + // Remove outer loop and safepoint (too few iterations) + Node* outer_sfpt = outer_safepoint(); + Node* outer_out = outer_loop_exit(); + igvn->replace_node(outer_out, outer_sfpt->in(0)); + igvn->replace_input_of(outer_sfpt, 0, igvn->C->top()); + inner_cl->clear_strip_mined(); + return; + } + if (iter_estimate <= scaled_iters_long) { + // We would only go through one iteration of + // the outer loop: drop the outer loop but + // keep the safepoint so we don't run for + // too long without a safepoint + IfNode* outer_le = outer_loop_end(); + Node* iff = igvn->transform(new IfNode(outer_le->in(0), outer_le->in(1), outer_le->_prob, outer_le->_fcnt)); + igvn->replace_node(outer_le, iff); + inner_cl->clear_strip_mined(); + return; + } + + Node* cle_tail = inner_cle->proj_out(true); + ResourceMark rm; + Node_List old_new; + if (cle_tail->outcnt() > 1) { + // Look for nodes on backedge of inner loop and clone them + Unique_Node_List backedge_nodes; + for (DUIterator_Fast imax, i = cle_tail->fast_outs(imax); i < imax; i++) { + Node* u = cle_tail->fast_out(i); + if (u != inner_cl) { + assert(!u->is_CFG(), "control flow on the backedge?"); + backedge_nodes.push(u); + } + } + uint last = igvn->C->unique(); + for (uint next = 0; next < backedge_nodes.size(); next++) { + Node* n = backedge_nodes.at(next); + old_new.map(n->_idx, n->clone()); + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + assert(!u->is_CFG(), "broken"); + if (u->_idx >= last) { + continue; + } + if (!u->is_Phi()) { + backedge_nodes.push(u); + } else { + assert(u->in(0) == inner_cl, "strange phi on the backedge"); + } + } + } + // Put the clones on the outer loop backedge + Node* le_tail = outer_loop_tail(); + for (uint next = 0; next < backedge_nodes.size(); next++) { + Node *n = old_new[backedge_nodes.at(next)->_idx]; + for (uint i = 1; i < n->req(); i++) { + if (n->in(i) != NULL && old_new[n->in(i)->_idx] != NULL) { + n->set_req(i, old_new[n->in(i)->_idx]); + } + } + if (n->in(0) != NULL) { + assert(n->in(0) == cle_tail, "node not on backedge?"); + n->set_req(0, le_tail); + } + igvn->register_new_node_with_optimizer(n); + } + } + + Node* iv_phi = NULL; + // Make a clone of each phi in the inner loop + // for the outer loop + for (uint i = 0; i < inner_cl->outcnt(); i++) { + Node* u = inner_cl->raw_out(i); + if (u->is_Phi()) { + assert(u->in(0) == inner_cl, "inconsistent"); + Node* phi = u->clone(); + phi->set_req(0, this); + Node* be = old_new[phi->in(LoopNode::LoopBackControl)->_idx]; + if (be != NULL) { + phi->set_req(LoopNode::LoopBackControl, be); + } + phi = igvn->transform(phi); + igvn->replace_input_of(u, LoopNode::EntryControl, phi); + if (u == inner_iv_phi) { + iv_phi = phi; + } + } + } + Node* cle_out = inner_cle->proj_out(false); + if (cle_out->outcnt() > 1) { + // Look for chains of stores that were sunk + // out of the inner loop and are in the outer loop + for (DUIterator_Fast imax, i = cle_out->fast_outs(imax); i < imax; i++) { + Node* u = cle_out->fast_out(i); + if (u->is_Store()) { + Node* first = u; + for(;;) { + Node* next = first->in(MemNode::Memory); + if (!next->is_Store() || next->in(0) != cle_out) { + break; + } + first = next; + } + Node* last = u; + for(;;) { + Node* next = NULL; + for (DUIterator_Fast jmax, j = last->fast_outs(jmax); j < jmax; j++) { + Node* uu = last->fast_out(j); + if (uu->is_Store() && uu->in(0) == cle_out) { + assert(next == NULL, "only one in the outer loop"); + next = uu; + } + } + if (next == NULL) { + break; + } + last = next; + } + Node* phi = NULL; + for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) { + Node* uu = fast_out(j); + if (uu->is_Phi()) { + Node* be = uu->in(LoopNode::LoopBackControl); + while (be->is_Store() && old_new[be->_idx] != NULL) { + ShouldNotReachHere(); + be = be->in(MemNode::Memory); + } + if (be == last || be == first->in(MemNode::Memory)) { + assert(phi == NULL, "only one phi"); + phi = uu; + } + } + } +#ifdef ASSERT + for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) { + Node* uu = fast_out(j); + if (uu->is_Phi() && uu->bottom_type() == Type::MEMORY) { + if (uu->adr_type() == igvn->C->get_adr_type(igvn->C->get_alias_index(u->adr_type()))) { + assert(phi == uu, "what's that phi?"); + } else if (uu->adr_type() == TypePtr::BOTTOM) { + Node* n = uu->in(LoopNode::LoopBackControl); + uint limit = igvn->C->live_nodes(); + uint i = 0; + while (n != uu) { + i++; + assert(i < limit, "infinite loop"); + if (n->is_Proj()) { + n = n->in(0); + } else if (n->is_SafePoint() || n->is_MemBar()) { + n = n->in(TypeFunc::Memory); + } else if (n->is_Phi()) { + n = n->in(1); + } else if (n->is_MergeMem()) { + n = n->as_MergeMem()->memory_at(igvn->C->get_alias_index(u->adr_type())); + } else if (n->is_Store() || n->is_LoadStore() || n->is_ClearArray()) { + n = n->in(MemNode::Memory); + } else { + n->dump(); + ShouldNotReachHere(); + } + } + } + } + } +#endif + if (phi == NULL) { + // If the an entire chains was sunk, the + // inner loop has no phi for that memory + // slice, create one for the outer loop + phi = PhiNode::make(this, first->in(MemNode::Memory), Type::MEMORY, + igvn->C->get_adr_type(igvn->C->get_alias_index(u->adr_type()))); + phi->set_req(LoopNode::LoopBackControl, last); + phi = igvn->transform(phi); + igvn->replace_input_of(first, MemNode::Memory, phi); + } else { + // Or fix the outer loop fix to include + // that chain of stores. + Node* be = phi->in(LoopNode::LoopBackControl); + while (be->is_Store() && old_new[be->_idx] != NULL) { + ShouldNotReachHere(); + be = be->in(MemNode::Memory); + } + if (be == first->in(MemNode::Memory)) { + if (be == phi->in(LoopNode::LoopBackControl)) { + igvn->replace_input_of(phi, LoopNode::LoopBackControl, last); + } else { + igvn->replace_input_of(be, MemNode::Memory, last); + } + } else { +#ifdef ASSERT + if (be == phi->in(LoopNode::LoopBackControl)) { + assert(phi->in(LoopNode::LoopBackControl) == last, ""); + } else { + assert(be->in(MemNode::Memory) == last, ""); + } +#endif + } + } + } + } + } + + if (iv_phi != NULL) { + // Now adjust the inner loop's exit condition + Node* limit = inner_cl->limit(); + Node* sub = NULL; + if (stride > 0) { + sub = igvn->transform(new SubINode(limit, iv_phi)); + } else { + sub = igvn->transform(new SubINode(iv_phi, limit)); + } + Node* min = igvn->transform(new MinINode(sub, igvn->intcon(scaled_iters))); + Node* new_limit = NULL; + if (stride > 0) { + new_limit = igvn->transform(new AddINode(min, iv_phi)); + } else { + new_limit = igvn->transform(new SubINode(iv_phi, min)); + } + igvn->replace_input_of(inner_cle->cmp_node(), 2, new_limit); + Node* cmp = inner_cle->cmp_node()->clone(); + Node* bol = inner_cle->in(CountedLoopEndNode::TestValue)->clone(); + cmp->set_req(2, limit); + bol->set_req(1, igvn->transform(cmp)); + igvn->replace_input_of(outer_loop_end(), 1, igvn->transform(bol)); + } else { + assert(false, "should be able to adjust outer loop"); + IfNode* outer_le = outer_loop_end(); + Node* iff = igvn->transform(new IfNode(outer_le->in(0), outer_le->in(1), outer_le->_prob, outer_le->_fcnt)); + igvn->replace_node(outer_le, iff); + inner_cl->clear_strip_mined(); + } +} + +const Type* OuterStripMinedLoopEndNode::Value(PhaseGVN* phase) const { + if (!in(0)) return Type::TOP; + if (phase->type(in(0)) == Type::TOP) + return Type::TOP; + + return TypeTuple::IFBOTH; +} + +Node *OuterStripMinedLoopEndNode::Ideal(PhaseGVN *phase, bool can_reshape) { + if (remove_dead_region(phase, can_reshape)) return this; + + return NULL; +} //------------------------------filtered_type-------------------------------- // Return a type based on condition control flow @@ -1778,10 +2305,11 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { if (_head->is_Loop()) _head->as_Loop()->set_inner_loop(); } + IdealLoopTree* loop = this; if (_head->is_CountedLoop() || - phase->is_counted_loop(_head, this)) { + phase->is_counted_loop(_head, loop)) { - if (!UseCountedLoopSafepoints) { + if (LoopStripMiningIter == 0 || (LoopStripMiningIter > 1 && _child == NULL)) { // Indicate we do not need a safepoint here _has_sfpt = 1; } @@ -1800,8 +2328,10 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { } // Recursively - if (_child) _child->counted_loop( phase ); - if (_next) _next ->counted_loop( phase ); + assert(loop->_child != this || (loop->_head->as_Loop()->is_OuterStripMinedLoop() && _head->as_CountedLoop()->is_strip_mined()), "what kind of loop was added?"); + assert(loop->_child != this || (loop->_child->_child == NULL && loop->_child->_next == NULL), "would miss some loops"); + if (loop->_child && loop->_child != this) loop->_child->counted_loop(phase); + if (loop->_next) loop->_next ->counted_loop(phase); } #ifndef PRODUCT @@ -1812,7 +2342,7 @@ void IdealLoopTree::dump_head( ) const { tty->print(" "); tty->print("Loop: N%d/N%d ",_head->_idx,_tail->_idx); if (_irreducible) tty->print(" IRREDUCIBLE"); - Node* entry = _head->in(LoopNode::EntryControl); + Node* entry = _head->as_Loop()->skip_strip_mined(-1)->in(LoopNode::EntryControl); Node* predicate = PhaseIdealLoop::find_predicate_insertion_point(entry, Deoptimization::Reason_loop_limit_check); if (predicate != NULL ) { tty->print(" limit_check"); @@ -1863,6 +2393,9 @@ void IdealLoopTree::dump_head( ) const { if (Verbose) { tty->print(" body={"); _body.dump_simple(); tty->print(" }"); } + if (_head->as_Loop()->is_strip_mined()) { + tty->print(" strip_mined"); + } tty->cr(); } @@ -3232,7 +3765,7 @@ bool PhaseIdealLoop::is_canonical_loop_entry(CountedLoopNode* cl) { if (!cl->is_main_loop() && !cl->is_post_loop()) { return false; } - Node* ctrl = cl->in(LoopNode::EntryControl); + Node* ctrl = cl->skip_strip_mined()->in(LoopNode::EntryControl); if (ctrl == NULL || (!ctrl->is_IfTrue() && !ctrl->is_IfFalse())) { return false; } @@ -3292,7 +3825,7 @@ Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { } while(worklist.size() != 0 && LCA != early) { Node* s = worklist.pop(); - if (s->is_Load()) { + if (s->is_Load() || s->Opcode() == Op_SafePoint) { continue; } else if (s->is_MergeMem()) { for (DUIterator_Fast imax, i = s->fast_outs(imax); i < imax; i++) { @@ -3471,6 +4004,38 @@ void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, N } } +// Verify that no data node is schedules in the outer loop of a strip +// mined loop. +void PhaseIdealLoop::verify_strip_mined_scheduling(Node *n, Node* least) { +#ifdef ASSERT + if (get_loop(least)->_nest == 0) { + return; + } + IdealLoopTree* loop = get_loop(least); + Node* head = loop->_head; + if (head->is_OuterStripMinedLoop()) { + Node* sfpt = head->as_Loop()->outer_safepoint(); + ResourceMark rm; + Unique_Node_List wq; + wq.push(sfpt); + for (uint i = 0; i < wq.size(); i++) { + Node *m = wq.at(i); + for (uint i = 1; i < m->req(); i++) { + Node* nn = m->in(i); + if (nn == n) { + return; + } + if (nn != NULL && has_ctrl(nn) && get_loop(get_ctrl(nn)) == loop) { + wq.push(nn); + } + } + } + ShouldNotReachHere(); + } +#endif +} + + //------------------------------build_loop_late_post--------------------------- // Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. // Second pass finds latest legal placement, and ideal loop placement. @@ -3580,8 +4145,9 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) { // which can inhibit range check elimination. if (least != early) { Node* ctrl_out = least->unique_ctrl_out(); - if (ctrl_out && ctrl_out->is_CountedLoop() && - least == ctrl_out->in(LoopNode::EntryControl)) { + if (ctrl_out && ctrl_out->is_Loop() && + least == ctrl_out->in(LoopNode::EntryControl) && + (ctrl_out->is_CountedLoop() || ctrl_out->is_OuterStripMinedLoop())) { Node* least_dom = idom(least); if (get_loop(least_dom)->is_member(get_loop(least))) { least = least_dom; @@ -3606,6 +4172,7 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) { // Assign discovered "here or above" point least = find_non_split_ctrl(least); + verify_strip_mined_scheduling(n, least); set_ctrl(n, least); // Collect inner loop bodies diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index f5027592c18..7e615de7e29 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -37,6 +37,7 @@ class CountedLoopNode; class IdealLoopTree; class LoopNode; class Node; +class OuterStripMinedLoopEndNode; class PhaseIdealLoop; class CountedLoopReserveKit; class VectorSet; @@ -71,7 +72,8 @@ protected: VectorizedLoop=2048, HasAtomicPostLoop=4096, HasRangeChecks=8192, - IsMultiversioned=16384}; + IsMultiversioned=16384, + StripMined=32768}; char _unswitch_count; enum { _unswitch_max=3 }; char _postloop_flags; @@ -90,6 +92,7 @@ public: int is_partial_peel_loop() const { return _loop_flags & PartialPeelLoop; } void set_partial_peel_loop() { _loop_flags |= PartialPeelLoop; } int partial_peel_has_failed() const { return _loop_flags & PartialPeelFailed; } + int is_strip_mined() const { return _loop_flags & StripMined; } void mark_partial_peel_failed() { _loop_flags |= PartialPeelFailed; } void mark_has_reductions() { _loop_flags |= HasReductions; } @@ -100,6 +103,8 @@ public: void mark_has_atomic_post_loop() { _loop_flags |= HasAtomicPostLoop; } void mark_has_range_checks() { _loop_flags |= HasRangeChecks; } void mark_is_multiversioned() { _loop_flags |= IsMultiversioned; } + void mark_strip_mined() { _loop_flags |= StripMined; } + void clear_strip_mined() { _loop_flags &= ~StripMined; } int unswitch_max() { return _unswitch_max; } int unswitch_count() { return _unswitch_count; } @@ -131,6 +136,13 @@ public: #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif + + void verify_strip_mined(int expect_skeleton) const; + virtual LoopNode* skip_strip_mined(int expect_opaq = 1) { return this; } + virtual IfTrueNode* outer_loop_tail() const { ShouldNotReachHere(); return NULL; } + virtual OuterStripMinedLoopEndNode* outer_loop_end() const { ShouldNotReachHere(); return NULL; } + virtual IfFalseNode* outer_loop_exit() const { ShouldNotReachHere(); return NULL; } + virtual SafePointNode* outer_safepoint() const { ShouldNotReachHere(); return NULL; } }; //------------------------------Counted Loops---------------------------------- @@ -278,6 +290,13 @@ public: void set_slp_max_unroll(int unroll_factor) { _slp_maximum_unroll_factor = unroll_factor; } int slp_max_unroll() const { return _slp_maximum_unroll_factor; } + virtual LoopNode* skip_strip_mined(int expect_opaq = 1); + OuterStripMinedLoopNode* outer_loop() const; + virtual IfTrueNode* outer_loop_tail() const; + virtual OuterStripMinedLoopEndNode* outer_loop_end() const; + virtual IfFalseNode* outer_loop_exit() const; + virtual SafePointNode* outer_safepoint() const; + #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif @@ -374,6 +393,40 @@ class LoopLimitNode : public Node { virtual Node* Identity(PhaseGVN* phase); }; +// Support for strip mining +class OuterStripMinedLoopNode : public LoopNode { +private: + CountedLoopNode* inner_loop() const; +public: + OuterStripMinedLoopNode(Compile* C, Node *entry, Node *backedge) + : LoopNode(entry, backedge) { + init_class_id(Class_OuterStripMinedLoop); + init_flags(Flag_is_macro); + C->add_macro_node(this); + } + + virtual int Opcode() const; + + virtual IfTrueNode* outer_loop_tail() const; + virtual OuterStripMinedLoopEndNode* outer_loop_end() const; + virtual IfFalseNode* outer_loop_exit() const; + virtual SafePointNode* outer_safepoint() const; + void adjust_strip_mined_loop(PhaseIterGVN* igvn); +}; + +class OuterStripMinedLoopEndNode : public IfNode { +public: + OuterStripMinedLoopEndNode(Node *control, Node *test, float prob, float cnt) + : IfNode(control, test, prob, cnt) { + init_class_id(Class_OuterStripMinedLoopEnd); + } + + virtual int Opcode() const; + + virtual const Type* Value(PhaseGVN* phase) const; + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); +}; + // -----------------------------IdealLoopTree---------------------------------- class IdealLoopTree : public ResourceObj { public: @@ -780,6 +833,7 @@ private: void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); void build_loop_late_post ( Node* n ); + void verify_strip_mined_scheduling(Node *n, Node* least); // Array of immediate dominance info for each CFG node indexed by node idx private: @@ -877,7 +931,10 @@ public: // Per-Node transform virtual Node *transform( Node *a_node ) { return 0; } - bool is_counted_loop( Node *x, IdealLoopTree *loop ); + bool is_counted_loop(Node* x, IdealLoopTree*& loop); + IdealLoopTree* create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control, + IdealLoopTree* loop, float cl_prob, float le_fcnt, + Node*& entry_control, Node*& iffalse); Node* exact_limit( IdealLoopTree *loop ); @@ -908,8 +965,24 @@ public: // When nonnull, the clone and original are side-by-side, both are // dominated by the passed in side_by_side_idom node. Used in // construction of unswitched loops. + enum CloneLoopMode { + IgnoreStripMined = 0, // Only clone inner strip mined loop + CloneIncludesStripMined = 1, // clone both inner and outer strip mined loops + ControlAroundStripMined = 2 // Only clone inner strip mined loop, + // result control flow branches + // either to inner clone or outer + // strip mined loop. + }; void clone_loop( IdealLoopTree *loop, Node_List &old_new, int dom_depth, - Node* side_by_side_idom = NULL); + CloneLoopMode mode, Node* side_by_side_idom = NULL); + void clone_loop_handle_data_uses(Node* old, Node_List &old_new, + IdealLoopTree* loop, IdealLoopTree* companion_loop, + Node_List*& split_if_set, Node_List*& split_bool_set, + Node_List*& split_cex_set, Node_List& worklist, + uint new_counter, CloneLoopMode mode); + void clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop, + IdealLoopTree* outer_loop, int dd, Node_List &old_new, + Node_List& extra_data_nodes); // If we got the effect of peeling, either by actually peeling or by // making a pre-loop which must execute at least once, we can remove @@ -1020,7 +1093,8 @@ public: // and inserting an if to select fast-slow versions. ProjNode* create_slow_version_of_loop(IdealLoopTree *loop, Node_List &old_new, - int opcode); + int opcode, + CloneLoopMode mode); // Clone a loop and return the clone head (clone_loop_head). // Added nodes include int(1), int(0) - disconnected, If, IfTrue, IfFalse, diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 552eaf91bfe..d6ddc5723fe 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -26,6 +26,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "opto/addnode.hpp" +#include "opto/callnode.hpp" #include "opto/castnode.hpp" #include "opto/connode.hpp" #include "opto/castnode.hpp" @@ -306,7 +307,12 @@ Node *PhaseIdealLoop::has_local_phi_input( Node *n ) { get_ctrl(m->in(2)) != n_ctrl && get_ctrl(m->in(3)) != n_ctrl) { // Move the AddP up to dominating point - set_ctrl_and_loop(m, find_non_split_ctrl(idom(n_ctrl))); + Node* c = find_non_split_ctrl(idom(n_ctrl)); + if (c->is_OuterStripMinedLoop()) { + c->as_Loop()->verify_strip_mined(1); + c = c->in(LoopNode::EntryControl); + } + set_ctrl_and_loop(m, c); continue; } return NULL; @@ -750,14 +756,13 @@ Node* PhaseIdealLoop::try_move_store_before_loop(Node* n, Node *n_ctrl) { if (ctrl_ok) { // move the Store _igvn.replace_input_of(mem, LoopNode::LoopBackControl, mem); - _igvn.replace_input_of(n, 0, n_loop->_head->in(LoopNode::EntryControl)); + _igvn.replace_input_of(n, 0, n_loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl)); _igvn.replace_input_of(n, MemNode::Memory, mem->in(LoopNode::EntryControl)); // Disconnect the phi now. An empty phi can confuse other // optimizations in this pass of loop opts. _igvn.replace_node(mem, mem->in(LoopNode::EntryControl)); n_loop->_body.yank(mem); - IdealLoopTree* new_loop = get_loop(n->in(0)); set_ctrl_and_loop(n, n->in(0)); return n; @@ -840,6 +845,16 @@ void PhaseIdealLoop::try_move_store_after_loop(Node* n) { _igvn.replace_node(hook, n); return; } +#ifdef ASSERT + if (n_loop->_head->is_Loop() && n_loop->_head->as_Loop()->is_strip_mined()) { + assert(n_loop->_head->Opcode() == Op_CountedLoop, "outer loop is a strip mined"); + n_loop->_head->as_Loop()->verify_strip_mined(1); + Node* outer = n_loop->_head->as_CountedLoop()->outer_loop(); + IdealLoopTree* outer_loop = get_loop(outer); + assert(n_loop->_parent == outer_loop, "broken loop tree"); + assert(get_loop(lca) == outer_loop, "safepoint in outer loop consume all memory state"); + } +#endif // Move store out of the loop _igvn.replace_node(hook, n->in(MemNode::Memory)); @@ -1016,7 +1031,7 @@ Node *PhaseIdealLoop::place_near_use( Node *useblock ) const { IdealLoopTree *u_loop = get_loop( useblock ); return (u_loop->_irreducible || u_loop->_child) ? useblock - : u_loop->_head->in(LoopNode::EntryControl); + : u_loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl); } @@ -1569,6 +1584,252 @@ void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) { } } +void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, + IdealLoopTree* loop, IdealLoopTree* outer_loop, + Node_List*& split_if_set, Node_List*& split_bool_set, + Node_List*& split_cex_set, Node_List& worklist, + uint new_counter, CloneLoopMode mode) { + Node* nnn = old_new[old->_idx]; + // Copy uses to a worklist, so I can munge the def-use info + // with impunity. + for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++) + worklist.push(old->fast_out(j)); + + while( worklist.size() ) { + Node *use = worklist.pop(); + if (!has_node(use)) continue; // Ignore dead nodes + if (use->in(0) == C->top()) continue; + IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use ); + // Check for data-use outside of loop - at least one of OLD or USE + // must not be a CFG node. +#ifdef ASSERT + if (loop->_head->as_Loop()->is_strip_mined() && outer_loop->is_member(use_loop) && !loop->is_member(use_loop) && old_new[use->_idx] == NULL) { + Node* sfpt = loop->_head->as_CountedLoop()->outer_safepoint(); + assert(mode == ControlAroundStripMined && use == sfpt, "missed a node"); + } +#endif + if (!loop->is_member(use_loop) && !outer_loop->is_member(use_loop) && (!old->is_CFG() || !use->is_CFG())) { + + // If the Data use is an IF, that means we have an IF outside of the + // loop that is switching on a condition that is set inside of the + // loop. Happens if people set a loop-exit flag; then test the flag + // in the loop to break the loop, then test is again outside of the + // loop to determine which way the loop exited. + // Loop predicate If node connects to Bool node through Opaque1 node. + if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) { + // Since this code is highly unlikely, we lazily build the worklist + // of such Nodes to go split. + if (!split_if_set) { + ResourceArea *area = Thread::current()->resource_area(); + split_if_set = new Node_List(area); + } + split_if_set->push(use); + } + if (use->is_Bool()) { + if (!split_bool_set) { + ResourceArea *area = Thread::current()->resource_area(); + split_bool_set = new Node_List(area); + } + split_bool_set->push(use); + } + if (use->Opcode() == Op_CreateEx) { + if (!split_cex_set) { + ResourceArea *area = Thread::current()->resource_area(); + split_cex_set = new Node_List(area); + } + split_cex_set->push(use); + } + + + // Get "block" use is in + uint idx = 0; + while( use->in(idx) != old ) idx++; + Node *prev = use->is_CFG() ? use : get_ctrl(use); + assert(!loop->is_member(get_loop(prev)) && !outer_loop->is_member(get_loop(prev)), "" ); + Node *cfg = prev->_idx >= new_counter + ? prev->in(2) + : idom(prev); + if( use->is_Phi() ) // Phi use is in prior block + cfg = prev->in(idx); // NOT in block of Phi itself + if (cfg->is_top()) { // Use is dead? + _igvn.replace_input_of(use, idx, C->top()); + continue; + } + + while(!outer_loop->is_member(get_loop(cfg))) { + prev = cfg; + cfg = cfg->_idx >= new_counter ? cfg->in(2) : idom(cfg); + } + // If the use occurs after merging several exits from the loop, then + // old value must have dominated all those exits. Since the same old + // value was used on all those exits we did not need a Phi at this + // merge point. NOW we do need a Phi here. Each loop exit value + // is now merged with the peeled body exit; each exit gets its own + // private Phi and those Phis need to be merged here. + Node *phi; + if( prev->is_Region() ) { + if( idx == 0 ) { // Updating control edge? + phi = prev; // Just use existing control + } else { // Else need a new Phi + phi = PhiNode::make( prev, old ); + // Now recursively fix up the new uses of old! + for( uint i = 1; i < prev->req(); i++ ) { + worklist.push(phi); // Onto worklist once for each 'old' input + } + } + } else { + // Get new RegionNode merging old and new loop exits + prev = old_new[prev->_idx]; + assert( prev, "just made this in step 7" ); + if( idx == 0) { // Updating control edge? + phi = prev; // Just use existing control + } else { // Else need a new Phi + // Make a new Phi merging data values properly + phi = PhiNode::make( prev, old ); + phi->set_req( 1, nnn ); + } + } + // If inserting a new Phi, check for prior hits + if( idx != 0 ) { + Node *hit = _igvn.hash_find_insert(phi); + if( hit == NULL ) { + _igvn.register_new_node_with_optimizer(phi); // Register new phi + } else { // or + // Remove the new phi from the graph and use the hit + _igvn.remove_dead_node(phi); + phi = hit; // Use existing phi + } + set_ctrl(phi, prev); + } + // Make 'use' use the Phi instead of the old loop body exit value + _igvn.replace_input_of(use, idx, phi); + if( use->_idx >= new_counter ) { // If updating new phis + // Not needed for correctness, but prevents a weak assert + // in AddPNode from tripping (when we end up with different + // base & derived Phis that will become the same after + // IGVN does CSE). + Node *hit = _igvn.hash_find_insert(use); + if( hit ) // Go ahead and re-hash for hits. + _igvn.replace_node( use, hit ); + } + + // If 'use' was in the loop-exit block, it now needs to be sunk + // below the post-loop merge point. + sink_use( use, prev ); + } + } +} + +void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop, + IdealLoopTree* outer_loop, int dd, Node_List &old_new, + Node_List& extra_data_nodes) { + if (head->is_strip_mined() && mode != IgnoreStripMined) { + CountedLoopNode* cl = head->as_CountedLoop(); + Node* l = cl->outer_loop(); + Node* tail = cl->outer_loop_tail(); + IfNode* le = cl->outer_loop_end(); + Node* sfpt = cl->outer_safepoint(); + CountedLoopEndNode* cle = cl->loopexit(); + CountedLoopNode* new_cl = old_new[cl->_idx]->as_CountedLoop(); + CountedLoopEndNode* new_cle = new_cl->as_CountedLoop()->loopexit(); + Node* cle_out = cle->proj_out(false); + + Node* new_sfpt = NULL; + Node* new_cle_out = cle_out->clone(); + old_new.map(cle_out->_idx, new_cle_out); + if (mode == CloneIncludesStripMined) { + // clone outer loop body + Node* new_l = l->clone(); + Node* new_tail = tail->clone(); + IfNode* new_le = le->clone()->as_If(); + new_sfpt = sfpt->clone(); + + set_loop(new_l, outer_loop->_parent); + set_idom(new_l, new_l->in(LoopNode::EntryControl), dd); + set_loop(new_cle_out, outer_loop->_parent); + set_idom(new_cle_out, new_cle, dd); + set_loop(new_sfpt, outer_loop->_parent); + set_idom(new_sfpt, new_cle_out, dd); + set_loop(new_le, outer_loop->_parent); + set_idom(new_le, new_sfpt, dd); + set_loop(new_tail, outer_loop->_parent); + set_idom(new_tail, new_le, dd); + set_idom(new_cl, new_l, dd); + + old_new.map(l->_idx, new_l); + old_new.map(tail->_idx, new_tail); + old_new.map(le->_idx, new_le); + old_new.map(sfpt->_idx, new_sfpt); + + new_l->set_req(LoopNode::LoopBackControl, new_tail); + new_l->set_req(0, new_l); + new_tail->set_req(0, new_le); + new_le->set_req(0, new_sfpt); + new_sfpt->set_req(0, new_cle_out); + new_cle_out->set_req(0, new_cle); + new_cl->set_req(LoopNode::EntryControl, new_l); + + _igvn.register_new_node_with_optimizer(new_l); + _igvn.register_new_node_with_optimizer(new_tail); + _igvn.register_new_node_with_optimizer(new_le); + } else { + Node *newhead = old_new[loop->_head->_idx]; + newhead->as_Loop()->clear_strip_mined(); + _igvn.replace_input_of(newhead, LoopNode::EntryControl, newhead->in(LoopNode::EntryControl)->in(LoopNode::EntryControl)); + set_idom(newhead, newhead->in(LoopNode::EntryControl), dd); + } + // Look at data node that were assigned a control in the outer + // loop: they are kept in the outer loop by the safepoint so start + // from the safepoint node's inputs. + IdealLoopTree* outer_loop = get_loop(l); + Node_Stack stack(2); + stack.push(sfpt, 1); + uint new_counter = C->unique(); + while (stack.size() > 0) { + Node* n = stack.node(); + uint i = stack.index(); + while (i < n->req() && + (n->in(i) == NULL || + !has_ctrl(n->in(i)) || + get_loop(get_ctrl(n->in(i))) != outer_loop || + (old_new[n->in(i)->_idx] != NULL && old_new[n->in(i)->_idx]->_idx >= new_counter))) { + i++; + } + if (i < n->req()) { + stack.set_index(i+1); + stack.push(n->in(i), 0); + } else { + assert(old_new[n->_idx] == NULL || n == sfpt || old_new[n->_idx]->_idx < new_counter, "no clone yet"); + Node* m = n == sfpt ? new_sfpt : n->clone(); + if (m != NULL) { + for (uint i = 0; i < n->req(); i++) { + if (m->in(i) != NULL && old_new[m->in(i)->_idx] != NULL) { + m->set_req(i, old_new[m->in(i)->_idx]); + } + } + } else { + assert(n == sfpt && mode != CloneIncludesStripMined, "where's the safepoint clone?"); + } + if (n != sfpt) { + extra_data_nodes.push(n); + _igvn.register_new_node_with_optimizer(m); + assert(get_ctrl(n) == cle_out, "what other control?"); + set_ctrl(m, new_cle_out); + old_new.map(n->_idx, m); + } + stack.pop(); + } + } + if (mode == CloneIncludesStripMined) { + _igvn.register_new_node_with_optimizer(new_sfpt); + _igvn.register_new_node_with_optimizer(new_cle_out); + } + } else { + Node *newhead = old_new[loop->_head->_idx]; + set_idom(newhead, newhead->in(LoopNode::EntryControl), dd); + } +} + //------------------------------clone_loop------------------------------------- // // C L O N E A L O O P B O D Y @@ -1597,7 +1858,10 @@ void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) { // dominated by the side_by_side_idom node. Used in construction of // unswitched loops. void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd, - Node* side_by_side_idom) { + CloneLoopMode mode, Node* side_by_side_idom) { + + LoopNode* head = loop->_head->as_Loop(); + head->verify_strip_mined(1); if (C->do_vector_loop() && PrintOpto) { const char* mname = C->method()->name()->as_quoted_ascii(); @@ -1630,6 +1894,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd _igvn.register_new_node_with_optimizer(nnn); } + IdealLoopTree* outer_loop = (head->is_strip_mined() && mode != IgnoreStripMined) ? get_loop(head->as_CountedLoop()->outer_loop()) : loop; // Step 2: Fix the edges in the new body. If the old input is outside the // loop use it. If the old input is INside the loop, use the corresponding @@ -1641,7 +1906,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd if (has_ctrl(old)) { set_ctrl(nnn, old_new[get_ctrl(old)->_idx]); } else { - set_loop(nnn, loop->_parent); + set_loop(nnn, outer_loop->_parent); if (old->outcnt() > 0) { set_idom( nnn, old_new[idom(old)->_idx], dd ); } @@ -1657,22 +1922,21 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd } _igvn.hash_find_insert(nnn); } - Node *newhead = old_new[loop->_head->_idx]; - set_idom(newhead, newhead->in(LoopNode::EntryControl), dd); + ResourceArea *area = Thread::current()->resource_area(); + Node_List extra_data_nodes(area); + clone_outer_loop(head, mode, loop, outer_loop, dd, old_new, extra_data_nodes); // Step 3: Now fix control uses. Loop varying control uses have already // been fixed up (as part of all input edges in Step 2). Loop invariant // control uses must be either an IfFalse or an IfTrue. Make a merge // point to merge the old and new IfFalse/IfTrue nodes; make the use // refer to this. - ResourceArea *area = Thread::current()->resource_area(); Node_List worklist(area); uint new_counter = C->unique(); for( i = 0; i < loop->_body.size(); i++ ) { Node* old = loop->_body.at(i); if( !old->is_CFG() ) continue; - Node* nnn = old_new[old->_idx]; // Copy uses to a worklist, so I can munge the def-use info // with impunity. @@ -1686,9 +1950,29 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd if( !loop->is_member( use_loop ) && use->is_CFG() ) { // Both OLD and USE are CFG nodes here. assert( use->is_Proj(), "" ); + Node* nnn = old_new[old->_idx]; + + Node* newuse = NULL; + if (head->is_strip_mined() && mode != IgnoreStripMined) { + CountedLoopNode* cl = head->as_CountedLoop(); + CountedLoopEndNode* cle = cl->loopexit(); + Node* cle_out = cle->proj_out(false); + if (use == cle_out) { + IfNode* le = cl->outer_loop_end(); + use = le->proj_out(false); + use_loop = get_loop(use); + if (mode == CloneIncludesStripMined) { + nnn = old_new[le->_idx]; + } else { + newuse = old_new[cle_out->_idx]; + } + } + } + if (newuse == NULL) { + newuse = use->clone(); + } // Clone the loop exit control projection - Node *newuse = use->clone(); if (C->do_vector_loop()) { cm.verify_insert_and_clone(use, newuse, cm.clone_idx()); } @@ -1722,6 +2006,10 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd if( useuse->in(k) == use ) { useuse->set_req(k, r); uses_found++; + if (useuse->is_Loop() && k == LoopNode::EntryControl) { + assert(dom_depth(useuse) > dd_r , ""); + set_idom(useuse, r, dom_depth(useuse)); + } } } l -= uses_found; // we deleted 1 or more copies of this edge @@ -1745,126 +2033,16 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd Node_List *split_cex_set = NULL; for( i = 0; i < loop->_body.size(); i++ ) { Node* old = loop->_body.at(i); - Node* nnn = old_new[old->_idx]; - // Copy uses to a worklist, so I can munge the def-use info - // with impunity. - for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++) - worklist.push(old->fast_out(j)); + clone_loop_handle_data_uses(old, old_new, loop, outer_loop, split_if_set, + split_bool_set, split_cex_set, worklist, new_counter, + mode); + } - while( worklist.size() ) { - Node *use = worklist.pop(); - if (!has_node(use)) continue; // Ignore dead nodes - if (use->in(0) == C->top()) continue; - IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use ); - // Check for data-use outside of loop - at least one of OLD or USE - // must not be a CFG node. - if( !loop->is_member( use_loop ) && (!old->is_CFG() || !use->is_CFG())) { - - // If the Data use is an IF, that means we have an IF outside of the - // loop that is switching on a condition that is set inside of the - // loop. Happens if people set a loop-exit flag; then test the flag - // in the loop to break the loop, then test is again outside of the - // loop to determine which way the loop exited. - // Loop predicate If node connects to Bool node through Opaque1 node. - if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) { - // Since this code is highly unlikely, we lazily build the worklist - // of such Nodes to go split. - if (!split_if_set) { - split_if_set = new Node_List(area); - } - split_if_set->push(use); - } - if (use->is_Bool()) { - if (!split_bool_set) { - split_bool_set = new Node_List(area); - } - split_bool_set->push(use); - } - if (use->Opcode() == Op_CreateEx) { - if (!split_cex_set) { - split_cex_set = new Node_List(area); - } - split_cex_set->push(use); - } - - - // Get "block" use is in - uint idx = 0; - while( use->in(idx) != old ) idx++; - Node *prev = use->is_CFG() ? use : get_ctrl(use); - assert( !loop->is_member( get_loop( prev ) ), "" ); - Node *cfg = prev->_idx >= new_counter - ? prev->in(2) - : idom(prev); - if( use->is_Phi() ) // Phi use is in prior block - cfg = prev->in(idx); // NOT in block of Phi itself - if (cfg->is_top()) { // Use is dead? - _igvn.replace_input_of(use, idx, C->top()); - continue; - } - - while( !loop->is_member( get_loop( cfg ) ) ) { - prev = cfg; - cfg = cfg->_idx >= new_counter ? cfg->in(2) : idom(cfg); - } - // If the use occurs after merging several exits from the loop, then - // old value must have dominated all those exits. Since the same old - // value was used on all those exits we did not need a Phi at this - // merge point. NOW we do need a Phi here. Each loop exit value - // is now merged with the peeled body exit; each exit gets its own - // private Phi and those Phis need to be merged here. - Node *phi; - if( prev->is_Region() ) { - if( idx == 0 ) { // Updating control edge? - phi = prev; // Just use existing control - } else { // Else need a new Phi - phi = PhiNode::make( prev, old ); - // Now recursively fix up the new uses of old! - for( uint i = 1; i < prev->req(); i++ ) { - worklist.push(phi); // Onto worklist once for each 'old' input - } - } - } else { - // Get new RegionNode merging old and new loop exits - prev = old_new[prev->_idx]; - assert( prev, "just made this in step 7" ); - if( idx == 0 ) { // Updating control edge? - phi = prev; // Just use existing control - } else { // Else need a new Phi - // Make a new Phi merging data values properly - phi = PhiNode::make( prev, old ); - phi->set_req( 1, nnn ); - } - } - // If inserting a new Phi, check for prior hits - if( idx != 0 ) { - Node *hit = _igvn.hash_find_insert(phi); - if( hit == NULL ) { - _igvn.register_new_node_with_optimizer(phi); // Register new phi - } else { // or - // Remove the new phi from the graph and use the hit - _igvn.remove_dead_node(phi); - phi = hit; // Use existing phi - } - set_ctrl(phi, prev); - } - // Make 'use' use the Phi instead of the old loop body exit value - _igvn.replace_input_of(use, idx, phi); - if( use->_idx >= new_counter ) { // If updating new phis - // Not needed for correctness, but prevents a weak assert - // in AddPNode from tripping (when we end up with different - // base & derived Phis that will become the same after - // IGVN does CSE). - Node *hit = _igvn.hash_find_insert(use); - if( hit ) // Go ahead and re-hash for hits. - _igvn.replace_node( use, hit ); - } - - // If 'use' was in the loop-exit block, it now needs to be sunk - // below the post-loop merge point. - sink_use( use, prev ); - } - } + for (i = 0; i < extra_data_nodes.size(); i++) { + Node* old = extra_data_nodes.at(i); + clone_loop_handle_data_uses(old, old_new, loop, outer_loop, split_if_set, + split_bool_set, split_cex_set, worklist, new_counter, + mode); } // Check for IFs that need splitting/cloning. Happens if an IF outside of @@ -2956,7 +3134,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { assert(is_valid_loop_partition(loop, peel, peel_list, not_peel), "bad partition"); - clone_loop( loop, old_new, dd ); + clone_loop(loop, old_new, dd, IgnoreStripMined); const uint clone_exit_idx = 1; const uint orig_exit_idx = 2; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index a6ec2ff4267..8e8737f65c6 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -282,7 +282,8 @@ void PhaseMacroExpand::eliminate_card_mark(Node* p2x) { if (!this_region->in(ind)->is_IfFalse()) { ind = 2; } - if (this_region->in(ind)->is_IfFalse()) { + if (this_region->in(ind)->is_IfFalse() && + this_region->in(ind)->in(0)->Opcode() == Op_If) { Node* bol = this_region->in(ind)->in(0)->in(1); assert(bol->is_Bool(), ""); cmpx = bol->in(1); @@ -2660,6 +2661,8 @@ void PhaseMacroExpand::eliminate_macro_nodes() { break; case Node::Class_ArrayCopy: break; + case Node::Class_OuterStripMinedLoop: + break; default: assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque1 || @@ -2733,6 +2736,10 @@ bool PhaseMacroExpand::expand_macro_nodes() { } else if (n->Opcode() == Op_Opaque4) { _igvn.replace_node(n, n->in(2)); success = true; + } else if (n->Opcode() == Op_OuterStripMinedLoop) { + n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); + C->remove_macro_node(n); + success = true; } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 658b54fd358..17942bac08b 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -111,6 +111,8 @@ class MulNode; class MultiNode; class MultiBranchNode; class NeverBranchNode; +class OuterStripMinedLoopNode; +class OuterStripMinedLoopEndNode; class Node; class Node_Array; class Node_List; @@ -623,8 +625,9 @@ public: DEFINE_CLASS_ID(Catch, PCTable, 0) DEFINE_CLASS_ID(Jump, PCTable, 1) DEFINE_CLASS_ID(If, MultiBranch, 1) - DEFINE_CLASS_ID(CountedLoopEnd, If, 0) - DEFINE_CLASS_ID(RangeCheck, If, 1) + DEFINE_CLASS_ID(CountedLoopEnd, If, 0) + DEFINE_CLASS_ID(RangeCheck, If, 1) + DEFINE_CLASS_ID(OuterStripMinedLoopEnd, If, 2) DEFINE_CLASS_ID(NeverBranch, MultiBranch, 2) DEFINE_CLASS_ID(Start, Multi, 2) DEFINE_CLASS_ID(MemBar, Multi, 3) @@ -684,8 +687,9 @@ public: DEFINE_CLASS_ID(Region, Node, 5) DEFINE_CLASS_ID(Loop, Region, 0) - DEFINE_CLASS_ID(Root, Loop, 0) - DEFINE_CLASS_ID(CountedLoop, Loop, 1) + DEFINE_CLASS_ID(Root, Loop, 0) + DEFINE_CLASS_ID(CountedLoop, Loop, 1) + DEFINE_CLASS_ID(OuterStripMinedLoop, Loop, 2) DEFINE_CLASS_ID(Sub, Node, 6) DEFINE_CLASS_ID(Cmp, Sub, 0) @@ -841,6 +845,8 @@ public: DEFINE_CLASS_QUERY(Mul) DEFINE_CLASS_QUERY(Multi) DEFINE_CLASS_QUERY(MultiBranch) + DEFINE_CLASS_QUERY(OuterStripMinedLoop) + DEFINE_CLASS_QUERY(OuterStripMinedLoopEnd) DEFINE_CLASS_QUERY(Parm) DEFINE_CLASS_QUERY(PCTable) DEFINE_CLASS_QUERY(Phi) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 86145b7647a..39a525a1955 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1353,6 +1353,7 @@ bool SuperWord::follow_def_uses(Node_List* p) { for (DUIterator_Fast jmax, j = s2->fast_outs(jmax); j < jmax; j++) { Node* t2 = s2->fast_out(j); if (!in_bb(t2)) continue; + if (t2->Opcode() == Op_AddI && t2 == _lp->as_CountedLoop()->incr()) continue; // don't mess with the iv if (!opnd_positions_match(s1, t1, s2, t2)) continue; if (stmts_can_pack(t1, t2, align)) { @@ -3313,7 +3314,7 @@ CountedLoopEndNode* SuperWord::get_pre_loop_end(CountedLoopNode* cl) { return NULL; } - Node* p_f = cl->in(LoopNode::EntryControl)->in(0)->in(0); + Node* p_f = cl->skip_strip_mined()->in(LoopNode::EntryControl)->in(0)->in(0); if (!p_f->is_IfFalse()) return NULL; if (!p_f->in(0)->is_CountedLoopEnd()) return NULL; CountedLoopEndNode* pre_end = p_f->in(0)->as_CountedLoopEnd(); diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 820f656e9ae..57615a76f16 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2207,6 +2207,21 @@ bool Arguments::check_vm_args_consistency() { } FLAG_SET_CMDLINE(bool, PostLoopMultiversioning, false); } + if (UseCountedLoopSafepoints && LoopStripMiningIter == 0) { + if (!FLAG_IS_DEFAULT(UseCountedLoopSafepoints) || !FLAG_IS_DEFAULT(LoopStripMiningIter)) { + warning("When counted loop safepoints are enabled, LoopStripMiningIter must be at least 1 (a safepoint every 1 iteration): setting it to 1"); + } + LoopStripMiningIter = 1; + } else if (!UseCountedLoopSafepoints && LoopStripMiningIter > 0) { + if (!FLAG_IS_DEFAULT(UseCountedLoopSafepoints) || !FLAG_IS_DEFAULT(LoopStripMiningIter)) { + warning("Disabling counted safepoints implies no loop strip mining: setting LoopStripMiningIter to 0"); + } + LoopStripMiningIter = 0; + } + if (FLAG_IS_DEFAULT(LoopStripMiningIterShortLoop)) { + // blind guess + LoopStripMiningIterShortLoop = LoopStripMiningIter / 10; + } #endif return status; } diff --git a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java index f23673ad651..e581f4ea3c8 100644 --- a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java +++ b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java @@ -61,7 +61,8 @@ public class UseCountedLoopSafepointsTest { OutputAnalyzer oa; try { oa = ProcessTools.executeTestJvm("-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", - "-XX:" + (enabled ? "+" : "-") + "UseCountedLoopSafepoints", "-XX:+WhiteBoxAPI", + "-XX:" + (enabled ? "+" : "-") + "UseCountedLoopSafepoints", + "-XX:LoopStripMiningIter=" + (enabled ? "1" : "0"), "-XX:+WhiteBoxAPI", "-XX:-Inline", "-Xbatch", "-XX:+PrintIdeal", "-XX:LoopUnrollLimit=0", "-XX:CompileOnly=" + UseCountedLoopSafepoints.class.getName() + "::testMethod", UseCountedLoopSafepoints.class.getName()); From 3fc999a1fe8d6ed1059319d6556c4fbd6a9261e9 Mon Sep 17 00:00:00 2001 From: Kishor Kharbas <kkharbas@openjdk.org> Date: Wed, 29 Nov 2017 17:03:10 -0800 Subject: [PATCH 076/165] 8190308: Implementation: JEP 316: Heap Allocation on Alternative Memory Devices Sub-task to be used for implementation of JEP 316: Support heap allocation on alternative memory devices Reviewed-by: sangheki, tschatzl --- src/hotspot/os/aix/os_aix.cpp | 16 ++ src/hotspot/os/bsd/os_bsd.cpp | 11 ++ src/hotspot/os/linux/os_linux.cpp | 31 +++- src/hotspot/os/posix/os_posix.cpp | 163 ++++++++++++++++-- src/hotspot/os/solaris/os_solaris.cpp | 11 ++ src/hotspot/os/windows/os_windows.cpp | 86 ++++++++- src/hotspot/share/memory/universe.cpp | 5 +- src/hotspot/share/memory/virtualspace.cpp | 94 ++++++++-- src/hotspot/share/memory/virtualspace.hpp | 5 +- src/hotspot/share/runtime/arguments.cpp | 9 +- src/hotspot/share/runtime/globals.hpp | 6 +- src/hotspot/share/runtime/os.cpp | 35 +++- src/hotspot/share/runtime/os.hpp | 17 +- test/hotspot/jtreg/gc/TestAllocateHeapAt.java | 68 ++++++++ 14 files changed, 503 insertions(+), 54 deletions(-) create mode 100644 test/hotspot/jtreg/gc/TestAllocateHeapAt.java diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index df5959b26d0..0325824e021 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2490,6 +2490,22 @@ bool os::can_execute_large_page_memory() { return false; } +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) { + assert(file_desc >= 0, "file_desc is not valid"); + char* result = NULL; + + // Always round to os::vm_page_size(), which may be larger than 4K. + bytes = align_up(bytes, os::vm_page_size()); + result = reserve_mmaped_memory(bytes, requested_addr, 0); + + if (result != NULL) { + if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + } + return result; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) { diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 6ebcb627fdc..64afc14318c 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2350,6 +2350,17 @@ bool os::can_execute_large_page_memory() { return UseHugeTLBFS; } +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) { + assert(file_desc >= 0, "file_desc is not valid"); + char* result = pd_attempt_reserve_memory_at(bytes, requested_addr); + if (result != NULL) { + if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + } + return result; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 27fe2dab85a..a91fac1b9fe 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -130,6 +130,7 @@ #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) #define LARGEPAGES_BIT (1 << 6) +#define DAX_SHARED_BIT (1 << 8) //////////////////////////////////////////////////////////////////////////////// // global variables julong os::Linux::_physical_memory = 0; @@ -3370,10 +3371,13 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) { // effective only if the bit 2 is cleared) // - (bit 5) hugetlb private memory // - (bit 6) hugetlb shared memory +// - (bit 7) dax private memory +// - (bit 8) dax shared memory // -static void set_coredump_filter(void) { +static void set_coredump_filter(bool largepages, bool dax_shared) { FILE *f; long cdm; + bool filter_changed = false; if ((f = fopen("/proc/self/coredump_filter", "r+")) == NULL) { return; @@ -3386,8 +3390,15 @@ static void set_coredump_filter(void) { rewind(f); - if ((cdm & LARGEPAGES_BIT) == 0) { + if (largepages && (cdm & LARGEPAGES_BIT) == 0) { cdm |= LARGEPAGES_BIT; + filter_changed = true; + } + if (dax_shared && (cdm & DAX_SHARED_BIT) == 0) { + cdm |= DAX_SHARED_BIT; + filter_changed = true; + } + if (filter_changed) { fprintf(f, "%#lx", cdm); } @@ -3526,7 +3537,7 @@ void os::large_page_init() { size_t large_page_size = Linux::setup_large_page_size(); UseLargePages = Linux::setup_large_page_type(large_page_size); - set_coredump_filter(); + set_coredump_filter(true /*largepages*/, false /*dax_shared*/); } #ifndef SHM_HUGETLB @@ -3897,6 +3908,17 @@ bool os::can_execute_large_page_memory() { return UseTransparentHugePages || UseHugeTLBFS; } +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) { + assert(file_desc >= 0, "file_desc is not valid"); + char* result = pd_attempt_reserve_memory_at(bytes, requested_addr); + if (result != NULL) { + if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + } + return result; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). @@ -5008,6 +5030,9 @@ jint os::init_2(void) { // initialize thread priority policy prio_init(); + if (!FLAG_IS_DEFAULT(AllocateHeapAt)) { + set_coredump_filter(false /*largepages*/, true /*dax_shared*/); + } return JNI_OK; } diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 5d393bf1e18..089ae4bc602 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -40,6 +40,7 @@ #include <pthread.h> #include <semaphore.h> #include <signal.h> +#include <sys/mman.h> #include <sys/resource.h> #include <sys/utsname.h> #include <time.h> @@ -52,6 +53,20 @@ #endif #define IS_VALID_PID(p) (p > 0 && p < MAX_PID) +#ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON +#endif + +#define check_with_errno(check_type, cond, msg) \ + do { \ + int err = errno; \ + check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err), \ + os::errno_name(err)); \ +} while (false) + +#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg) +#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg) + // Check core dump limit and report possible place where core can be found void os::check_dump_limit(char* buffer, size_t bufferSize) { if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) { @@ -145,10 +160,124 @@ void os::wait_for_keypress_at_exit(void) { return; } +int os::create_file_for_heap(const char* dir) { + + const char name_template[] = "/jvmheap.XXXXXX"; + + char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal); + if (fullname == NULL) { + vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno))); + return -1; + } + (void)strncpy(fullname, dir, strlen(dir)+1); + (void)strncat(fullname, name_template, strlen(name_template)); + + os::native_path(fullname); + + sigset_t set, oldset; + int ret = sigfillset(&set); + assert_with_errno(ret == 0, "sigfillset returned error"); + + // set the file creation mask. + mode_t file_mode = S_IRUSR | S_IWUSR; + + // create a new file. + int fd = mkstemp(fullname); + + if (fd < 0) { + warning("Could not create file for heap with template %s", fullname); + os::free(fullname); + return -1; + } + + // delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted. + ret = unlink(fullname); + assert_with_errno(ret == 0, "unlink returned error"); + + os::free(fullname); + return fd; +} + +static char* reserve_mmapped_memory(size_t bytes, char* requested_addr) { + char * addr; + int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS; + if (requested_addr != NULL) { + assert((uintptr_t)requested_addr % os::vm_page_size() == 0, "Requested address should be aligned to OS page size"); + flags |= MAP_FIXED; + } + + // Map reserved/uncommitted pages PROT_NONE so we fail early if we + // touch an uncommitted page. Otherwise, the read/write might + // succeed if we have enough swap space to back the physical page. + addr = (char*)::mmap(requested_addr, bytes, PROT_NONE, + flags, -1, 0); + + if (addr != MAP_FAILED) { + MemTracker::record_virtual_memory_reserve((address)addr, bytes, CALLER_PC); + return addr; + } + return NULL; +} + +static int util_posix_fallocate(int fd, off_t offset, off_t len) { +#ifdef __APPLE__ + fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, len }; + // First we try to get a continuous chunk of disk space + int ret = fcntl(fd, F_PREALLOCATE, &store); + if (ret == -1) { + // Maybe we are too fragmented, try to allocate non-continuous range + store.fst_flags = F_ALLOCATEALL; + ret = fcntl(fd, F_PREALLOCATE, &store); + } + if(ret != -1) { + return ftruncate(fd, len); + } + return -1; +#else + return posix_fallocate(fd, offset, len); +#endif +} + +// Map the given address range to the provided file descriptor. +char* os::map_memory_to_file(char* base, size_t size, int fd) { + assert(fd != -1, "File descriptor is not valid"); + + // allocate space for the file + if (util_posix_fallocate(fd, 0, (off_t)size) != 0) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory.")); + return NULL; + } + + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_SHARED; + if (base != NULL) { + flags |= MAP_FIXED; + } + char* addr = (char*)mmap(base, size, prot, flags, fd, 0); + + if (addr == MAP_FAILED) { + return NULL; + } + if (base != NULL && addr != base) { + if (!os::release_memory(addr, size)) { + warning("Could not release memory on unsuccessful file mapping"); + } + return NULL; + } + return addr; +} + +char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) { + assert(fd != -1, "File descriptor is not valid"); + assert(base != NULL, "Base cannot be NULL"); + + return map_memory_to_file(base, size, fd); +} + // Multiple threads can race in this code, and can remap over each other with MAP_FIXED, // so on posix, unmap the section at the start and at the end of the chunk that we mapped // rather than unmapping and remapping the whole chunk to get requested alignment. -char* os::reserve_memory_aligned(size_t size, size_t alignment) { +char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)"); assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); @@ -156,7 +285,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) { size_t extra_size = size + alignment; assert(extra_size >= size, "overflow, size is too large to allow alignment"); - char* extra_base = os::reserve_memory(extra_size, NULL, alignment); + char* extra_base; + if (file_desc != -1) { + // For file mapping, we do not call os:reserve_memory(extra_size, NULL, alignment, file_desc) because + // we need to deal with shrinking of the file space later when we release extra memory after alignment. + // We also cannot called os:reserve_memory() with file_desc set to -1 because on aix we might get SHM memory. + // So here to call a helper function while reserve memory for us. After we have a aligned base, + // we will replace anonymous mapping with file mapping. + extra_base = reserve_mmapped_memory(extra_size, NULL); + if (extra_base != NULL) { + MemTracker::record_virtual_memory_reserve((address)extra_base, extra_size, CALLER_PC); + } + } else { + extra_base = os::reserve_memory(extra_size, NULL, alignment); + } if (extra_base == NULL) { return NULL; @@ -183,6 +325,13 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) { os::release_memory(extra_base + begin_offset + size, end_offset); } + if (file_desc != -1) { + // After we have an aligned address, we can replace anonymous mapping with file mapping + if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC); + } return aligned_base; } @@ -1348,16 +1497,6 @@ void os::ThreadCrashProtection::check_crash_protection(int sig, } } -#define check_with_errno(check_type, cond, msg) \ - do { \ - int err = errno; \ - check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err), \ - os::errno_name(err)); \ -} while (false) - -#define assert_with_errno(cond, msg) check_with_errno(assert, cond, msg) -#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg) - // POSIX unamed semaphores are not supported on OS X. #ifndef __APPLE__ diff --git a/src/hotspot/os/solaris/os_solaris.cpp b/src/hotspot/os/solaris/os_solaris.cpp index f136cec8275..ef7c1deaea4 100644 --- a/src/hotspot/os/solaris/os_solaris.cpp +++ b/src/hotspot/os/solaris/os_solaris.cpp @@ -2585,6 +2585,17 @@ char* os::pd_reserve_memory(size_t bytes, char* requested_addr, return addr; } +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) { + assert(file_desc >= 0, "file_desc is not valid"); + char* result = pd_attempt_reserve_memory_at(bytes, requested_addr); + if (result != NULL) { + if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + } + return result; +} + // Reserve memory at an arbitrary address, only if that area is // available (and not reserved for something else). diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 2742c53614d..656a119d4d4 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2904,6 +2904,75 @@ void os::large_page_init() { UseLargePages = success; } +int os::create_file_for_heap(const char* dir) { + + const char name_template[] = "/jvmheap.XXXXXX"; + char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal); + if (fullname == NULL) { + vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno))); + return -1; + } + + (void)strncpy(fullname, dir, strlen(dir)+1); + (void)strncat(fullname, name_template, strlen(name_template)); + + os::native_path(fullname); + + char *path = _mktemp(fullname); + if (path == NULL) { + warning("_mktemp could not create file name from template %s (%s)", fullname, os::strerror(errno)); + os::free(fullname); + return -1; + } + + int fd = _open(path, O_RDWR | O_CREAT | O_TEMPORARY | O_EXCL, S_IWRITE | S_IREAD); + + os::free(fullname); + if (fd < 0) { + warning("Problem opening file for heap (%s)", os::strerror(errno)); + return -1; + } + return fd; +} + +// If 'base' is not NULL, function will return NULL if it cannot get 'base' +char* os::map_memory_to_file(char* base, size_t size, int fd) { + assert(fd != -1, "File descriptor is not valid"); + + HANDLE fh = (HANDLE)_get_osfhandle(fd); +#ifdef _LP64 + HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE, + (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL); +#else + HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE, + 0, (DWORD)size, NULL); +#endif + if (fileMapping == NULL) { + if (GetLastError() == ERROR_DISK_FULL) { + vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap")); + } + else { + vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory")); + } + + return NULL; + } + + LPVOID addr = MapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base); + + CloseHandle(fileMapping); + + return (char*)addr; +} + +char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) { + assert(fd != -1, "File descriptor is not valid"); + assert(base != NULL, "Base address cannot be NULL"); + + release_memory(base, size); + return map_memory_to_file(base, size, fd); +} + // On win32, one cannot release just a part of reserved memory, it's an // all or nothing deal. When we split a reservation, we must break the // reservation into two reservations. @@ -2923,7 +2992,7 @@ void os::pd_split_reserved_memory(char *base, size_t size, size_t split, // Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. -char* os::reserve_memory_aligned(size_t size, size_t alignment) { +char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) { assert((alignment & (os::vm_allocation_granularity() - 1)) == 0, "Alignment must be a multiple of allocation granularity (page size)"); assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned"); @@ -2934,16 +3003,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) { char* aligned_base = NULL; do { - char* extra_base = os::reserve_memory(extra_size, NULL, alignment); + char* extra_base = os::reserve_memory(extra_size, NULL, alignment, file_desc); if (extra_base == NULL) { return NULL; } // Do manual alignment aligned_base = align_up(extra_base, alignment); - os::release_memory(extra_base, extra_size); + if (file_desc != -1) { + os::unmap_memory(extra_base, extra_size); + } else { + os::release_memory(extra_base, extra_size); + } - aligned_base = os::reserve_memory(size, aligned_base); + aligned_base = os::reserve_memory(size, aligned_base, 0, file_desc); } while (aligned_base == NULL); @@ -2989,6 +3062,11 @@ char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) { return reserve_memory(bytes, requested_addr); } +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) { + assert(file_desc >= 0, "file_desc is not valid"); + return map_memory_to_file(requested_addr, bytes, file_desc); +} + size_t os::large_page_size() { return _large_page_size; } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 538ca6e294e..6ddf3e42e79 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -856,7 +856,7 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { || use_large_pages, "Wrong alignment to use large pages"); // Now create the space. - ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages); + ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, AllocateHeapAt); if (total_rs.is_reserved()) { assert((total_reserved == total_rs.size()) && ((uintptr_t)total_rs.base() % alignment == 0), @@ -870,6 +870,9 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { Universe::set_narrow_oop_base((address)total_rs.compressed_oop_base()); } + if (AllocateHeapAt != NULL) { + log_info(gc,heap)("Successfully allocated Java heap at location %s", AllocateHeapAt); + } return total_rs; } diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index a3289d9b043..759210aace1 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -35,10 +35,10 @@ // Dummy constructor ReservedSpace::ReservedSpace() : _base(NULL), _size(0), _noaccess_prefix(0), - _alignment(0), _special(false), _executable(false) { + _alignment(0), _special(false), _executable(false), _fd_for_heap(-1) { } -ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) { +ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) : _fd_for_heap(-1) { bool has_preferred_page_size = preferred_page_size != 0; // Want to use large pages where possible and pad with small pages. size_t page_size = has_preferred_page_size ? preferred_page_size : os::page_size_for_region_unaligned(size, 1); @@ -59,19 +59,30 @@ ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) { ReservedSpace::ReservedSpace(size_t size, size_t alignment, bool large, - char* requested_address) { + char* requested_address) : _fd_for_heap(-1) { initialize(size, alignment, large, requested_address, false); } ReservedSpace::ReservedSpace(size_t size, size_t alignment, bool large, - bool executable) { + bool executable) : _fd_for_heap(-1) { initialize(size, alignment, large, NULL, executable); } +// Helper method +static void unmap_or_release_memory(char* base, size_t size, bool is_file_mapped) { + if (is_file_mapped) { + if (!os::unmap_memory(base, size)) { + fatal("os::unmap_memory failed"); + } + } else if (!os::release_memory(base, size)) { + fatal("os::release_memory failed"); + } +} + // Helper method. static bool failed_to_reserve_as_requested(char* base, char* requested_address, - const size_t size, bool special) + const size_t size, bool special, bool is_file_mapped = false) { if (base == requested_address || requested_address == NULL) return false; // did not fail @@ -87,9 +98,7 @@ static bool failed_to_reserve_as_requested(char* base, char* requested_address, fatal("os::release_memory_special failed"); } } else { - if (!os::release_memory(base, size)) { - fatal("os::release_memory failed"); - } + unmap_or_release_memory(base, size, is_file_mapped); } } return true; @@ -120,7 +129,18 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // If OS doesn't support demand paging for large page memory, we need // to use reserve_memory_special() to reserve and pin the entire region. + // If there is a backing file directory for this space then whether + // large pages are allocated is up to the filesystem of the backing file. + // So we ignore the UseLargePages flag in this case. bool special = large && !os::can_commit_large_page_memory(); + if (special && _fd_for_heap != -1) { + special = false; + if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes))) { + log_debug(gc, heap)("Ignoring UseLargePages since large page support is up to the file system of the backing file for Java heap"); + } + } + char* base = NULL; if (special) { @@ -157,13 +177,13 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // important. If available space is not detected, return NULL. if (requested_address != 0) { - base = os::attempt_reserve_memory_at(size, requested_address); - if (failed_to_reserve_as_requested(base, requested_address, size, false)) { + base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap); + if (failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) { // OS ignored requested address. Try different address. base = NULL; } } else { - base = os::reserve_memory(size, NULL, alignment); + base = os::reserve_memory(size, NULL, alignment, _fd_for_heap); } if (base == NULL) return; @@ -171,13 +191,14 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, // Check alignment constraints if ((((size_t)base) & (alignment - 1)) != 0) { // Base not aligned, retry - if (!os::release_memory(base, size)) fatal("os::release_memory failed"); + unmap_or_release_memory(base, size, _fd_for_heap != -1 /*is_file_mapped*/); + // Make sure that size is aligned size = align_up(size, alignment); - base = os::reserve_memory_aligned(size, alignment); + base = os::reserve_memory_aligned(size, alignment, _fd_for_heap); if (requested_address != 0 && - failed_to_reserve_as_requested(base, requested_address, size, false)) { + failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) { // As a result of the alignment constraints, the allocated base differs // from the requested address. Return back to the caller who can // take remedial action (like try again without a requested address). @@ -190,6 +211,10 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large, _base = base; _size = size; _alignment = alignment; + // If heap is reserved with a backing file, the entire space has been committed. So set the _special flag to true + if (_fd_for_heap != -1) { + _special = true; + } } @@ -252,7 +277,11 @@ void ReservedSpace::release() { char *real_base = _base - _noaccess_prefix; const size_t real_size = _size + _noaccess_prefix; if (special()) { - os::release_memory_special(real_base, real_size); + if (_fd_for_heap != -1) { + os::unmap_memory(real_base, real_size); + } else { + os::release_memory_special(real_base, real_size); + } } else{ os::release_memory(real_base, real_size); } @@ -313,7 +342,17 @@ void ReservedHeapSpace::try_reserve_heap(size_t size, // If OS doesn't support demand paging for large page memory, we need // to use reserve_memory_special() to reserve and pin the entire region. + // If there is a backing file directory for this space then whether + // large pages are allocated is up to the filesystem of the backing file. + // So we ignore the UseLargePages flag in this case. bool special = large && !os::can_commit_large_page_memory(); + if (special && _fd_for_heap != -1) { + special = false; + if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes))) { + log_debug(gc, heap)("Cannot allocate large pages for Java Heap when AllocateHeapAt option is set."); + } + } char* base = NULL; log_trace(gc, heap, coops)("Trying to allocate at address " PTR_FORMAT @@ -350,9 +389,9 @@ void ReservedHeapSpace::try_reserve_heap(size_t size, // important. If available space is not detected, return NULL. if (requested_address != 0) { - base = os::attempt_reserve_memory_at(size, requested_address); + base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap); } else { - base = os::reserve_memory(size, NULL, alignment); + base = os::reserve_memory(size, NULL, alignment, _fd_for_heap); } } if (base == NULL) { return; } @@ -362,6 +401,11 @@ void ReservedHeapSpace::try_reserve_heap(size_t size, _size = size; _alignment = alignment; + // If heap is reserved with a backing file, the entire space has been committed. So set the _special flag to true + if (_fd_for_heap != -1) { + _special = true; + } + // Check alignment constraints if ((((size_t)base) & (alignment - 1)) != 0) { // Base not aligned, retry. @@ -556,12 +600,20 @@ void ReservedHeapSpace::initialize_compressed_heap(const size_t size, size_t ali } } -ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) : ReservedSpace() { +ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large, const char* heap_allocation_directory) : ReservedSpace() { if (size == 0) { return; } + if (heap_allocation_directory != NULL) { + _fd_for_heap = os::create_file_for_heap(heap_allocation_directory); + if (_fd_for_heap == -1) { + vm_exit_during_initialization( + err_msg("Could not create file for Heap at location %s", heap_allocation_directory)); + } + } + // Heap size should be aligned to alignment, too. guarantee(is_aligned(size, alignment), "set by caller"); @@ -585,6 +637,10 @@ ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) if (base() != NULL) { MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap); } + + if (_fd_for_heap != -1) { + os::close(_fd_for_heap); + } } // Reserve space for code segment. Same as Java heap only we mark this as diff --git a/src/hotspot/share/memory/virtualspace.hpp b/src/hotspot/share/memory/virtualspace.hpp index 2475f09d156..5041ce145ec 100644 --- a/src/hotspot/share/memory/virtualspace.hpp +++ b/src/hotspot/share/memory/virtualspace.hpp @@ -37,6 +37,7 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC { size_t _noaccess_prefix; size_t _alignment; bool _special; + int _fd_for_heap; private: bool _executable; @@ -115,7 +116,9 @@ class ReservedHeapSpace : public ReservedSpace { void establish_noaccess_prefix(); public: // Constructor. Tries to find a heap that is good for compressed oops. - ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large); + // heap_allocation_directory is the path to the backing memory for Java heap. When set, Java heap will be allocated + // on the device which is managed by the file system where the directory resides. + ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large, const char* heap_allocation_directory = NULL); // Returns the base to be used for compression, i.e. so that null can be // encoded safely and implicit null checks can work. char *compressed_oop_base() { return _base - _noaccess_prefix; } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 57615a76f16..c8d8ac912a2 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2223,6 +2223,11 @@ bool Arguments::check_vm_args_consistency() { LoopStripMiningIterShortLoop = LoopStripMiningIter / 10; } #endif + if (!FLAG_IS_DEFAULT(AllocateHeapAt)) { + if ((UseNUMAInterleaving && !FLAG_IS_DEFAULT(UseNUMAInterleaving)) || (UseNUMA && !FLAG_IS_DEFAULT(UseNUMA))) { + log_warning(arguments) ("NUMA support for Heap depends on the file system when AllocateHeapAt option is used.\n"); + } + } return status; } @@ -4304,7 +4309,9 @@ jint Arguments::apply_ergo() { jint Arguments::adjust_after_os() { if (UseNUMA) { - if (UseParallelGC || UseParallelOldGC) { + if (!FLAG_IS_DEFAULT(AllocateHeapAt)) { + FLAG_SET_ERGO(bool, UseNUMA, false); + } else if (UseParallelGC || UseParallelOldGC) { if (FLAG_IS_DEFAULT(MinHeapDeltaBytes)) { FLAG_SET_DEFAULT(MinHeapDeltaBytes, 64*M); } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 51d1f04c092..2222ddfe618 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -4083,7 +4083,11 @@ public: diagnostic(bool, CompilerDirectivesPrint, false, \ "Print compiler directives on installation.") \ diagnostic(int, CompilerDirectivesLimit, 50, \ - "Limit on number of compiler directives.") + "Limit on number of compiler directives.") \ + \ + product(ccstr, AllocateHeapAt, NULL, \ + "Path to the directoy where a temporary file will be created " \ + "to use as the backing store for Java Heap.") /* diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 58ce94f6fa1..dce176a8a52 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1665,10 +1665,21 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) { return os::pd_create_stack_guard_pages(addr, bytes); } -char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) { - char* result = pd_reserve_memory(bytes, addr, alignment_hint); - if (result != NULL) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); +char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, int file_desc) { + char* result = NULL; + + if (file_desc != -1) { + // Could have called pd_reserve_memory() followed by replace_existing_mapping_with_file_mapping(), + // but AIX may use SHM in which case its more trouble to detach the segment and remap memory to the file. + result = os::map_memory_to_file(addr, bytes, file_desc); + if (result != NULL) { + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC); + } + } else { + result = pd_reserve_memory(bytes, addr, alignment_hint); + if (result != NULL) { + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); + } } return result; @@ -1685,10 +1696,18 @@ char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, return result; } -char* os::attempt_reserve_memory_at(size_t bytes, char* addr) { - char* result = pd_attempt_reserve_memory_at(bytes, addr); - if (result != NULL) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); +char* os::attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc) { + char* result = NULL; + if (file_desc != -1) { + result = pd_attempt_reserve_memory_at(bytes, addr, file_desc); + if (result != NULL) { + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC); + } + } else { + result = pd_attempt_reserve_memory_at(bytes, addr); + if (result != NULL) { + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC); + } } return result; } diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 61999125339..83b24f574ed 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -108,8 +108,9 @@ class os: AllStatic { } static char* pd_reserve_memory(size_t bytes, char* addr = 0, - size_t alignment_hint = 0); + size_t alignment_hint = 0); static char* pd_attempt_reserve_memory_at(size_t bytes, char* addr); + static char* pd_attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc); static void pd_split_reserved_memory(char *base, size_t size, size_t split, bool realloc); static bool pd_commit_memory(char* addr, size_t bytes, bool executable); @@ -310,11 +311,11 @@ class os: AllStatic { static int vm_allocation_granularity(); static char* reserve_memory(size_t bytes, char* addr = 0, - size_t alignment_hint = 0); + size_t alignment_hint = 0, int file_desc = -1); static char* reserve_memory(size_t bytes, char* addr, size_t alignment_hint, MEMFLAGS flags); - static char* reserve_memory_aligned(size_t size, size_t alignment); - static char* attempt_reserve_memory_at(size_t bytes, char* addr); + static char* reserve_memory_aligned(size_t size, size_t alignment, int file_desc = -1); + static char* attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc = -1); static void split_reserved_memory(char *base, size_t size, size_t split, bool realloc); static bool commit_memory(char* addr, size_t bytes, bool executable); @@ -345,6 +346,14 @@ class os: AllStatic { static bool create_stack_guard_pages(char* addr, size_t bytes); static bool pd_create_stack_guard_pages(char* addr, size_t bytes); static bool remove_stack_guard_pages(char* addr, size_t bytes); + // Helper function to create a new file with template jvmheap.XXXXXX. + // Returns a valid fd on success or else returns -1 + static int create_file_for_heap(const char* dir); + // Map memory to the file referred by fd. This function is slightly different from map_memory() + // and is added to be used for implementation of -XX:AllocateHeapAt + static char* map_memory_to_file(char* base, size_t size, int fd); + // Replace existing reserved memory with file mapping + static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd); static char* map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only = false, diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAt.java b/test/hotspot/jtreg/gc/TestAllocateHeapAt.java new file mode 100644 index 00000000000..aae999ce1ea --- /dev/null +++ b/test/hotspot/jtreg/gc/TestAllocateHeapAt.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 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 TestAllocateHeapAt.java + * @key gc + * @summary Test to check allocation of Java Heap with AllocateHeapAt option + * @library /test/lib + * @modules java.base/jdk.internal.misc + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; + +public class TestAllocateHeapAt { + public static void main(String args[]) throws Exception { + ArrayList<String> vmOpts = new ArrayList(); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(vmOpts, testVmOpts); + } + String test_dir = System.getProperty("test.dir", "."); + Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + test_dir, + "-Xlog:gc+heap=info", + "-Xmx32m", + "-Xms32m", + "-version"}); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < vmOpts.size(); i += 1) { + System.out.print(" " + vmOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldContain("Successfully allocated Java heap at location"); + output.shouldHaveExitValue(0); + } +} From 66c00b41848cde4e999bd48f689f6bee8f58393e Mon Sep 17 00:00:00 2001 From: Kishor Kharbas <kkharbas@openjdk.org> Date: Wed, 29 Nov 2017 16:45:31 -0800 Subject: [PATCH 077/165] 8190980: Develop test cases and collect test pass rate Develop tests covering all the test cases described in the test plan and achieve 100% pass rate for JEP 316: Heap Allocation on Alternative Memory Devices Reviewed-by: sangheki, tschatzl --- .../jtreg/gc/TestAllocateHeapAtError.java | 78 ++++++++++++++++ .../jtreg/gc/TestAllocateHeapAtMultiple.java | 91 +++++++++++++++++++ .../TestGCBasherWithAllocateHeapAt.java | 40 ++++++++ 3 files changed, 209 insertions(+) create mode 100644 test/hotspot/jtreg/gc/TestAllocateHeapAtError.java create mode 100644 test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java create mode 100644 test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java b/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java new file mode 100644 index 00000000000..b7f667c1a17 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 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 TestAllocateHeapAtError.java + * @key gc + * @summary Test to check correct handling of non-existent directory passed to AllocateHeapAt option + * @library /test/lib + * @modules java.base/jdk.internal.misc + */ + +import java.io.File; +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.UUID; + +public class TestAllocateHeapAtError { + public static void main(String args[]) throws Exception { + ArrayList<String> vmOpts = new ArrayList(); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(vmOpts, testVmOpts); + } + String test_dir = System.getProperty("test.dir", "."); + + File f = null; + do { + f = new File(test_dir, UUID.randomUUID().toString()); + } while(f.exists()); + + Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + f.getName(), + "-Xlog:gc+heap=info", + "-Xmx32m", + "-Xms32m", + "-version"}); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < vmOpts.size(); i += 1) { + System.out.print(" " + vmOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldContain("Could not create file for Heap"); + output.shouldContain("Error occurred during initialization of VM"); + output.shouldNotHaveExitValue(0); + } +} + diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java b/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java new file mode 100644 index 00000000000..492a976c1e9 --- /dev/null +++ b/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, 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 TestAllocateHeapAtMultiple.java + * @key gc + * @summary Test to check allocation of Java Heap with AllocateHeapAt option. Has multiple sub-tests to cover different code paths. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @requires vm.bits == "64" + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import java.util.ArrayList; +import java.util.Collections; + +public class TestAllocateHeapAtMultiple { + public static void main(String args[]) throws Exception { + ArrayList<String> vmOpts = new ArrayList(); + String[] testVmOpts = null; + + String test_dir = System.getProperty("test.dir", "."); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + testVmOpts = testVmOptsStr.split(" "); + } + + // Extra options for each of the sub-tests + String[] extraOptsList = new String[] { + "-Xmx32m -Xms32m -XX:+UseCompressedOops", // 1. With compressedoops enabled. + "-Xmx32m -Xms32m -XX:-UseCompressedOops", // 2. With compressedoops disabled. + "-Xmx32m -Xms32m -XX:HeapBaseMinAddress=3g", // 3. With user specified HeapBaseMinAddress. + "-Xmx4g -Xms4g", // 4. With larger heap size (UnscaledNarrowOop not possible). + "-Xmx4g -Xms4g -XX:+UseLargePages", // 5. Set UseLargePages. + "-Xmx4g -Xms4g -XX:+UseNUMA" // 6. Set UseNUMA. + }; + + for(String extraOpts : extraOptsList) { + vmOpts.clear(); + if(testVmOpts != null) { + Collections.addAll(vmOpts, testVmOpts); + } + // Add extra options specific to the sub-test. + String[] extraOptsArray = extraOpts.split(" "); + if(extraOptsArray != null) { + Collections.addAll(vmOpts, extraOptsArray); + } + // Add common options + Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + test_dir, + "-Xlog:gc+heap=info", + "-version"}); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < vmOpts.size(); i += 1) { + System.out.print(" " + vmOpts.get(i)); + } + System.out.println(); + + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldContain("Successfully allocated Java heap at location"); + output.shouldHaveExitValue(0); + } + } +} diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java new file mode 100644 index 00000000000..8cd540eb402 --- /dev/null +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016, 2017, 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. + * + */ + +import java.io.IOException; + +/* + * @test TestGCBasherWithAllocateHeapAt + * @key gc + * @key stress + * @requires vm.gc.G1 + * @requires vm.flavor == "server" & !vm.emulatedClient + * @summary Stress Java heap allocation with AllocateHeapAt flag using GC basher. + * @run main/othervm/timeout=500 -Xlog:gc*=info -Xmx256m -server -XX:+UseG1GC -XX:AllocateHeapAt=. TestGCBasherWithAllocateHeapAt 120000 + */ +public class TestGCBasherWithAllocateHeapAt { + public static void main(String[] args) throws IOException { + TestGCBasher.main(args); + } +} From 7617012f555ead8acec301af452b074c5894bf44 Mon Sep 17 00:00:00 2001 From: Volker Simonis <volker.simonis@gmail.com> Date: Tue, 28 Nov 2017 09:47:04 -0800 Subject: [PATCH 078/165] 8191927: Enable AppCDS for custom loaders on all 64-bit Linux and AIX Added "@requires vm.cds.custom.loaders" to mark tests related to custom loaders Reviewed-by: simonis, mseledtsov --- src/hotspot/share/classfile/classListParser.cpp | 8 +++++--- test/hotspot/jtreg/TEST.ROOT | 1 + .../jtreg/runtime/appcds/ProhibitedPackage.java | 7 +++---- test/hotspot/jtreg/runtime/appcds/TestCommon.java | 15 --------------- .../CheckCachedResolvedReferences.java | 9 ++++----- .../appcds/customLoader/ClassListFormatA.java | 3 +-- .../appcds/customLoader/ClassListFormatB.java | 3 +-- .../appcds/customLoader/ClassListFormatC.java | 3 +-- .../appcds/customLoader/ClassListFormatD.java | 3 +-- .../appcds/customLoader/ClassListFormatE.java | 3 +-- .../runtime/appcds/customLoader/HelloCustom.java | 3 +-- .../customLoader/LoaderSegregationTest.java | 3 +-- .../appcds/customLoader/ParallelTestMultiFP.java | 3 +-- .../appcds/customLoader/ParallelTestSingleFP.java | 3 +-- .../customLoader/ProhibitedPackageNamesTest.java | 3 +-- .../appcds/customLoader/ProtectionDomain.java | 3 +-- .../customLoader/SameNameInTwoLoadersTest.java | 3 +-- .../customLoader/UnintendedLoadersTest.java | 3 +-- .../UnloadUnregisteredLoaderTest.java | 3 +-- .../appcds/customLoader/UnsupportedPlatforms.java | 5 ++--- .../TransformRelatedClassesAppCDS.java | 3 ++- test/jtreg-ext/requires/VMProps.java | 14 ++++++++++++++ test/lib/jdk/test/lib/Platform.java | 14 +++++++++++++- 23 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/hotspot/share/classfile/classListParser.cpp b/src/hotspot/share/classfile/classListParser.cpp index ed7fa514dcc..b1fa759ae10 100644 --- a/src/hotspot/share/classfile/classListParser.cpp +++ b/src/hotspot/share/classfile/classListParser.cpp @@ -272,9 +272,11 @@ void ClassListParser::error(const char *msg, ...) { // This function is used for loading classes for customized class loaders // during archive dumping. InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) { -#if !((defined(LINUX) && defined(X86) && defined(_LP64)) || \ - (defined(SOLARIS) && defined(_LP64))) - // The only supported platforms are: (1) Linux/AMD64; (2) Solaris/64-bit +#if !(defined(_LP64) && (defined(LINUX)|| defined(SOLARIS) || defined(AIX))) + // The only supported platforms are: (1) Linux/64-bit; (2) Solaris/64-bit; (3) AIX/64-bit + // + // This #if condition should be in sync with the areCustomLoadersSupportedForCDS + // method in test/lib/jdk/test/lib/Platform.java. error("AppCDS custom class loaders not supported on this platform"); #endif diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 69eca8ec107..c7ccaf2f4de 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -53,6 +53,7 @@ requires.properties= \ vm.rtm.os \ vm.aot \ vm.cds \ + vm.cds.custom.loaders \ vm.graal.enabled \ docker.support diff --git a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java index 33ea5a33f0c..635bb0a974c 100644 --- a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java +++ b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java @@ -27,6 +27,7 @@ * @summary AppCDS handling of prohibited package. * AppCDS does not support uncompressed oops * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.cds * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -45,10 +46,8 @@ public class ProhibitedPackage { String appJar = TestCommon.getTestJar("prohibited_pkg.jar"); - // AppCDS for custom loader is only supported on linux-x64 and - // Solaris 64-bit platforms. - if ((Platform.isLinux() || Platform.isSolaris()) && - Platform.is64bit()) { + // Test support for customer loaders + if (Platform.areCustomLoadersSupportedForCDS()) { String classlist[] = new String[] { "java/lang/Object id: 1", "java/lang/Prohibited id: 2 super: 1 source: " + appJar diff --git a/test/hotspot/jtreg/runtime/appcds/TestCommon.java b/test/hotspot/jtreg/runtime/appcds/TestCommon.java index 882b235d1cd..ad6f8e272cb 100644 --- a/test/hotspot/jtreg/runtime/appcds/TestCommon.java +++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java @@ -321,19 +321,4 @@ public class TestCommon extends CDSTestUtils { } return dirFile.getPath(); } - - - // Returns true if custom loader is supported, based on a platform. - // Custom loader AppCDS is only supported for Linux-x64 and Solaris. - public static boolean isCustomLoaderSupported() { - boolean isLinux = Platform.isLinux(); - boolean isX64 = Platform.isX64(); - boolean isSolaris = Platform.isSolaris(); - - System.out.println("isCustomLoaderSupported: isX64 = " + isX64); - System.out.println("isCustomLoaderSupported: isLinux = " + isLinux); - System.out.println("isCustomLoaderSupported: isSolaris = " + isSolaris); - - return ((isX64 && isLinux) || isSolaris); - } } diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java index 73df2eaf816..6bd96c2abfd 100644 --- a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java @@ -26,8 +26,7 @@ * @test * @summary Test resolved_references * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @requires (vm.gc=="null") * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc @@ -53,9 +52,9 @@ public class CheckCachedResolvedReferences { String helloJarPath = ClassFileInstaller.getJarPath("hello.jar"); String classlist[] = new String[] { - "CheckCachedResolvedReferencesApp", - "java/lang/Object id: 1", - "Hello id: 2 super: 1 source: " + helloJarPath + "CheckCachedResolvedReferencesApp", // built-in app loader + "java/lang/Object id: 1", // boot loader + "Hello id: 2 super: 1 source: " + helloJarPath // custom loader }; TestCommon.testDump(appJar, classlist, use_whitebox_jar); diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java index b90e75d57d8..65c4d846499 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java index 5e24eefb8c4..6ae1488d125 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java index ab2006db56b..ff4dd11eb1e 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java index ed379a04629..0a911d6750e 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java index f17ad47ab83..b61e08459b6 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java index 0d5257df89b..750a251f178 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java @@ -27,8 +27,7 @@ * @summary Hello World test for AppCDS custom loader support * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java index 52a573f1885..f7d6b7bdcf0 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java @@ -28,8 +28,7 @@ * custom loader classes. * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java index 24b1f4034e9..bd7f99ccccc 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java @@ -27,8 +27,7 @@ * @summary Load classes from CDS archive into multiple custom loader using parallel threads * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java index 1a7f0b75609..585cd67f71c 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java @@ -27,8 +27,7 @@ * @summary Load classes from CDS archive into a single custom loader using parallel threads (finger print) * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java index 47f1cafd639..1b0da00532f 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java @@ -27,8 +27,7 @@ * @summary Make sure prohibited packages cannot be stored into archive for custom loaders. * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java index faeb75ffa82..dad59f91ab7 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java @@ -28,8 +28,7 @@ * * AppCDS does not support uncompressed oops * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java index 2b4e40938db..0e1df9ee222 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java @@ -28,8 +28,7 @@ * * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java index 042db736420..a83d2cdd9e3 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java @@ -27,8 +27,7 @@ * @summary Make sure classes intended for custom loaders cannot be loaded by BOOT/EXT/APP loaders * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java index 8b9885e6bab..1b80ebca452 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java @@ -28,8 +28,7 @@ * unloaded. * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) - * @requires (sun.arch.data.model == "64") - * @requires ((os.family == "linux") & (os.arch=="amd64")) | (os.family == "solaris") + * @requires vm.cds.custom.loaders * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/testlibrary * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java index 0175c5630a4..ade10497dab 100644 --- a/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java +++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java @@ -25,9 +25,9 @@ /* * @test * @summary Ensure that support for AppCDS custom class loaders are not enabled on unsupported platforms. - * The only supported platforms are Linux/AMD64 and 64-bit Solaris. * (NOTE: AppCDS does not support uncompressed oops) * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.cds * @library /test/lib /test/hotspot/jtreg/runtime/appcds * @modules java.base/jdk.internal.misc * java.management @@ -55,8 +55,7 @@ public class UnsupportedPlatforms { OutputAnalyzer out = TestCommon.dump(appJar, classlist); - if ((Platform.isSolaris() && Platform.is64bit()) || - (Platform.isLinux() && Platform.isX64())) { + if (Platform.areCustomLoadersSupportedForCDS()) { out.shouldNotContain(PLATFORM_NOT_SUPPORTED_WARNING); out.shouldHaveExitValue(0); } else { diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java index 1935715c09d..0ad374471a1 100644 --- a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java +++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java @@ -39,6 +39,7 @@ import java.io.File; import java.util.ArrayList; +import jdk.test.lib.Platform; import jdk.test.lib.process.OutputAnalyzer; // This class is intended to test 2 parent-child relationships: @@ -148,7 +149,7 @@ public class TransformRelatedClassesAppCDS extends TransformRelatedClasses { private void runWithCustomLoader(ArrayList<TestEntry> testTable) throws Exception { - if (!TestCommon.isCustomLoaderSupported()) { + if (!Platform.areCustomLoadersSupportedForCDS()) { log("custom loader not supported for this platform" + " - skipping test case for custom loader"); return; diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 13632fe7abe..215e143b610 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -74,6 +74,7 @@ public class VMProps implements Callable<Map<String, String>> { map.put("vm.aot", vmAOT()); // vm.cds is true if the VM is compiled with cds support. map.put("vm.cds", vmCDS()); + map.put("vm.cds.custom.loaders", vmCDSForCustomLoaders()); // vm.graal.enabled is true if Graal is used as JIT map.put("vm.graal.enabled", isGraalEnabled()); map.put("docker.support", dockerSupport()); @@ -296,6 +297,19 @@ public class VMProps implements Callable<Map<String, String>> { } } + /** + * Check for CDS support for custom loaders. + * + * @return true if CDS is supported for customer loader by the VM to be tested. + */ + protected String vmCDSForCustomLoaders() { + if (vmCDS().equals("true") && Platform.areCustomLoadersSupportedForCDS()) { + return "true"; + } else { + return "false"; + } + } + /** * Check if Graal is used as JIT compiler. * diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 16cfc52ccbd..d38a137068e 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -288,4 +288,16 @@ public class Platform { return "so"; } } + + /* + * This should match the #if condition in ClassListParser::load_class_from_source(). + */ + public static boolean areCustomLoadersSupportedForCDS() { + boolean isLinux = Platform.isLinux(); + boolean is64 = Platform.is64bit(); + boolean isSolaris = Platform.isSolaris(); + boolean isAix = Platform.isAix(); + + return (is64 && (isLinux || isSolaris || isAix)); + } } From 6f31e2eada3f565bc56682959a83f4947409fc16 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann <thartmann@openjdk.org> Date: Tue, 28 Nov 2017 19:02:23 +0100 Subject: [PATCH 079/165] 8191996: VM startup fails with CodeCacheExpansionSize=32768 is outside the allowed range Adjusted minimum value according to platform specific default values. Reviewed-by: kvn --- src/hotspot/share/runtime/globals.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index a6ae28a1683..405a5652165 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -3392,7 +3392,7 @@ public: \ product_pd(uintx, CodeCacheExpansionSize, \ "Code cache expansion size (in bytes)") \ - range(os::vm_page_size(), max_uintx) \ + range(32*K, max_uintx) \ \ diagnostic_pd(uintx, CodeCacheMinBlockLength, \ "Minimum number of segments in a code cache block") \ From 177b24b7d769d386211a2cd0bb4fce5199f40d0c Mon Sep 17 00:00:00 2001 From: Ioi Lam <iklam@openjdk.org> Date: Wed, 29 Nov 2017 18:43:35 -0800 Subject: [PATCH 080/165] 8191747: [TESTBUG] runtime/appcds/DumpClassList.java and ProhibitedPackage.java fail on product bits Reviewed-by: simonis --- test/hotspot/jtreg/runtime/appcds/DumpClassList.java | 4 ++-- .../jtreg/runtime/appcds/ProhibitedPackage.java | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/runtime/appcds/DumpClassList.java b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java index c2670a7ef45..4526770c2da 100644 --- a/test/hotspot/jtreg/runtime/appcds/DumpClassList.java +++ b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java @@ -93,11 +93,11 @@ public class DumpClassList { output = TestCommon.createArchive(appJar, appClass, "-Xbootclasspath/a:" + appendJar, "-XX:+UnlockDiagnosticVMOptions", - "-XX:+PrintSystemDictionaryAtExit", + "-Xlog:class+load", "-XX:SharedClassListFile=" + classList); TestCommon.checkDump(output) .shouldNotContain("Preload Warning: Cannot find java/lang/invoke/LambdaForm") .shouldNotContain("Preload Warning: Cannot find boot/append/Foo") - .shouldContain("boot.append.Foo, loader <shared, not restored>"); + .shouldContain("[info][class,load] boot.append.Foo"); } } diff --git a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java index 635bb0a974c..92e7227c9c5 100644 --- a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java +++ b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java @@ -55,11 +55,9 @@ public class ProhibitedPackage { // Make sure a class in a prohibited package for a custom loader // will be ignored during dumping. - TestCommon.dump(appJar, - classlist, - "-XX:+PrintSystemDictionaryAtExit") + TestCommon.dump(appJar, classlist, "-Xlog:cds") .shouldContain("Dumping") - .shouldNotContain("java.lang.Prohibited") + .shouldContain("[cds] Prohibited package for non-bootstrap classes: java/lang/Prohibited.class") .shouldHaveExitValue(0); } @@ -68,9 +66,9 @@ public class ProhibitedPackage { // will be ignored during dumping. TestCommon.dump(appJar, TestCommon.list("java/lang/Prohibited", "ProhibitedHelper"), - "-XX:+PrintSystemDictionaryAtExit") + "-Xlog:class+load") .shouldContain("Dumping") - .shouldNotContain("java.lang.Prohibited") + .shouldNotContain("[info][class,load] java.lang.Prohibited source: ") .shouldHaveExitValue(0); // Try loading the class in a prohibited package with various -Xshare From 58dd5210ec0309240c0709b9dd81ec5b09c63ba7 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson <stefank@openjdk.org> Date: Tue, 28 Nov 2017 21:43:45 +0100 Subject: [PATCH 081/165] 8192061: Clean up allocation.inline.hpp includes Reviewed-by: eosterlund, coleenp --- src/hotspot/os/aix/osThread_aix.cpp | 1 + src/hotspot/os/bsd/osThread_bsd.cpp | 1 + src/hotspot/os/linux/osThread_linux.cpp | 1 + src/hotspot/share/classfile/klassFactory.hpp | 2 +- .../share/classfile/sharedPathsMiscInfo.cpp | 12 ++ .../share/classfile/sharedPathsMiscInfo.hpp | 13 +- src/hotspot/share/classfile/stringTable.hpp | 2 +- src/hotspot/share/classfile/symbolTable.hpp | 2 +- src/hotspot/share/compiler/compileTask.hpp | 4 +- src/hotspot/share/compiler/methodMatcher.hpp | 2 +- src/hotspot/share/compiler/oopMap.hpp | 2 + src/hotspot/share/gc/cms/allocationStats.cpp | 17 +++ src/hotspot/share/gc/cms/allocationStats.hpp | 17 +-- src/hotspot/share/gc/cms/gSpaceCounters.cpp | 5 + src/hotspot/share/gc/cms/gSpaceCounters.hpp | 4 +- .../share/gc/g1/g1ConcurrentRefine.cpp | 1 + .../share/gc/g1/g1FullGCCompactionPoint.hpp | 1 + .../share/gc/g1/g1ParScanThreadState.cpp | 19 +++ .../share/gc/g1/g1ParScanThreadState.hpp | 20 +-- .../gc/parallel/psAdaptiveSizePolicy.cpp | 1 + .../gc/parallel/psGenerationCounters.cpp | 2 +- .../share/gc/parallel/spaceCounters.cpp | 5 + .../share/gc/parallel/spaceCounters.hpp | 4 +- .../share/gc/serial/cSpaceCounters.cpp | 5 + .../share/gc/serial/cSpaceCounters.hpp | 4 +- .../share/gc/shared/adaptiveSizePolicy.cpp | 1 + .../share/gc/shared/collectorCounters.cpp | 1 + src/hotspot/share/gc/shared/gcStats.cpp | 3 +- src/hotspot/share/gc/shared/gcUtil.hpp | 2 +- src/hotspot/share/gc/shared/gcUtil.inline.hpp | 35 ++++++ .../share/gc/shared/generationCounters.cpp | 1 + .../share/gc/shared/hSpaceCounters.cpp | 2 +- src/hotspot/share/gc/shared/taskqueue.hpp | 2 + src/hotspot/share/memory/resourceArea.cpp | 2 +- src/hotspot/share/memory/resourceArea.hpp | 13 +- .../share/memory/resourceArea.inline.hpp | 43 +++++++ src/hotspot/share/oops/array.hpp | 1 - src/hotspot/share/oops/constantPool.cpp | 9 ++ src/hotspot/share/oops/constantPool.hpp | 8 +- src/hotspot/share/oops/generateOopMap.cpp | 7 ++ src/hotspot/share/oops/generateOopMap.hpp | 4 +- src/hotspot/share/opto/reg_split.cpp | 2 +- src/hotspot/share/precompiled/precompiled.hpp | 1 - .../share/prims/jvmtiEnvThreadState.hpp | 1 - .../share/prims/jvmtiEventController.hpp | 1 - src/hotspot/share/prims/jvmtiThreadState.hpp | 1 - src/hotspot/share/prims/methodHandles.cpp | 20 +++ src/hotspot/share/prims/methodHandles.hpp | 20 +-- src/hotspot/share/runtime/arguments.cpp | 119 ++++++++++++++++++ src/hotspot/share/runtime/arguments.hpp | 119 ++---------------- src/hotspot/share/runtime/objectMonitor.cpp | 14 +++ src/hotspot/share/runtime/objectMonitor.hpp | 18 +-- src/hotspot/share/runtime/park.cpp | 3 +- .../share/services/diagnosticArgument.cpp | 23 ++++ .../share/services/diagnosticArgument.hpp | 25 +--- src/hotspot/share/utilities/decoder.cpp | 1 + src/hotspot/share/utilities/decoder_elf.cpp | 1 + src/hotspot/share/utilities/growableArray.cpp | 5 + src/hotspot/share/utilities/growableArray.hpp | 8 +- src/hotspot/share/utilities/stack.hpp | 1 - .../gtest/logging/test_logMessageTest.cpp | 1 + 61 files changed, 409 insertions(+), 256 deletions(-) create mode 100644 src/hotspot/share/gc/shared/gcUtil.inline.hpp create mode 100644 src/hotspot/share/memory/resourceArea.inline.hpp diff --git a/src/hotspot/os/aix/osThread_aix.cpp b/src/hotspot/os/aix/osThread_aix.cpp index 0b13454ffef..6303dc27eb8 100644 --- a/src/hotspot/os/aix/osThread_aix.cpp +++ b/src/hotspot/os/aix/osThread_aix.cpp @@ -25,6 +25,7 @@ // no precompiled headers +#include "memory/allocation.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp index de1383be848..c614f3825e3 100644 --- a/src/hotspot/os/bsd/osThread_bsd.cpp +++ b/src/hotspot/os/bsd/osThread_bsd.cpp @@ -23,6 +23,7 @@ */ // no precompiled headers +#include "memory/allocation.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/osThread.hpp" diff --git a/src/hotspot/os/linux/osThread_linux.cpp b/src/hotspot/os/linux/osThread_linux.cpp index 381e7b0e7ba..6f7e074a522 100644 --- a/src/hotspot/os/linux/osThread_linux.cpp +++ b/src/hotspot/os/linux/osThread_linux.cpp @@ -23,6 +23,7 @@ */ // no precompiled headers +#include "memory/allocation.inline.hpp" #include "runtime/mutex.hpp" #include "runtime/osThread.hpp" diff --git a/src/hotspot/share/classfile/klassFactory.hpp b/src/hotspot/share/classfile/klassFactory.hpp index cb3ed851dd9..c08f8b9a119 100644 --- a/src/hotspot/share/classfile/klassFactory.hpp +++ b/src/hotspot/share/classfile/klassFactory.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_KLASSFACTORY_HPP #define SHARE_VM_CLASSFILE_KLASSFACTORY_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "runtime/handles.hpp" class ClassFileStream; diff --git a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp index 7f9314ea63c..06d5a50600b 100644 --- a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp +++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp @@ -34,6 +34,18 @@ #include "runtime/arguments.hpp" #include "utilities/ostream.hpp" +SharedPathsMiscInfo::SharedPathsMiscInfo() { + _buf_size = INITIAL_BUF_SIZE; + _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass); + _allocated = true; +} + +SharedPathsMiscInfo::~SharedPathsMiscInfo() { + if (_allocated) { + FREE_C_HEAP_ARRAY(char, _buf_start); + } +} + void SharedPathsMiscInfo::add_path(const char* path, int type) { log_info(class, path)("type=%s ", type_name(type)); ClassLoader::trace_class_path("add misc shared path ", path); diff --git a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp index 2099dc24881..e5576156e67 100644 --- a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp +++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp @@ -74,11 +74,7 @@ public: INITIAL_BUF_SIZE = 128 }; // This constructor is used when creating the misc information (during dump) - SharedPathsMiscInfo() { - _buf_size = INITIAL_BUF_SIZE; - _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass); - _allocated = true; - } + SharedPathsMiscInfo(); // This constructor is used when validating the misc info (during run time) SharedPathsMiscInfo(char *buff, int size) { _cur_ptr = _buf_start = buff; @@ -86,11 +82,8 @@ public: _buf_size = size; _allocated = false; } - ~SharedPathsMiscInfo() { - if (_allocated) { - FREE_C_HEAP_ARRAY(char, _buf_start); - } - } + ~SharedPathsMiscInfo(); + int get_used_bytes() { return _cur_ptr - _buf_start; } diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index a08bd225277..77538a53e5c 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_STRINGTABLE_HPP #define SHARE_VM_CLASSFILE_STRINGTABLE_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "utilities/hashtable.hpp" template <class T, class N> class CompactHashtable; diff --git a/src/hotspot/share/classfile/symbolTable.hpp b/src/hotspot/share/classfile/symbolTable.hpp index 931669650b5..bf3c36257da 100644 --- a/src/hotspot/share/classfile/symbolTable.hpp +++ b/src/hotspot/share/classfile/symbolTable.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP #define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "oops/symbol.hpp" #include "utilities/hashtable.hpp" diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index c7329c9ba45..3522ee4f10e 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_VM_COMPILER_COMPILETASK_HPP #define SHARE_VM_COMPILER_COMPILETASK_HPP -#include "code/nmethod.hpp" #include "ci/ciMethod.hpp" +#include "code/nmethod.hpp" #include "compiler/compileLog.hpp" -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "utilities/xmlstream.hpp" // CompileTask diff --git a/src/hotspot/share/compiler/methodMatcher.hpp b/src/hotspot/share/compiler/methodMatcher.hpp index 546af4d5ed7..4adf6587417 100644 --- a/src/hotspot/share/compiler/methodMatcher.hpp +++ b/src/hotspot/share/compiler/methodMatcher.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_COMPILER_METHODMATCHER_HPP #define SHARE_VM_COMPILER_METHODMATCHER_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "runtime/handles.inline.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp index 6c9fe4ee299..853f42a6f0d 100644 --- a/src/hotspot/share/compiler/oopMap.hpp +++ b/src/hotspot/share/compiler/oopMap.hpp @@ -28,6 +28,7 @@ #include "code/compressedStream.hpp" #include "code/vmreg.hpp" #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" #include "utilities/growableArray.hpp" // Interface for generating the frame map for compiled code. A frame map @@ -42,6 +43,7 @@ class frame; class RegisterMap; class DerivedPointerEntry; +class OopClosure; class OopMapValue: public StackObj { friend class VMStructs; diff --git a/src/hotspot/share/gc/cms/allocationStats.cpp b/src/hotspot/share/gc/cms/allocationStats.cpp index f23fa37c313..3bc7074a46e 100644 --- a/src/hotspot/share/gc/cms/allocationStats.cpp +++ b/src/hotspot/share/gc/cms/allocationStats.cpp @@ -30,3 +30,20 @@ // Technically this should be derived from machine speed, and // ideally it would be dynamically adjusted. float AllocationStats::_threshold = ((float)CMS_SweepTimerThresholdMillis)/1000; + +void AllocationStats::initialize(bool split_birth) { + AdaptivePaddedAverage* dummy = + new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight, + CMS_FLSPadding); + _desired = 0; + _coal_desired = 0; + _surplus = 0; + _bfr_surp = 0; + _prev_sweep = 0; + _before_sweep = 0; + _coal_births = 0; + _coal_deaths = 0; + _split_births = (split_birth ? 1 : 0); + _split_deaths = 0; + _returned_bytes = 0; +} diff --git a/src/hotspot/share/gc/cms/allocationStats.hpp b/src/hotspot/share/gc/cms/allocationStats.hpp index 747b2904bed..c98b23f6918 100644 --- a/src/hotspot/share/gc/cms/allocationStats.hpp +++ b/src/hotspot/share/gc/cms/allocationStats.hpp @@ -64,22 +64,7 @@ class AllocationStats VALUE_OBJ_CLASS_SPEC { ssize_t _split_deaths; // loss from splitting size_t _returned_bytes; // number of bytes returned to list. public: - void initialize(bool split_birth = false) { - AdaptivePaddedAverage* dummy = - new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight, - CMS_FLSPadding); - _desired = 0; - _coal_desired = 0; - _surplus = 0; - _bfr_surp = 0; - _prev_sweep = 0; - _before_sweep = 0; - _coal_births = 0; - _coal_deaths = 0; - _split_births = (split_birth ? 1 : 0); - _split_deaths = 0; - _returned_bytes = 0; - } + void initialize(bool split_birth = false); AllocationStats() { initialize(); diff --git a/src/hotspot/share/gc/cms/gSpaceCounters.cpp b/src/hotspot/share/gc/cms/gSpaceCounters.cpp index dce1f39f6d5..36776c52945 100644 --- a/src/hotspot/share/gc/cms/gSpaceCounters.cpp +++ b/src/hotspot/share/gc/cms/gSpaceCounters.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/cms/gSpaceCounters.hpp" #include "gc/shared/generation.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "utilities/macros.hpp" @@ -71,3 +72,7 @@ GSpaceCounters::GSpaceCounters(const char* name, int ordinal, size_t max_size, _gen->capacity(), CHECK); } } + +GSpaceCounters::~GSpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); +} diff --git a/src/hotspot/share/gc/cms/gSpaceCounters.hpp b/src/hotspot/share/gc/cms/gSpaceCounters.hpp index 64ae6c60b26..40f181f4c4c 100644 --- a/src/hotspot/share/gc/cms/gSpaceCounters.hpp +++ b/src/hotspot/share/gc/cms/gSpaceCounters.hpp @@ -52,9 +52,7 @@ class GSpaceCounters: public CHeapObj<mtGC> { GSpaceCounters(const char* name, int ordinal, size_t max_size, Generation* g, GenerationCounters* gc, bool sampled=true); - ~GSpaceCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~GSpaceCounters(); inline void update_capacity() { _capacity->set_value(_gen->capacity()); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index a28e0e790b4..31d09e9428b 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -26,6 +26,7 @@ #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "logging/log.hpp" +#include "memory/allocation.inline.hpp" #include "runtime/java.hpp" #include "runtime/thread.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp index 95f01be62c6..0ec0b324aab 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1FULLGCCOMPACTIONPOINT_HPP #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" #include "utilities/growableArray.hpp" class HeapRegion; diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 32b405d4226..03e75f1989d 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -32,6 +32,7 @@ #include "gc/g1/g1StringDedup.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/taskqueue.inline.hpp" +#include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/prefetch.inline.hpp" @@ -390,3 +391,21 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markOop m) { } } +G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length) : + _g1h(g1h), + _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, n_workers, mtGC)), + _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, young_cset_length, mtGC)), + _young_cset_length(young_cset_length), + _n_workers(n_workers), + _flushed(false) { + for (uint i = 0; i < n_workers; ++i) { + _states[i] = NULL; + } + memset(_surviving_young_words_total, 0, young_cset_length * sizeof(size_t)); +} + +G1ParScanThreadStateSet::~G1ParScanThreadStateSet() { + assert(_flushed, "thread local state from the per thread states should have been flushed"); + FREE_C_HEAP_ARRAY(G1ParScanThreadState*, _states); + FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_total); +} diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 9bbb9a90a62..310b4270b02 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -204,24 +204,8 @@ class G1ParScanThreadStateSet : public StackObj { bool _flushed; public: - G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length) : - _g1h(g1h), - _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, n_workers, mtGC)), - _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, young_cset_length, mtGC)), - _young_cset_length(young_cset_length), - _n_workers(n_workers), - _flushed(false) { - for (uint i = 0; i < n_workers; ++i) { - _states[i] = NULL; - } - memset(_surviving_young_words_total, 0, young_cset_length * sizeof(size_t)); - } - - ~G1ParScanThreadStateSet() { - assert(_flushed, "thread local state from the per thread states should have been flushed"); - FREE_C_HEAP_ARRAY(G1ParScanThreadState*, _states); - FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_total); - } + G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length); + ~G1ParScanThreadStateSet(); void flush(); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index d4350f7db6c..437356866df 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -29,6 +29,7 @@ #include "gc/parallel/psScavenge.hpp" #include "gc/shared/collectorPolicy.hpp" #include "gc/shared/gcCause.hpp" +#include "gc/shared/gcUtil.inline.hpp" #include "gc/shared/gcPolicyCounters.hpp" #include "logging/log.hpp" #include "runtime/timer.hpp" diff --git a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp index d200258acdf..1b0e8d320a9 100644 --- a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp +++ b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp @@ -25,9 +25,9 @@ #include "precompiled.hpp" #include "gc/parallel/psGenerationCounters.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" - PSGenerationCounters::PSGenerationCounters(const char* name, int ordinal, int spaces, size_t min_capacity, diff --git a/src/hotspot/share/gc/parallel/spaceCounters.cpp b/src/hotspot/share/gc/parallel/spaceCounters.cpp index 36cb8099523..3a7231d8b25 100644 --- a/src/hotspot/share/gc/parallel/spaceCounters.cpp +++ b/src/hotspot/share/gc/parallel/spaceCounters.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/parallel/spaceCounters.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "utilities/macros.hpp" @@ -63,3 +64,7 @@ SpaceCounters::SpaceCounters(const char* name, int ordinal, size_t max_size, _object_space->capacity_in_bytes(), CHECK); } } + +SpaceCounters::~SpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); +} diff --git a/src/hotspot/share/gc/parallel/spaceCounters.hpp b/src/hotspot/share/gc/parallel/spaceCounters.hpp index 38e6542085f..e2cf1621b3d 100644 --- a/src/hotspot/share/gc/parallel/spaceCounters.hpp +++ b/src/hotspot/share/gc/parallel/spaceCounters.hpp @@ -53,9 +53,7 @@ class SpaceCounters: public CHeapObj<mtGC> { SpaceCounters(const char* name, int ordinal, size_t max_size, MutableSpace* m, GenerationCounters* gc); - ~SpaceCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~SpaceCounters(); inline void update_capacity() { _capacity->set_value(_object_space->capacity_in_bytes()); diff --git a/src/hotspot/share/gc/serial/cSpaceCounters.cpp b/src/hotspot/share/gc/serial/cSpaceCounters.cpp index 84e9c96bd4e..076902d1dca 100644 --- a/src/hotspot/share/gc/serial/cSpaceCounters.cpp +++ b/src/hotspot/share/gc/serial/cSpaceCounters.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/serial/cSpaceCounters.hpp" +#include "memory/allocation.inline.hpp" #include "memory/metaspace.hpp" #include "memory/resourceArea.hpp" @@ -64,6 +65,10 @@ CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size, } } +CSpaceCounters::~CSpaceCounters() { + if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); +} + void CSpaceCounters::update_capacity() { _capacity->set_value(_space->capacity()); } diff --git a/src/hotspot/share/gc/serial/cSpaceCounters.hpp b/src/hotspot/share/gc/serial/cSpaceCounters.hpp index ffe43ab14a2..da0c4cfbf31 100644 --- a/src/hotspot/share/gc/serial/cSpaceCounters.hpp +++ b/src/hotspot/share/gc/serial/cSpaceCounters.hpp @@ -52,9 +52,7 @@ class CSpaceCounters: public CHeapObj<mtGC> { CSpaceCounters(const char* name, int ordinal, size_t max_size, ContiguousSpace* s, GenerationCounters* gc); - ~CSpaceCounters() { - if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space); - } + ~CSpaceCounters(); virtual void update_capacity(); virtual void update_used(); diff --git a/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp b/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp index 9aa3f79c08d..830835f19dc 100644 --- a/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp @@ -26,6 +26,7 @@ #include "gc/shared/adaptiveSizePolicy.hpp" #include "gc/shared/collectorPolicy.hpp" #include "gc/shared/gcCause.hpp" +#include "gc/shared/gcUtil.inline.hpp" #include "gc/shared/workgroup.hpp" #include "logging/log.hpp" #include "runtime/timer.hpp" diff --git a/src/hotspot/share/gc/shared/collectorCounters.cpp b/src/hotspot/share/gc/shared/collectorCounters.cpp index ff1edac2e4b..2a2e60f99b3 100644 --- a/src/hotspot/share/gc/shared/collectorCounters.cpp +++ b/src/hotspot/share/gc/shared/collectorCounters.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/collectorCounters.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/gc/shared/gcStats.cpp b/src/hotspot/share/gc/shared/gcStats.cpp index 3a050f73066..14e63ea108f 100644 --- a/src/hotspot/share/gc/shared/gcStats.cpp +++ b/src/hotspot/share/gc/shared/gcStats.cpp @@ -24,8 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/gcStats.hpp" -#include "gc/shared/gcUtil.hpp" -#include "memory/allocation.inline.hpp" +#include "gc/shared/gcUtil.inline.hpp" GCStats::GCStats() { _avg_promoted = new AdaptivePaddedNoZeroDevAverage( diff --git a/src/hotspot/share/gc/shared/gcUtil.hpp b/src/hotspot/share/gc/shared/gcUtil.hpp index 6b2f929bb5d..1fafbb51a00 100644 --- a/src/hotspot/share/gc/shared/gcUtil.hpp +++ b/src/hotspot/share/gc/shared/gcUtil.hpp @@ -146,7 +146,7 @@ class AdaptivePaddedAverage : public AdaptiveWeightedAverage { // Placement support void* operator new(size_t ignored, void* p) throw() { return p; } // Allocator - void* operator new(size_t size) throw() { return CHeapObj<mtGC>::operator new(size); } + void* operator new(size_t size) throw(); // Accessor float padded_average() const { return _padded_avg; } diff --git a/src/hotspot/share/gc/shared/gcUtil.inline.hpp b/src/hotspot/share/gc/shared/gcUtil.inline.hpp new file mode 100644 index 00000000000..f03480becde --- /dev/null +++ b/src/hotspot/share/gc/shared/gcUtil.inline.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002, 2015, 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP +#define SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP + +#include "gc/shared/gcUtil.hpp" +#include "memory/allocation.inline.hpp" + +inline void* AdaptivePaddedAverage::operator new(size_t size) throw() { + return CHeapObj<mtGC>::operator new(size); +} + +#endif // SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp index d1b8f3e64c4..1efa58c4790 100644 --- a/src/hotspot/share/gc/shared/generationCounters.cpp +++ b/src/hotspot/share/gc/shared/generationCounters.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/generationCounters.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" void GenerationCounters::initialize(const char* name, int ordinal, int spaces, diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp index 8b5e92fa7f4..351c5cd920f 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.cpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "gc/shared/hSpaceCounters.hpp" -#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/perfData.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp index 439d81e6e23..ca0e1a02a58 100644 --- a/src/hotspot/share/gc/shared/taskqueue.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.hpp @@ -26,6 +26,8 @@ #define SHARE_VM_GC_SHARED_TASKQUEUE_HPP #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/ostream.hpp" #include "utilities/stack.hpp" // Simple TaskQueue stats that are collected by default in debug builds. diff --git a/src/hotspot/share/memory/resourceArea.cpp b/src/hotspot/share/memory/resourceArea.cpp index 3995e6335db..aca27c0b56a 100644 --- a/src/hotspot/share/memory/resourceArea.cpp +++ b/src/hotspot/share/memory/resourceArea.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "memory/allocation.inline.hpp" -#include "memory/resourceArea.hpp" +#include "memory/resourceArea.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/thread.inline.hpp" #include "services/memTracker.hpp" diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp index 5fc13ac9243..32d57fd6e61 100644 --- a/src/hotspot/share/memory/resourceArea.hpp +++ b/src/hotspot/share/memory/resourceArea.hpp @@ -57,18 +57,7 @@ public: debug_only(_nesting = 0;); } - char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) { -#ifdef ASSERT - if (_nesting < 1 && !_warned++) - fatal("memory leak: allocating without ResourceMark"); - if (UseMallocOnly) { - // use malloc, but save pointer in res. area for later freeing - char** save = (char**)internal_malloc_4(sizeof(char*)); - return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC)); - } -#endif - return (char*)Amalloc(size, alloc_failmode); - } + char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); // Bias this resource area to specific memory type // (by default, ResourceArea is tagged as mtThread, per-thread general purpose storage) diff --git a/src/hotspot/share/memory/resourceArea.inline.hpp b/src/hotspot/share/memory/resourceArea.inline.hpp new file mode 100644 index 00000000000..895c0b71bbf --- /dev/null +++ b/src/hotspot/share/memory/resourceArea.inline.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2017, 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. + * + */ + +#ifndef SHARE_VM_MEMORY_RESOURCEAREA_INLINE_HPP +#define SHARE_VM_MEMORY_RESOURCEAREA_INLINE_HPP + +#include "memory/resourceArea.hpp" + +inline char* ResourceArea::allocate_bytes(size_t size, AllocFailType alloc_failmode) { +#ifdef ASSERT + if (_nesting < 1 && !_warned++) + fatal("memory leak: allocating without ResourceMark"); + if (UseMallocOnly) { + // use malloc, but save pointer in res. area for later freeing + char** save = (char**)internal_malloc_4(sizeof(char*)); + return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC)); + } +#endif + return (char*)Amalloc(size, alloc_failmode); +} + +#endif // SHARE_VM_MEMORY_RESOURCEAREA_INLINE_HPP diff --git a/src/hotspot/share/oops/array.hpp b/src/hotspot/share/oops/array.hpp index 225331e0521..f56da8e26e0 100644 --- a/src/hotspot/share/oops/array.hpp +++ b/src/hotspot/share/oops/array.hpp @@ -26,7 +26,6 @@ #define SHARE_VM_OOPS_ARRAY_HPP #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" #include "memory/metaspace.hpp" #include "runtime/orderAccess.hpp" #include "utilities/align.hpp" diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 9e1eefd5251..4dd2e35e2cf 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -31,6 +31,7 @@ #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "interpreter/linkResolver.hpp" +#include "memory/allocation.inline.hpp" #include "memory/heapInspection.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" @@ -2300,3 +2301,11 @@ SymbolHashMapEntry* SymbolHashMap::find_entry(Symbol* sym) { } return NULL; } + +void SymbolHashMap::initialize_table(int table_size) { + _table_size = table_size; + _buckets = NEW_C_HEAP_ARRAY(SymbolHashMapBucket, table_size, mtSymbol); + for (int index = 0; index < table_size; index++) { + _buckets[index].clear(); + } +} diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index 681935edae5..8dc75922419 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -982,13 +982,7 @@ class SymbolHashMap: public CHeapObj<mtSymbol> { int _table_size; SymbolHashMapBucket* _buckets; - void initialize_table(int table_size) { - _table_size = table_size; - _buckets = NEW_C_HEAP_ARRAY(SymbolHashMapBucket, table_size, mtSymbol); - for (int index = 0; index < table_size; index++) { - _buckets[index].clear(); - } - } + void initialize_table(int table_size); public: diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index e8edb2c0030..50d4de01f4f 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -27,6 +27,7 @@ #include "interpreter/bytecodeStream.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/allocation.inline.hpp" #include "oops/generateOopMap.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" @@ -217,6 +218,12 @@ public: int RetTable::_init_nof_entries = 10; int RetTableEntry::_init_nof_jsrs = 5; +RetTableEntry::RetTableEntry(int target, RetTableEntry *next) { + _target_bci = target; + _jsrs = new GrowableArray<intptr_t>(_init_nof_jsrs); + _next = next; +} + void RetTableEntry::add_delta(int bci, int delta) { if (_target_bci > bci) _target_bci += delta; diff --git a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp index e401746240a..3fa44525146 100644 --- a/src/hotspot/share/oops/generateOopMap.hpp +++ b/src/hotspot/share/oops/generateOopMap.hpp @@ -26,7 +26,7 @@ #define SHARE_VM_OOPS_GENERATEOOPMAP_HPP #include "interpreter/bytecodeStream.hpp" -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "memory/universe.inline.hpp" #include "oops/method.hpp" #include "oops/oopsHierarchy.hpp" @@ -57,7 +57,7 @@ class RetTableEntry : public ResourceObj { GrowableArray<intptr_t> * _jsrs; // List of return addresses (bytecode index) RetTableEntry *_next; // Link to next entry public: - RetTableEntry(int target, RetTableEntry *next) { _target_bci=target; _jsrs = new GrowableArray<intptr_t>(_init_nof_jsrs); _next = next; } + RetTableEntry(int target, RetTableEntry *next); // Query int target_bci() const { return _target_bci; } diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index d0c72c3a5b1..05f42d5a343 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "libadt/vectset.hpp" #include "memory/allocation.inline.hpp" -#include "memory/resourceArea.hpp" +#include "memory/resourceArea.inline.hpp" #include "opto/addnode.hpp" #include "opto/c2compiler.hpp" #include "opto/callnode.hpp" diff --git a/src/hotspot/share/precompiled/precompiled.hpp b/src/hotspot/share/precompiled/precompiled.hpp index dd76a0b9c93..728a75535c0 100644 --- a/src/hotspot/share/precompiled/precompiled.hpp +++ b/src/hotspot/share/precompiled/precompiled.hpp @@ -131,7 +131,6 @@ # include "jvmtifiles/jvmti.h" # include "logging/log.hpp" # include "memory/allocation.hpp" -# include "memory/allocation.inline.hpp" # include "memory/arena.hpp" # include "memory/heap.hpp" # include "memory/iterator.hpp" diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp index c25e5e34547..9b947f93e94 100644 --- a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp @@ -27,7 +27,6 @@ #include "jvmtifiles/jvmti.h" #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" #include "oops/instanceKlass.hpp" #include "prims/jvmtiEventController.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/prims/jvmtiEventController.hpp b/src/hotspot/share/prims/jvmtiEventController.hpp index 90b000ccfb3..c250c2f4772 100644 --- a/src/hotspot/share/prims/jvmtiEventController.hpp +++ b/src/hotspot/share/prims/jvmtiEventController.hpp @@ -27,7 +27,6 @@ #include "jvmtifiles/jvmti.h" #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" #include "utilities/globalDefinitions.hpp" // forward declaration diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 6633c1c570f..16a0a3455c7 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -27,7 +27,6 @@ #include "jvmtifiles/jvmti.h" #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" #include "prims/jvmtiEventController.hpp" #include "runtime/thread.hpp" #include "utilities/growableArray.hpp" diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index 8374755490e..2da189889fb 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -1029,6 +1029,26 @@ void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) { } } +void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid) { + if (TraceMethodHandles) { + const char* name = vmIntrinsics::name_at(iid); + if (*name == '_') name += 1; + const size_t len = strlen(name) + 50; + char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal); + const char* suffix = ""; + if (is_signature_polymorphic(iid)) { + if (is_signature_polymorphic_static(iid)) + suffix = "/static"; + else + suffix = "/private"; + } + jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix); + trace_method_handle(_masm, qname); + // Note: Don't free the allocated char array because it's used + // during runtime. + } +} + // // Here are the native methods in java.lang.invoke.MethodHandleNatives // They are the private interface between this JVM and the HotSpot-specific diff --git a/src/hotspot/share/prims/methodHandles.hpp b/src/hotspot/share/prims/methodHandles.hpp index df83f23daf3..2be115636af 100644 --- a/src/hotspot/share/prims/methodHandles.hpp +++ b/src/hotspot/share/prims/methodHandles.hpp @@ -195,25 +195,7 @@ public: // Tracing static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN; - static void trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid) { - if (TraceMethodHandles) { - const char* name = vmIntrinsics::name_at(iid); - if (*name == '_') name += 1; - const size_t len = strlen(name) + 50; - char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal); - const char* suffix = ""; - if (is_signature_polymorphic(iid)) { - if (is_signature_polymorphic_static(iid)) - suffix = "/static"; - else - suffix = "/private"; - } - jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix); - trace_method_handle(_masm, qname); - // Note: Don't free the allocated char array because it's used - // during runtime. - } - } + static void trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid); }; //------------------------------------------------------------------------------ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index c8d8ac912a2..0f5a32717e6 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -114,6 +114,108 @@ bool Arguments::_has_jimage = false; char* Arguments::_ext_dirs = NULL; +bool PathString::set_value(const char *value) { + if (_value != NULL) { + FreeHeap(_value); + } + _value = AllocateHeap(strlen(value)+1, mtArguments); + assert(_value != NULL, "Unable to allocate space for new path value"); + if (_value != NULL) { + strcpy(_value, value); + } else { + // not able to allocate + return false; + } + return true; +} + +void PathString::append_value(const char *value) { + char *sp; + size_t len = 0; + if (value != NULL) { + len = strlen(value); + if (_value != NULL) { + len += strlen(_value); + } + sp = AllocateHeap(len+2, mtArguments); + assert(sp != NULL, "Unable to allocate space for new append path value"); + if (sp != NULL) { + if (_value != NULL) { + strcpy(sp, _value); + strcat(sp, os::path_separator()); + strcat(sp, value); + FreeHeap(_value); + } else { + strcpy(sp, value); + } + _value = sp; + } + } +} + +PathString::PathString(const char* value) { + if (value == NULL) { + _value = NULL; + } else { + _value = AllocateHeap(strlen(value)+1, mtArguments); + strcpy(_value, value); + } +} + +PathString::~PathString() { + if (_value != NULL) { + FreeHeap(_value); + _value = NULL; + } +} + +ModulePatchPath::ModulePatchPath(const char* module_name, const char* path) { + assert(module_name != NULL && path != NULL, "Invalid module name or path value"); + size_t len = strlen(module_name) + 1; + _module_name = AllocateHeap(len, mtInternal); + strncpy(_module_name, module_name, len); // copy the trailing null + _path = new PathString(path); +} + +ModulePatchPath::~ModulePatchPath() { + if (_module_name != NULL) { + FreeHeap(_module_name); + _module_name = NULL; + } + if (_path != NULL) { + delete _path; + _path = NULL; + } +} + +SystemProperty::SystemProperty(const char* key, const char* value, bool writeable, bool internal) : PathString(value) { + if (key == NULL) { + _key = NULL; + } else { + _key = AllocateHeap(strlen(key)+1, mtArguments); + strcpy(_key, key); + } + _next = NULL; + _internal = internal; + _writeable = writeable; +} + +AgentLibrary::AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) { + _name = AllocateHeap(strlen(name)+1, mtArguments); + strcpy(_name, name); + if (options == NULL) { + _options = NULL; + } else { + _options = AllocateHeap(strlen(options)+1, mtArguments); + strcpy(_options, options); + } + _is_absolute_path = is_absolute_path; + _os_lib = os_lib; + _next = NULL; + _state = agent_invalid; + _is_static_lib = false; +} + // Check if head of 'option' matches 'name', and sets 'tail' to the remaining // part of the option string. static bool match_option(const JavaVMOption *option, const char* name, @@ -180,6 +282,23 @@ bool needs_module_property_warning = false; #define UPGRADE_PATH "upgrade.path" #define UPGRADE_PATH_LEN 12 +void Arguments::add_init_library(const char* name, char* options) { + _libraryList.add(new AgentLibrary(name, options, false, NULL)); +} + +void Arguments::add_init_agent(const char* name, char* options, bool absolute_path) { + _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); +} + +// Late-binding agents not started via arguments +void Arguments::add_loaded_agent(AgentLibrary *agentLib) { + _agentList.add(agentLib); +} + +void Arguments::add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib) { + _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); +} + // Return TRUE if option matches 'property', or 'property=', or 'property.'. static bool matches_property_suffix(const char* option, const char* property, size_t len) { return ((strncmp(option, property, len) == 0) && diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index c3105c39263..222f4b25ad6 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -27,7 +27,7 @@ #include "logging/logLevel.hpp" #include "logging/logTag.hpp" -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" #include "runtime/perfData.hpp" @@ -60,60 +60,11 @@ class PathString : public CHeapObj<mtArguments> { public: char* value() const { return _value; } - bool set_value(const char *value) { - if (_value != NULL) { - FreeHeap(_value); - } - _value = AllocateHeap(strlen(value)+1, mtArguments); - assert(_value != NULL, "Unable to allocate space for new path value"); - if (_value != NULL) { - strcpy(_value, value); - } else { - // not able to allocate - return false; - } - return true; - } + bool set_value(const char *value); + void append_value(const char *value); - void append_value(const char *value) { - char *sp; - size_t len = 0; - if (value != NULL) { - len = strlen(value); - if (_value != NULL) { - len += strlen(_value); - } - sp = AllocateHeap(len+2, mtArguments); - assert(sp != NULL, "Unable to allocate space for new append path value"); - if (sp != NULL) { - if (_value != NULL) { - strcpy(sp, _value); - strcat(sp, os::path_separator()); - strcat(sp, value); - FreeHeap(_value); - } else { - strcpy(sp, value); - } - _value = sp; - } - } - } - - PathString(const char* value) { - if (value == NULL) { - _value = NULL; - } else { - _value = AllocateHeap(strlen(value)+1, mtArguments); - strcpy(_value, value); - } - } - - ~PathString() { - if (_value != NULL) { - FreeHeap(_value); - _value = NULL; - } - } + PathString(const char* value); + ~PathString(); }; // ModulePatchPath records the module/path pair as specified to --patch-module. @@ -122,24 +73,8 @@ private: char* _module_name; PathString* _path; public: - ModulePatchPath(const char* module_name, const char* path) { - assert(module_name != NULL && path != NULL, "Invalid module name or path value"); - size_t len = strlen(module_name) + 1; - _module_name = AllocateHeap(len, mtInternal); - strncpy(_module_name, module_name, len); // copy the trailing null - _path = new PathString(path); - } - - ~ModulePatchPath() { - if (_module_name != NULL) { - FreeHeap(_module_name); - _module_name = NULL; - } - if (_path != NULL) { - delete _path; - _path = NULL; - } - } + ModulePatchPath(const char* module_name, const char* path); + ~ModulePatchPath(); inline void set_path(const char* path) { _path->set_value(path); } inline const char* module_name() const { return _module_name; } @@ -186,17 +121,7 @@ class SystemProperty : public PathString { } // Constructor - SystemProperty(const char* key, const char* value, bool writeable, bool internal = false) : PathString(value) { - if (key == NULL) { - _key = NULL; - } else { - _key = AllocateHeap(strlen(key)+1, mtArguments); - strcpy(_key, key); - } - _next = NULL; - _internal = internal; - _writeable = writeable; - } + SystemProperty(const char* key, const char* value, bool writeable, bool internal = false); }; @@ -235,21 +160,7 @@ public: void set_invalid() { _state = agent_invalid; } // Constructor - AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) { - _name = AllocateHeap(strlen(name)+1, mtArguments); - strcpy(_name, name); - if (options == NULL) { - _options = NULL; - } else { - _options = AllocateHeap(strlen(options)+1, mtArguments); - strcpy(_options, options); - } - _is_absolute_path = is_absolute_path; - _os_lib = os_lib; - _next = NULL; - _state = agent_invalid; - _is_static_lib = false; - } + AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib); }; // maintain an order of entry list of AgentLibrary @@ -421,19 +332,15 @@ class Arguments : AllStatic { // -Xrun arguments static AgentLibraryList _libraryList; - static void add_init_library(const char* name, char* options) - { _libraryList.add(new AgentLibrary(name, options, false, NULL)); } + static void add_init_library(const char* name, char* options); // -agentlib and -agentpath arguments static AgentLibraryList _agentList; - static void add_init_agent(const char* name, char* options, bool absolute_path) - { _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); } + static void add_init_agent(const char* name, char* options, bool absolute_path); // Late-binding agents not started via arguments - static void add_loaded_agent(AgentLibrary *agentLib) - { _agentList.add(agentLib); } - static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib) - { _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); } + static void add_loaded_agent(AgentLibrary *agentLib); + static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib); // Operation modi static Mode _mode; diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 40037a496a5..ca5a22129f3 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/vmSymbols.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/markOop.hpp" #include "oops/oop.inline.hpp" @@ -242,6 +243,19 @@ static volatile int InitDone = 0; // * See also http://blogs.sun.com/dave +void* ObjectMonitor::operator new (size_t size) throw() { + return AllocateHeap(size, mtInternal); +} +void* ObjectMonitor::operator new[] (size_t size) throw() { + return operator new (size); +} +void ObjectMonitor::operator delete(void* p) { + FreeHeap(p); +} +void ObjectMonitor::operator delete[] (void *p) { + operator delete(p); +} + // ----------------------------------------------------------------------------- // Enter support diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index e7744cc5962..ee610b1a4db 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -25,7 +25,7 @@ #ifndef SHARE_VM_RUNTIME_OBJECTMONITOR_HPP #define SHARE_VM_RUNTIME_OBJECTMONITOR_HPP -#include "memory/allocation.inline.hpp" +#include "memory/allocation.hpp" #include "memory/padded.hpp" #include "runtime/os.hpp" #include "runtime/park.hpp" @@ -213,18 +213,10 @@ class ObjectMonitor { static int Knob_VerifyMatch; static int Knob_SpinLimit; - void* operator new (size_t size) throw() { - return AllocateHeap(size, mtInternal); - } - void* operator new[] (size_t size) throw() { - return operator new (size); - } - void operator delete(void* p) { - FreeHeap(p); - } - void operator delete[] (void *p) { - operator delete(p); - } + void* operator new (size_t size) throw(); + void* operator new[] (size_t size) throw(); + void operator delete(void* p); + void operator delete[] (void *p); // TODO-FIXME: the "offset" routines should return a type of off_t instead of int ... // ByteSize would also be an appropriate type. diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp index 6f278dff23a..6314b54bd64 100644 --- a/src/hotspot/share/runtime/park.cpp +++ b/src/hotspot/share/runtime/park.cpp @@ -23,10 +23,9 @@ */ #include "precompiled.hpp" +#include "memory/allocation.inline.hpp" #include "runtime/thread.hpp" - - // Lifecycle management for TSM ParkEvents. // ParkEvents are type-stable (TSM). // In our particular implementation they happen to be immortal. diff --git a/src/hotspot/share/services/diagnosticArgument.cpp b/src/hotspot/share/services/diagnosticArgument.cpp index 691bd797dd1..4456260f29b 100644 --- a/src/hotspot/share/services/diagnosticArgument.cpp +++ b/src/hotspot/share/services/diagnosticArgument.cpp @@ -29,6 +29,29 @@ #include "runtime/thread.hpp" #include "services/diagnosticArgument.hpp" +StringArrayArgument::StringArrayArgument() { + _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true); + assert(_array != NULL, "Sanity check"); +} + +StringArrayArgument::~StringArrayArgument() { + for (int i=0; i<_array->length(); i++) { + if(_array->at(i) != NULL) { // Safety check + FREE_C_HEAP_ARRAY(char, _array->at(i)); + } + } + delete _array; +} + +void StringArrayArgument::add(const char* str, size_t len) { + if (str != NULL) { + char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); + strncpy(ptr, str, len); + ptr[len] = 0; + _array->append(ptr); + } +} + void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) { /* NOTE:Some argument types doesn't require a value, * for instance boolean arguments: "enableFeatureX". is diff --git a/src/hotspot/share/services/diagnosticArgument.hpp b/src/hotspot/share/services/diagnosticArgument.hpp index d276d3b6294..654650ccd68 100644 --- a/src/hotspot/share/services/diagnosticArgument.hpp +++ b/src/hotspot/share/services/diagnosticArgument.hpp @@ -35,29 +35,14 @@ class StringArrayArgument : public CHeapObj<mtInternal> { private: GrowableArray<char*>* _array; public: - StringArrayArgument() { - _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true); - assert(_array != NULL, "Sanity check"); - } - void add(const char* str, size_t len) { - if (str != NULL) { - char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); - strncpy(ptr, str, len); - ptr[len] = 0; - _array->append(ptr); - } - } + StringArrayArgument(); + ~StringArrayArgument(); + + void add(const char* str, size_t len); + GrowableArray<char*>* array() { return _array; } - ~StringArrayArgument() { - for (int i=0; i<_array->length(); i++) { - if(_array->at(i) != NULL) { // Safety check - FREE_C_HEAP_ARRAY(char, _array->at(i)); - } - } - delete _array; - } }; class NanoTimeArgument { diff --git a/src/hotspot/share/utilities/decoder.cpp b/src/hotspot/share/utilities/decoder.cpp index 61a274c68fb..38ba9decb6e 100644 --- a/src/hotspot/share/utilities/decoder.cpp +++ b/src/hotspot/share/utilities/decoder.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jvm.h" +#include "memory/allocation.inline.hpp" #include "runtime/os.hpp" #include "utilities/decoder.hpp" #include "utilities/vmError.hpp" diff --git a/src/hotspot/share/utilities/decoder_elf.cpp b/src/hotspot/share/utilities/decoder_elf.cpp index cd438dc1c49..c8639c2b972 100644 --- a/src/hotspot/share/utilities/decoder_elf.cpp +++ b/src/hotspot/share/utilities/decoder_elf.cpp @@ -26,6 +26,7 @@ #if !defined(_WINDOWS) && !defined(__APPLE__) #include "decoder_elf.hpp" +#include "memory/allocation.inline.hpp" ElfDecoder::~ElfDecoder() { if (_opened_elf_files != NULL) { diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp index d6dc256a2c7..076529897c6 100644 --- a/src/hotspot/share/utilities/growableArray.cpp +++ b/src/hotspot/share/utilities/growableArray.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/thread.inline.hpp" #include "utilities/growableArray.hpp" @@ -56,3 +57,7 @@ void* GenericGrowableArray::raw_allocate(int elementSize) { return _arena->Amalloc(byte_size); } } + +void GenericGrowableArray::free_C_heap(void* elements) { + FreeHeap(elements); +} diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index f65f45cede7..4c5a4914db7 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -26,9 +26,9 @@ #define SHARE_VM_UTILITIES_GROWABLEARRAY_HPP #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" // A growable array. @@ -144,6 +144,8 @@ class GenericGrowableArray : public ResourceObj { assert(on_stack(), "fast ResourceObj path only"); return (void*)resource_allocate_bytes(thread, elementSize * _max); } + + void free_C_heap(void* elements); }; template<class E> class GrowableArrayIterator; @@ -451,7 +453,7 @@ template<class E> void GrowableArray<E>::grow(int j) { for ( ; i < _max; i++) ::new ((void*)&newData[i]) E(); for (i = 0; i < old_max; i++) _data[i].~E(); if (on_C_heap() && _data != NULL) { - FreeHeap(_data); + free_C_heap(_data); } _data = newData; } @@ -475,7 +477,7 @@ template<class E> void GrowableArray<E>::clear_and_deallocate() { clear(); if (_data != NULL) { for (int i = 0; i < _max; i++) _data[i].~E(); - FreeHeap(_data); + free_C_heap(_data); _data = NULL; } } diff --git a/src/hotspot/share/utilities/stack.hpp b/src/hotspot/share/utilities/stack.hpp index 7c866897691..b606bce533a 100644 --- a/src/hotspot/share/utilities/stack.hpp +++ b/src/hotspot/share/utilities/stack.hpp @@ -26,7 +26,6 @@ #define SHARE_VM_UTILITIES_STACK_HPP #include "memory/allocation.hpp" -#include "memory/allocation.inline.hpp" // Class Stack (below) grows and shrinks by linking together "segments" which // are allocated on demand. Segments are arrays of the element type (E) plus an diff --git a/test/hotspot/gtest/logging/test_logMessageTest.cpp b/test/hotspot/gtest/logging/test_logMessageTest.cpp index b3c736f34fa..5540b6c5424 100644 --- a/test/hotspot/gtest/logging/test_logMessageTest.cpp +++ b/test/hotspot/gtest/logging/test_logMessageTest.cpp @@ -27,6 +27,7 @@ #include "logTestUtils.inline.hpp" #include "logging/log.hpp" #include "logging/logMessage.hpp" +#include "memory/allocation.inline.hpp" #include "unittest.hpp" #include "utilities/globalDefinitions.hpp" From 678aafa42cae9f52cd627baff7a249066ed93a83 Mon Sep 17 00:00:00 2001 From: Kim Barrett <kbarrett@openjdk.org> Date: Tue, 28 Nov 2017 15:52:32 -0500 Subject: [PATCH 082/165] 8191870: Remove badJNIHandle Reviewed-by: coleenp, eosterlund, dcubed --- src/hotspot/share/runtime/globals.hpp | 3 --- src/hotspot/share/runtime/jniHandles.cpp | 14 ++++++++------ src/hotspot/share/runtime/jniHandles.hpp | 5 ++--- src/hotspot/share/runtime/sharedRuntime.cpp | 2 +- src/hotspot/share/utilities/globalDefinitions.hpp | 2 -- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 405a5652165..51d1f04c092 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -917,9 +917,6 @@ public: notproduct(bool, ZapVMHandleArea, trueInDebug, \ "Zap freed VM handle space with 0xBCBCBCBC") \ \ - develop(bool, ZapJNIHandleArea, trueInDebug, \ - "Zap freed JNI handle space with 0xFEFEFEFE") \ - \ notproduct(bool, ZapStackSegments, trueInDebug, \ "Zap allocated/freed stack segments with 0xFADFADED") \ \ diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp index 8819f3c2cfc..483f9a5cb6e 100644 --- a/src/hotspot/share/runtime/jniHandles.cpp +++ b/src/hotspot/share/runtime/jniHandles.cpp @@ -279,13 +279,15 @@ JNIHandleBlock* JNIHandleBlock::_block_list = NULL; #endif +#ifdef ASSERT void JNIHandleBlock::zap() { // Zap block values _top = 0; for (int index = 0; index < block_size_in_oops; index++) { - _handles[index] = badJNIHandle; + _handles[index] = NULL; } } +#endif // ASSERT JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread) { assert(thread == NULL || thread == Thread::current(), "sanity check"); @@ -307,7 +309,7 @@ JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread) { // Allocate new block block = new JNIHandleBlock(); _blocks_allocated++; - if (ZapJNIHandleArea) block->zap(); + block->zap(); #ifndef PRODUCT // Link new block to list of all allocated blocks block->_block_list_link = _block_list; @@ -339,7 +341,7 @@ void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) { // we _don't_ want the block to be kept on the free_handle_block. // See for instance JavaThread::exit(). if (thread != NULL ) { - if (ZapJNIHandleArea) block->zap(); + block->zap(); JNIHandleBlock* freelist = thread->free_handle_block(); block->_pop_frame_link = NULL; thread->set_free_handle_block(block); @@ -360,7 +362,7 @@ void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) { MutexLockerEx ml(JNIHandleBlockFreeList_lock, Mutex::_no_safepoint_check_flag); while (block != NULL) { - if (ZapJNIHandleArea) block->zap(); + block->zap(); JNIHandleBlock* next = block->_next; block->_next = _block_free_list; _block_free_list = block; @@ -453,13 +455,13 @@ jobject JNIHandleBlock::allocate_handle(oop obj) { break; } current->_top = 0; - if (ZapJNIHandleArea) current->zap(); + current->zap(); } // Clear initial block _free_list = NULL; _allocate_before_rebuild = 0; _last = this; - if (ZapJNIHandleArea) zap(); + zap(); } // Try last block diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp index b7cd7668549..8bba3e3125b 100644 --- a/src/hotspot/share/runtime/jniHandles.hpp +++ b/src/hotspot/share/runtime/jniHandles.hpp @@ -148,7 +148,7 @@ class JNIHandleBlock : public CHeapObj<mtInternal> { static int _blocks_allocated; // For debugging/printing // Fill block with bad_handle values - void zap(); + void zap() NOT_DEBUG_RETURN; // Free list computation void rebuild_free_list(); @@ -219,9 +219,8 @@ inline oop& JNIHandles::jweak_ref(jobject handle) { template<bool external_guard> inline oop JNIHandles::guard_value(oop value) { if (!external_guard) { - assert(value != badJNIHandle, "Pointing to zapped jni handle area"); assert(value != deleted_handle(), "Used a deleted global handle"); - } else if ((value == badJNIHandle) || (value == deleted_handle())) { + } else if (value == deleted_handle()) { value = NULL; } return value; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 8e4c90f9199..abb070dffd7 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -970,7 +970,7 @@ JNI_ENTRY(void*, throw_unsatisfied_link_error(JNIEnv* env, ...)) { // We return a bad value here to make sure that the exception is // forwarded before we look at the return value. - THROW_(vmSymbols::java_lang_UnsatisfiedLinkError(), (void*)badJNIHandle); + THROW_(vmSymbols::java_lang_UnsatisfiedLinkError(), (void*)badAddress); } JNI_END diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 33f9962da15..e98ec391144 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -951,7 +951,6 @@ const int badResourceValue = 0xAB; // value used to zap const int freeBlockPad = 0xBA; // value used to pad freed blocks. const int uninitBlockPad = 0xF1; // value used to zap newly malloc'd blocks. const juint uninitMetaWordVal= 0xf7f7f7f7; // value used to zap newly allocated metachunk -const intptr_t badJNIHandleVal = (intptr_t) UCONST64(0xFEFEFEFEFEFEFEFE); // value used to zap jni handle area const juint badHeapWordVal = 0xBAADBABE; // value used to zap heap after GC const juint badMetaWordVal = 0xBAADFADE; // value used to zap metadata heap after GC const int badCodeHeapNewVal= 0xCC; // value used to zap Code heap at allocation @@ -963,7 +962,6 @@ const int badCodeHeapFreeVal = 0xDD; // value used to zap #define badAddress ((address)::badAddressVal) #define badOop (cast_to_oop(::badOopVal)) #define badHeapWord (::badHeapWordVal) -#define badJNIHandle (cast_to_oop(::badJNIHandleVal)) // Default TaskQueue size is 16K (32-bit) or 128K (64-bit) #define TASKQUEUE_SIZE (NOT_LP64(1<<14) LP64_ONLY(1<<17)) From b25c9ddbaee914777a4d850bfaea4e14148c6ddc Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga <ysuenaga@openjdk.org> Date: Wed, 29 Nov 2017 09:26:58 +0900 Subject: [PATCH 083/165] 8165736: Error message should be shown when JVMTI agent cannot be attached Reviewed-by: sspitsyn, dholmes --- make/test/JtregNativeHotspot.gmk | 3 + src/hotspot/share/prims/jvmtiExport.cpp | 11 +++- .../tools/attach/HotSpotVirtualMachine.java | 19 +++--- .../AttachFailed/AttachFailedTestBase.java | 59 +++++++++++++++++++ .../AttachFailed/AttachIncorrectLibrary.java | 43 ++++++++++++++ .../jvmti/AttachFailed/AttachNoEntry.java | 45 ++++++++++++++ .../jvmti/AttachFailed/AttachReturnError.java | 45 ++++++++++++++ .../jvmti/AttachFailed/libHasNoEntryPoint.c | 26 ++++++++ .../dcmd/jvmti/AttachFailed/libReturnError.c | 30 ++++++++++ .../tools/attach/StartManagementAgent.java | 4 +- 10 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c create mode 100644 test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 9c621f53410..8cf3b0ed6f3 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -79,6 +79,7 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC += \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/ModuleAwareAgents/ClassLoadPrepare \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/ModuleAwareAgents/ThreadStart \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions \ + $(TOPDIR)/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed \ # # Add conditional directories here when needed. @@ -110,6 +111,8 @@ ifeq ($(TOOLCHAIN_TYPE), solstudio) BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAllowedFunctions := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libRedefineDoubleDelete := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libHandshakeTransitionTest := -lc + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libHasNoEntryPoint := -lc + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libReturnError := -lc endif ifeq ($(OPENJDK_TARGET_OS), linux) diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 87d0f2bf40a..631ddcdabaa 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -2578,7 +2578,7 @@ extern "C" { jint JvmtiExport::load_agent_library(const char *agent, const char *absParam, const char *options, outputStream* st) { - char ebuf[1024]; + char ebuf[1024] = {0}; char buffer[JVM_MAXPATHLEN]; void* library = NULL; jint result = JNI_ERR; @@ -2628,6 +2628,8 @@ jint JvmtiExport::load_agent_library(const char *agent, const char *absParam, if (!agent_lib->is_static_lib()) { os::dll_unload(library); } + st->print_cr("%s is not available in %s", + on_attach_symbols[0], agent_lib->name()); delete agent_lib; } else { // Invoke the Agent_OnAttach function @@ -2654,9 +2656,14 @@ jint JvmtiExport::load_agent_library(const char *agent, const char *absParam, } // Agent_OnAttach executed so completion status is JNI_OK - st->print_cr("%d", result); + st->print_cr("return code: %d", result); result = JNI_OK; } + } else { + st->print_cr("%s was not loaded.", agent); + if (*ebuf != '\0') { + st->print_cr("%s", ebuf); + } } return result; } diff --git a/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java b/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java index 9ab9357ad05..a475bf42a38 100644 --- a/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java +++ b/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java @@ -86,18 +86,23 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine { private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options) throws AgentLoadException, AgentInitializationException, IOException { + String msgPrefix = "return code: "; InputStream in = execute("load", agentLibrary, isAbsolute ? "true" : "false", options); - try { - int result = readInt(in); - if (result != 0) { - throw new AgentInitializationException("Agent_OnAttach failed", result); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { + String result = reader.readLine(); + if (result == null) { + throw new AgentLoadException("Target VM did not respond"); + } else if (result.startsWith(msgPrefix)) { + int retCode = Integer.parseInt(result.substring(msgPrefix.length())); + if (retCode != 0) { + throw new AgentInitializationException("Agent_OnAttach failed", retCode); + } + } else { + throw new AgentLoadException(result); } - } finally { - in.close(); - } } diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java new file mode 100644 index 00000000000..017c7654cfd --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 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. + */ +import java.nio.file.Paths; +import jdk.test.lib.dcmd.*; +import jdk.test.lib.Platform; +import org.testng.annotations.Test; + +public abstract class AttachFailedTestBase { + + public abstract void run(CommandExecutor executor); + + /** + * Build path to shared object according to platform rules + */ + public static String getSharedObjectPath(String name) { + String libname; + if (Platform.isWindows()) { + libname = name + ".dll"; + } else if (Platform.isOSX()) { + libname = "lib" + name + ".dylib"; + } else { + libname = "lib" + name + ".so"; + } + + return Paths.get(System.getProperty("test.nativepath"), libname) + .toAbsolutePath() + .toString(); + } + + @Test + public void jmx() throws Throwable { + run(new JMXExecutor()); + } + + @Test + public void cli() throws Throwable { + run(new PidJcmdExecutor()); + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java new file mode 100644 index 00000000000..bc83dece0f2 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017, 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. + */ +import jdk.test.lib.dcmd.*; +import jdk.test.lib.process.OutputAnalyzer; + +/* + * @test + * @bug 8165736 + * @library /test/lib + * @run testng AttachIncorrectLibrary + */ +public class AttachIncorrectLibrary extends AttachFailedTestBase { + @Override + public void run(CommandExecutor executor) { + try { + OutputAnalyzer output = executor.execute("JVMTI.agent_load " + + getSharedObjectPath("SilverBullet")); + output.shouldContain(" was not loaded"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java new file mode 100644 index 00000000000..76268f012eb --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 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. + */ +import jdk.test.lib.dcmd.*; +import jdk.test.lib.process.OutputAnalyzer; + +/* + * @test + * @bug 8165736 + * @library /test/lib + * @run testng AttachNoEntry + */ +public class AttachNoEntry extends AttachFailedTestBase { + @Override + public void run(CommandExecutor executor) { + try { + String libpath = getSharedObjectPath("HasNoEntryPoint"); + OutputAnalyzer output = null; + + output = executor.execute("JVMTI.agent_load " + libpath); + output.shouldContain("Agent_OnAttach is not available"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java new file mode 100644 index 00000000000..ad1772de1a6 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 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. + */ +import jdk.test.lib.dcmd.*; +import jdk.test.lib.process.OutputAnalyzer; + +/* + * @test + * @bug 8165736 + * @library /test/lib + * @run testng AttachReturnError + */ +public class AttachReturnError extends AttachFailedTestBase { + @Override + public void run(CommandExecutor executor) { + try { + String libpath = getSharedObjectPath("ReturnError"); + OutputAnalyzer output = null; + + output = executor.execute("JVMTI.agent_load " + libpath); + output.shouldContain("return code: -1"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c new file mode 100644 index 00000000000..1a945c6ccce --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017, 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. + */ + +extern int dummy() { + return 0; +} diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c new file mode 100644 index 00000000000..ce9eb216882 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, 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. + */ + +#include <jni.h> +#include <jvmti.h> + +JNIEXPORT +jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { + return JNI_ERR; +} diff --git a/test/jdk/com/sun/tools/attach/StartManagementAgent.java b/test/jdk/com/sun/tools/attach/StartManagementAgent.java index 1a5234b3408..bd996ea2c63 100644 --- a/test/jdk/com/sun/tools/attach/StartManagementAgent.java +++ b/test/jdk/com/sun/tools/attach/StartManagementAgent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -99,7 +99,7 @@ public class StartManagementAgent { } catch(AttachOperationFailedException ex) { // We expect parsing of "apa" above to fail, but if the file path // can't be read we get a different exception message - if (!ex.getMessage().contains("Invalid com.sun.management.jmxremote.port number")) { + if (!ex.getMessage().contains("NumberFormatException: For input string: \"apa\"")) { throw ex; } ex.printStackTrace(System.err); From c5df44a4ce8b6dfa971d25355d9d5293ea6f40de Mon Sep 17 00:00:00 2001 From: Dmitry Chuyko <dchuyko@openjdk.org> Date: Wed, 29 Nov 2017 14:34:19 +0300 Subject: [PATCH 084/165] 8188221: Return type profiling is not performed from aarch64 interpreter Reviewed-by: drwhite --- .../cpu/aarch64/templateInterpreterGenerator_aarch64.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index f9e12bd73a3..b8ac5ecbcd7 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -414,6 +414,14 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, __ restore_constant_pool_cache(); __ get_method(rmethod); + if (state == atos) { + Register obj = r0; + Register mdp = r1; + Register tmp = r2; + __ ldr(mdp, Address(rmethod, Method::method_data_offset())); + __ profile_return_type(mdp, obj, tmp); + } + // Pop N words from the stack __ get_cache_and_index_at_bcp(r1, r2, 1, index_size); __ ldr(r1, Address(r1, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); From 3faa620f4c078e24f0be11744213c1aebe5e4df2 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov <kvn@openjdk.org> Date: Wed, 29 Nov 2017 10:30:51 -0800 Subject: [PATCH 085/165] 8184361: AOT lib at jdk/lib/libjava.base-coop.so seems to override -XX:AOTLibrary= Load libraries specified by AOTLibrary. Don't load a library if an other library with the same name is already loaded. Reviewed-by: dlong --- src/hotspot/share/aot/aotLoader.cpp | 34 +++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/aot/aotLoader.cpp b/src/hotspot/share/aot/aotLoader.cpp index c91c04b9b1b..9b77338e43d 100644 --- a/src/hotspot/share/aot/aotLoader.cpp +++ b/src/hotspot/share/aot/aotLoader.cpp @@ -146,15 +146,6 @@ void AOTLoader::initialize() { return; } - const char* home = Arguments::get_java_home(); - const char* file_separator = os::file_separator(); - - for (int i = 0; i < (int) (sizeof(modules) / sizeof(const char*)); i++) { - char library[JVM_MAXPATHLEN]; - jio_snprintf(library, sizeof(library), "%s%slib%slib%s%s%s%s", home, file_separator, file_separator, modules[i], UseCompressedOops ? "-coop" : "", UseG1GC ? "" : "-nong1", os::dll_file_extension()); - load_library(library, false); - } - // Scan the AOTLibrary option. if (AOTLibrary != NULL) { const int len = (int)strlen(AOTLibrary); @@ -172,6 +163,16 @@ void AOTLoader::initialize() { } } } + + // Load well-know AOT libraries from Java installation directory. + const char* home = Arguments::get_java_home(); + const char* file_separator = os::file_separator(); + + for (int i = 0; i < (int) (sizeof(modules) / sizeof(const char*)); i++) { + char library[JVM_MAXPATHLEN]; + jio_snprintf(library, sizeof(library), "%s%slib%slib%s%s%s%s", home, file_separator, file_separator, modules[i], UseCompressedOops ? "-coop" : "", UseG1GC ? "" : "-nong1", os::dll_file_extension()); + load_library(library, false); + } } } @@ -239,6 +240,21 @@ void AOTLoader::set_narrow_klass_shift() { } void AOTLoader::load_library(const char* name, bool exit_on_error) { + // Skip library if a library with the same name is already loaded. + const int file_separator = *os::file_separator(); + const char* start = strrchr(name, file_separator); + const char* new_name = (start == NULL) ? name : (start + 1); + FOR_ALL_AOT_LIBRARIES(lib) { + const char* lib_name = (*lib)->name(); + start = strrchr(lib_name, file_separator); + const char* old_name = (start == NULL) ? lib_name : (start + 1); + if (strcmp(old_name, new_name) == 0) { + if (PrintAOT) { + warning("AOT library %s is already loaded as %s.", name, lib_name); + } + return; + } + } char ebuf[1024]; void* handle = os::dll_load(name, ebuf, sizeof ebuf); if (handle == NULL) { From f684b1a856e59361598fe3b877212edbb485255a Mon Sep 17 00:00:00 2001 From: Sharath Ballal <sballal@openjdk.org> Date: Thu, 30 Nov 2017 14:58:41 +0530 Subject: [PATCH 086/165] 8184982: SA: Running ClassDump on a simple java program generates NullPointerException Reviewed-by: sundar, jgeorge --- .../tools/jcore/PackageNameFilter.java | 8 +- .../serviceability/sa/TestClassDump.java | 104 ++++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/TestClassDump.java diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java index 1701c11b02d..e0312ff5a4d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -50,10 +50,14 @@ public class PackageNameFilter implements ClassFilter } public boolean canInclude(InstanceKlass kls) { - String klassName = kls.getName().asString().replace('/', '.'); + if (pkgList == null) { + // Dump everything + return true; + } final int len = pkgList.length; if (len == 0) return true; + String klassName = kls.getName().asString().replace('/', '.'); for (int i=0; i < len; i++) if (klassName.startsWith((String) pkgList[i] )) return true; return false; diff --git a/test/hotspot/jtreg/serviceability/sa/TestClassDump.java b/test/hotspot/jtreg/serviceability/sa/TestClassDump.java new file mode 100644 index 00000000000..cdaa3de6c64 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestClassDump.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8184982 + * @summary Test ClassDump tool + * @library /test/lib + * @run main/othervm TestClassDump + */ + +public class TestClassDump { + + private static void dumpClass(long lingeredAppPid) + throws IOException { + + ProcessBuilder pb; + OutputAnalyzer output; + + pb = ProcessTools.createJavaProcessBuilder( + "-Dsun.jvm.hotspot.tools.jcore.outputDir=jtreg_classes", + "-m", "jdk.hotspot.agent/sun.jvm.hotspot.tools.jcore.ClassDump", String.valueOf(lingeredAppPid)); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + if (!Files.isDirectory(Paths.get("jtreg_classes"))) { + throw new RuntimeException("jtreg_classes directory not found"); + } + if (Files.notExists(Paths.get("jtreg_classes", "java", "lang", "Integer.class"))) { + throw new RuntimeException("jtreg_classes/java/lang/Integer.class not found"); + } + if (Files.notExists(Paths.get("jtreg_classes", "jdk", "test", "lib", "apps", "LingeredApp.class"))) { + throw new RuntimeException("jtreg_classes/jdk/test/lib/apps/LingeredApp.class not found"); + } + if (Files.notExists(Paths.get("jtreg_classes", "sun", "net", "util", "URLUtil.class"))) { + throw new RuntimeException("jtreg_classes/sun/net/util/URLUtil.class not found"); + } + + pb = ProcessTools.createJavaProcessBuilder( + "-Dsun.jvm.hotspot.tools.jcore.outputDir=jtreg_classes2", + "-Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=jdk,sun", + "-m", "jdk.hotspot.agent/sun.jvm.hotspot.tools.jcore.ClassDump", String.valueOf(lingeredAppPid)); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + if (Files.exists(Paths.get("jtreg_classes2", "java", "math", "BigInteger.class"))) { + throw new RuntimeException("jtreg_classes2/java/math/BigInteger.class not expected"); + } + if (Files.notExists(Paths.get("jtreg_classes2", "sun", "util", "calendar", "BaseCalendar.class"))) { + throw new RuntimeException("jtreg_classes2/sun/util/calendar/BaseCalendar.class not found"); + } + if (Files.notExists(Paths.get("jtreg_classes2", "jdk", "internal", "vm", "PostVMInitHook.class"))) { + throw new RuntimeException("jtreg_classes2/jdk/internal/vm/PostVMInitHook.class not found"); + } + } + + public static void main(String[] args) throws Exception { + if (!Platform.shouldSAAttach()) { + // Silently skip the test if we don't have enough permissions to attach + System.out.println("SA attach not expected to work - test skipped."); + return; + } + + LingeredApp theApp = null; + try { + theApp = LingeredApp.startApp(); + long pid = theApp.getPid(); + System.out.println("Started LingeredApp with pid " + pid); + dumpClass(pid); + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} From 937799a84c67e809d8645362b2682104b0696dba Mon Sep 17 00:00:00 2001 From: Stefan Johansson <sjohanss@openjdk.org> Date: Thu, 30 Nov 2017 15:05:03 +0100 Subject: [PATCH 087/165] 8192807: testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java fail due to new method in Platform.java Reviewed-by: tschatzl, jwilhelm --- .../TestMutuallyExclusivePlatformPredicates.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java index 9903b188e62..fc563ee2368 100644 --- a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java +++ b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java @@ -51,7 +51,7 @@ public class TestMutuallyExclusivePlatformPredicates { VM_TYPE("isClient", "isServer", "isGraal", "isMinimal", "isZero", "isEmbedded"), MODE("isInt", "isMixed", "isComp"), IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isSlowDebugBuild", - "shouldSAAttach", "isTieredSupported"); + "shouldSAAttach", "isTieredSupported", "areCustomLoadersSupportedForCDS"); public final List<String> methodNames; @@ -106,7 +106,7 @@ public class TestMutuallyExclusivePlatformPredicates { && m.getReturnType() == boolean.class) { Asserts.assertTrue(allMethods.contains(m.getName()), "All Platform's methods with signature '():Z' should " - + "be tested "); + + "be tested. Missing: " + m.getName()); } } } From 0ac9a94c4ddb5f0edf60ec21d1fd27a2b269f00a Mon Sep 17 00:00:00 2001 From: Sharath Ballal <sballal@openjdk.org> Date: Thu, 30 Nov 2017 19:46:20 +0530 Subject: [PATCH 088/165] 8191658: SA: Testcases for attach, detach, reattach and Jhisto commands Reviewed-by: sspitsyn, jgeorge --- .../jtreg/serviceability/sa/ClhsdbAttach.java | 77 +++++++++++++++++++ .../jtreg/serviceability/sa/ClhsdbJhisto.java | 76 ++++++++++++++++++ .../serviceability/sa/ClhsdbLauncher.java | 6 +- 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java new file mode 100644 index 00000000000..32095655640 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.test.lib.apps.LingeredApp; + +/* + * @test + * @bug 8191658 + * @summary Test clhsdb attach, detach, reattach commands + * @library /test/lib + * @run main/othervm ClhsdbAttach + */ + +public class ClhsdbAttach { + + public static void main(String[] args) throws Exception { + System.out.println("Starting ClhsdbAttach test"); + + LingeredApp theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + theApp = LingeredApp.startApp(); + System.out.println("Started LingeredApp with pid " + theApp.getPid()); + String attach = "attach " + theApp.getPid(); + + List<String> cmds = List.of( + "where", + attach, + "flags MaxJavaStackTraceDepth", + "detach", + "universe", + "reattach", + "longConstant markOopDesc::locked_value"); + + Map<String, List<String>> expStrMap = new HashMap<>(); + expStrMap.put("where", List.of( + "Command not valid until attached to a VM")); + expStrMap.put("flags MaxJavaStackTraceDepth", List.of( + "MaxJavaStackTraceDepth = ")); + expStrMap.put("universe", List.of( + "Command not valid until attached to a VM")); + expStrMap.put("longConstant markOopDesc::locked_value", List.of( + "longConstant markOopDesc::locked_value")); + + test.run(-1, cmds, expStrMap, null); + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java new file mode 100644 index 00000000000..76e32a62479 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.Utils; + +/* + * @test + * @bug 8191658 + * @summary Test clhsdb jhisto command + * @library /test/lib + * @run main/othervm ClhsdbJhisto + */ + +public class ClhsdbJhisto { + + public static void main(String[] args) throws Exception { + System.out.println("Starting ClhsdbJhisto test"); + + LingeredAppWithInterface theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + List<String> vmArgs = new ArrayList<String>(); + vmArgs.addAll(Utils.getVmOptions()); + + theApp = new LingeredAppWithInterface(); + LingeredApp.startApp(vmArgs, theApp); + System.out.println("Started LingeredApp with pid " + theApp.getPid()); + + List<String> cmds = List.of("jhisto"); + + Map<String, List<String>> expStrMap = new HashMap<>(); + expStrMap.put("jhisto", List.of( + "java.lang.String", + "java.util.HashMap", + "java.lang.Class", + "java.nio.HeapByteBuffer", + "java.net.URI", + "LingeredAppWithInterface", + "ParselTongue", + "ImmutableCollections$SetN$1")); + + test.run(theApp.getPid(), cmds, expStrMap, null); + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java index 5366be6f826..2d6ccfd3455 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java @@ -53,10 +53,12 @@ public class ClhsdbLauncher { private void attach(long lingeredAppPid) throws IOException { - System.out.println("Starting clhsdb against " + lingeredAppPid); JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); launcher.addToolArg("clhsdb"); - launcher.addToolArg("--pid=" + Long.toString(lingeredAppPid)); + if (lingeredAppPid != -1) { + launcher.addToolArg("--pid=" + Long.toString(lingeredAppPid)); + System.out.println("Starting clhsdb against " + lingeredAppPid); + } ProcessBuilder processBuilder = new ProcessBuilder(launcher.getCommand()); processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); From 0e7f779f1b3524e6c9c1e3992b87cc3b5de8854a Mon Sep 17 00:00:00 2001 From: Bob Vandette <bobv@openjdk.org> Date: Thu, 30 Nov 2017 09:49:45 -0500 Subject: [PATCH 089/165] 8192154: JVM crashes inside some chroot environments on linux Reviewed-by: dholmes, sgehwolf, glaubitz --- src/hotspot/os/linux/osContainer_linux.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 03fd695cbc4..dacd53b27dc 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -323,7 +323,12 @@ void OSContainer::init() { } } - if (mntinfo != NULL) fclose(mntinfo); + fclose(mntinfo); + + if (memory == NULL || cpuset == NULL || cpu == NULL || cpuacct == NULL) { + log_debug(os, container)("Required cgroup subsystems not found"); + return; + } /* * Read /proc/self/cgroup and map host mount point to @@ -383,12 +388,7 @@ void OSContainer::init() { } } - if (cgroup != NULL) fclose(cgroup); - - if (memory == NULL || cpuset == NULL || cpu == NULL) { - log_debug(os, container)("Required cgroup subsystems not found"); - return; - } + fclose(cgroup); // We need to update the amount of physical memory now that // command line arguments have been processed. From b9c69b3401c97e74ffd5dce038d7b92874f6efeb Mon Sep 17 00:00:00 2001 From: Eric Caspole <ecaspole@openjdk.org> Date: Thu, 30 Nov 2017 09:59:27 -0500 Subject: [PATCH 090/165] 8191779: LogCompilation throws java.lang.Error: scope underflow Add the trap into the last call site as the site may not yet be added into scopes. Reviewed-by: kvn, thartmann --- .../sun/hotspot/tools/compiler/LogParser.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java b/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java index 04102c2e468..bd98f02b283 100644 --- a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java +++ b/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java @@ -1025,13 +1025,20 @@ public class LogParser extends DefaultHandler implements ErrorHandler { return; } try { + UncommonTrap unc = new UncommonTrap(Integer.parseInt(search(atts, "bci")), + search(atts, "reason"), + search(atts, "action"), + bytecodes[current_bytecode]); if (scopes.size() == 0) { - reportInternalError("scope underflow"); + // There may be a dangling site not yet in scopes after a late_inline + if (site != null) { + site.add(unc); + } else { + reportInternalError("scope underflow"); + } + } else { + scopes.peek().add(unc); } - scopes.peek().add(new UncommonTrap(Integer.parseInt(search(atts, "bci")), - search(atts, "reason"), - search(atts, "action"), - bytecodes[current_bytecode])); } catch (Error e) { e.printStackTrace(); } From 3ef7804e6d5b56b1655ef2b7613b2ef41caba8f8 Mon Sep 17 00:00:00 2001 From: Robbin Ehn <rehn@openjdk.org> Date: Thu, 30 Nov 2017 16:08:13 +0100 Subject: [PATCH 091/165] 8192072: 8191782 fix for VMDeprecatedOptions.java missed DeferThrSuspendLoopCount and duplicated DeferPollingPageLoopCount Pushed under trivial rules. Reviewed-by: coleenp --- test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java index 2d372ebe2e1..bedcad2e4fe 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java +++ b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java @@ -50,7 +50,7 @@ public class VMDeprecatedOptions { {"FastTLABRefill", "false"}, {"DeferPollingPageLoopCount", "-1"}, {"SafepointSpinBeforeYield", "2000"}, - {"DeferPollingPageLoopCount", "4000"}, + {"DeferThrSuspendLoopCount", "4000"}, // deprecated alias flags (see also aliased_jvm_flags): {"DefaultMaxRAMFraction", "4"}, From 42d3f8d610caed8d12e249c20096428e2fba9d4a Mon Sep 17 00:00:00 2001 From: Jini George <jgeorge@openjdk.org> Date: Thu, 30 Nov 2017 21:24:52 +0530 Subject: [PATCH 092/165] 8191324: SA cleanup -- part 2 Avoid redefinition of hotspot constants and ia64 code vestiges removal Reviewed-by: sspitsyn, dholmes, coleenp --- src/hotspot/share/runtime/perfData.hpp | 1 + src/hotspot/share/runtime/vmStructs.cpp | 22 +++ .../native/libsaproc/BsdDebuggerLocal.c | 13 +- .../classes/sun/jvm/hotspot/HotSpotAgent.java | 9 +- .../debugger/MachineDescriptionIA64.java | 39 ---- .../debugger/bsd/BsdDebuggerLocal.java | 13 +- .../debugger/ia64/IA64ThreadContext.java | 187 ------------------ .../debugger/linux/LinuxDebuggerLocal.java | 13 +- .../linux/LinuxThreadContextFactory.java | 5 +- .../linux/ia64/LinuxIA64ThreadContext.java | 46 ----- .../debugger/windbg/WindbgDebuggerLocal.java | 12 +- .../windbg/ia64/WindbgIA64Thread.java | 103 ---------- .../windbg/ia64/WindbgIA64ThreadContext.java | 46 ----- .../windbg/ia64/WindbgIA64ThreadFactory.java | 44 ----- .../sun/jvm/hotspot/oops/InstanceKlass.java | 60 ++++-- .../hotspot/runtime/ObjectSynchronizer.java | 10 +- .../jvm/hotspot/runtime/PerfDataEntry.java | 59 +++--- .../sun/jvm/hotspot/runtime/VFrame.java | 5 +- .../solaris/native/libsaproc/libproc.h | 6 +- 19 files changed, 115 insertions(+), 578 deletions(-) delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java delete mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java diff --git a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp index 25167bf3930..f667ecc9d03 100644 --- a/src/hotspot/share/runtime/perfData.hpp +++ b/src/hotspot/share/runtime/perfData.hpp @@ -245,6 +245,7 @@ class PerfData : public CHeapObj<mtInternal> { friend class StatSampler; // for access to protected void sample() friend class PerfDataManager; // for access to protected destructor + friend class VMStructs; public: diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index ee435d854f7..e11f75cd0cb 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1534,6 +1534,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_toplevel_type(PerfDataPrologue*) \ declare_toplevel_type(PerfDataEntry) \ declare_toplevel_type(PerfMemory) \ + declare_type(PerfData, CHeapObj<mtInternal>) \ \ /*********************************/ \ /* SymbolTable, SystemDictionary */ \ @@ -2481,6 +2482,12 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_constant(InstanceKlass::inner_class_access_flags_offset) \ declare_constant(InstanceKlass::inner_class_next_offset) \ \ + /*****************************************************/ \ + /* InstanceKlass EnclosingMethodAttributeOffset enum */ \ + /*****************************************************/ \ + \ + declare_constant(InstanceKlass::enclosing_method_attribute_size) \ + \ /*********************************/ \ /* InstanceKlass ClassState enum */ \ /*********************************/ \ @@ -2734,6 +2741,21 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_c2_preprocessor_constant("SAVED_ON_ENTRY_REG_COUNT", SAVED_ON_ENTRY_REG_COUNT) \ declare_c2_preprocessor_constant("C_SAVED_ON_ENTRY_REG_COUNT", C_SAVED_ON_ENTRY_REG_COUNT) \ \ + /************/ \ + /* PerfData */ \ + /************/ \ + \ + /***********************/ \ + /* PerfData Units enum */ \ + /***********************/ \ + \ + declare_constant(PerfData::U_None) \ + declare_constant(PerfData::U_Bytes) \ + declare_constant(PerfData::U_Ticks) \ + declare_constant(PerfData::U_Events) \ + declare_constant(PerfData::U_String) \ + declare_constant(PerfData::U_Hertz) \ + \ /****************/ \ /* JVMCI */ \ /****************/ \ diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c index 85b07f76440..fadf211ac0f 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -298,9 +298,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_ #ifdef i386 #define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG #endif -#ifdef ia64 -#define NPRGREG IA64_REG_COUNT -#endif #ifdef amd64 #define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG #endif @@ -335,14 +332,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_ #endif /* i386 */ -#if ia64 - regs = (*env)->GetLongArrayElements(env, array, &isCopy); - int i; - for (i = 0; i < NPRGREG; i++ ) { - regs[i] = 0xDEADDEAD; - } -#endif /* ia64 */ - #ifdef amd64 #define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java index bb92d20b1c8..33f20eadbb8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -35,7 +35,6 @@ import sun.jvm.hotspot.debugger.MachineDescription; import sun.jvm.hotspot.debugger.MachineDescriptionAMD64; import sun.jvm.hotspot.debugger.MachineDescriptionPPC64; import sun.jvm.hotspot.debugger.MachineDescriptionAArch64; -import sun.jvm.hotspot.debugger.MachineDescriptionIA64; import sun.jvm.hotspot.debugger.MachineDescriptionIntelX86; import sun.jvm.hotspot.debugger.MachineDescriptionSPARC32Bit; import sun.jvm.hotspot.debugger.MachineDescriptionSPARC64Bit; @@ -556,10 +555,8 @@ public class HotSpotAgent { machDesc = new MachineDescriptionIntelX86(); } else if (cpu.equals("amd64")) { machDesc = new MachineDescriptionAMD64(); - } else if (cpu.equals("ia64")) { - machDesc = new MachineDescriptionIA64(); } else { - throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only"); + throw new DebuggerException("Win32 supported under x86 and amd64 only"); } // Note we do not use a cache for the local debugger in server @@ -586,8 +583,6 @@ public class HotSpotAgent { if (cpu.equals("x86")) { machDesc = new MachineDescriptionIntelX86(); - } else if (cpu.equals("ia64")) { - machDesc = new MachineDescriptionIA64(); } else if (cpu.equals("amd64")) { machDesc = new MachineDescriptionAMD64(); } else if (cpu.equals("ppc64")) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java deleted file mode 100644 index a113cbe4447..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2003, 2008, 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. - * - */ - -package sun.jvm.hotspot.debugger; - -public class MachineDescriptionIA64 extends MachineDescriptionTwosComplement implements MachineDescription { - public long getAddressSize() { - return 8; - } - - public boolean isLP64() { - return true; - } - - public boolean isBigEndian() { - return false; - } -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java index 45697a83808..655b450c3fc 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -216,11 +216,7 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { // the UI. This is a cache of 4096 4K pages, or 16 MB. The page // size must be adjusted to be the hardware's page size. // (FIXME: should pick this up from the debugger.) - if (getCPU().equals("ia64")) { - initCache(16384, parseCacheNumPagesProperty(1024)); - } else { - initCache(4096, parseCacheNumPagesProperty(4096)); - } + initCache(4096, parseCacheNumPagesProperty(4096)); } isDarwin = getOS().equals("darwin"); @@ -575,11 +571,6 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { public CDebugger getCDebugger() { if (cdbg == null) { - String cpu = getCPU(); - if (cpu.equals("ia64") ) { - // IA-64 is not supported because of stack-walking issues - return null; - } cdbg = new BsdCDebugger(this); } return cdbg; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java deleted file mode 100644 index 8a8ce383f2e..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2003, 2012, 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. - * - */ - -package sun.jvm.hotspot.debugger.ia64; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.cdbg.*; - -/** Specifies the thread context on ia64 platform; only a sub-portion - of the context is guaranteed to be present on all operating - systems. */ - -public abstract class IA64ThreadContext implements ThreadContext { - // Refer to winnt.h CONTEXT structure - Nov 2001 edition Platform SDK - // only a relevant subset of CONTEXT structure is used here. - // For eg. floating point registers are ignored. - - // NOTE: the indices for the various registers must be maintained as - // listed across various operating systems. However, only a - // subset of the registers' values are guaranteed to be present - - // global registers r0-r31 - public static final int GR0 = 0; - public static final int GR1 = 1; - public static final int GR2 = 2; - public static final int GR3 = 3; - public static final int GR4 = 4; - public static final int GR5 = 5; - public static final int GR6 = 6; - public static final int GR7 = 7; - public static final int GR8 = 8; - public static final int GR9 = 9; - public static final int GR10 = 10; - public static final int GR11 = 11; - public static final int GR12 = 12; - public static final int SP = GR12; - public static final int GR13 = 13; - public static final int GR14 = 14; - public static final int GR15 = 15; - public static final int GR16 = 16; - public static final int GR17 = 17; - public static final int GR18 = 18; - public static final int GR19 = 19; - public static final int GR20 = 20; - public static final int GR21 = 21; - public static final int GR22 = 22; - public static final int GR23 = 23; - public static final int GR24 = 24; - public static final int GR25 = 25; - public static final int GR26 = 26; - public static final int GR27 = 27; - public static final int GR28 = 28; - public static final int GR29 = 29; - public static final int GR30 = 30; - public static final int GR31 = 31; - - // Nat bits for r1-r31 - public static final int INT_NATS = 32; - - // predicates - public static final int PREDS = 33; - - // branch registers - public static final int BR0 = 34; - public static final int BR_RP = BR0; - public static final int BR1 = 35; - public static final int BR2 = 36; - public static final int BR3 = 37; - public static final int BR4 = 38; - public static final int BR5 = 39; - public static final int BR6 = 40; - public static final int BR7 = 41; - - // application registers - public static final int AP_UNAT = 42; // User Nat Collection register - public static final int AP_LC = 43; // Loop counter register - public static final int AP_EC = 43; // Epilog counter register - public static final int AP_CCV = 45; // CMPXCHG value register - public static final int AP_DCR = 46; // Default control register - - // register stack info - public static final int RS_PFS = 47; // Previous function state - public static final int AP_PFS = RS_PFS; - public static final int RS_BSP = 48; // Backing store pointer - public static final int AR_BSP = RS_BSP; - public static final int RS_BSPSTORE = 49; - public static final int AP_BSPSTORE = RS_BSPSTORE; - public static final int RS_RSC = 50; // RSE configuration - public static final int AP_RSC = RS_RSC; - public static final int RS_RNAT = 51; // RSE Nat collection register - public static final int AP_RNAT = RS_RNAT; - - // trap status register - public static final int ST_IPSR = 52; // Interuption Processor Status - public static final int ST_IIP = 53; // Interruption IP - public static final int ST_IFS = 54; // Interruption Function State - - // debug registers - public static final int DB_I0 = 55; - public static final int DB_I1 = 56; - public static final int DB_I2 = 57; - public static final int DB_I3 = 58; - public static final int DB_I4 = 59; - public static final int DB_I5 = 60; - public static final int DB_I6 = 61; - public static final int DB_I7 = 62; - - public static final int DB_D0 = 63; - public static final int DB_D1 = 64; - public static final int DB_D2 = 65; - public static final int DB_D3 = 66; - public static final int DB_D4 = 67; - public static final int DB_D5 = 68; - public static final int DB_D6 = 69; - public static final int DB_D7 = 70; - - public static final int NPRGREG = 71; - - private static final String[] regNames = { - "GR0", "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "GR7", "GR8", - "GR9", "GR10", "GR11", "GR12", "GR13", "GR14", "GR15", "GR16", - "GR17","GR18", "GR19", "GR20", "GR21", "GR22", "GR23", "GR24", - "GR25","GR26", "GR27", "GR28", "GR29", "GR30", "GR31", - "INT_NATS", "PREDS", - "BR0", "BR1", "BR2", "BR3", "BR4", "BR5", "BR6", "BR7", - "AP_UNAT", "AP_LC", "AP_EC", "AP_CCV", "AP_DCR", - "RS_FPS", "RS_BSP", "RS_BSPSTORE", "RS_RSC", "RS_RNAT", - "ST_IPSR", "ST_IIP", "ST_IFS", - "DB_I0", "DB_I1", "DB_I2", "DB_I3", "DB_I4", "DB_I5", "DB_I6", "DB_I7", - "DB_D0", "DB_D1", "DB_D2", "DB_D3", "DB_D4", "DB_D5", "DB_D6", "DB_D7" - }; - - private long[] data; - - public IA64ThreadContext() { - data = new long[NPRGREG]; - } - - public int getNumRegisters() { - return NPRGREG; - } - - public String getRegisterName(int index) { - return regNames[index]; - } - - public void setRegister(int index, long value) { - data[index] = value; - } - - public long getRegister(int index) { - return data[index]; - } - - public CFrame getTopFrame(Debugger dbg) { - return null; - } - - /** This can't be implemented in this class since we would have to - tie the implementation to, for example, the debugging system */ - public abstract void setRegisterAsAddress(int index, Address value); - - /** This can't be implemented in this class since we would have to - tie the implementation to, for example, the debugging system */ - public abstract Address getRegisterAsAddress(int index); -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java index f282a228df6..0d47eb03630 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -210,11 +210,7 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger { // the UI. This is a cache of 4096 4K pages, or 16 MB. The page // size must be adjusted to be the hardware's page size. // (FIXME: should pick this up from the debugger.) - if (getCPU().equals("ia64")) { - initCache(16384, parseCacheNumPagesProperty(1024)); - } else { - initCache(4096, parseCacheNumPagesProperty(4096)); - } + initCache(4096, parseCacheNumPagesProperty(4096)); } workerThread = new LinuxDebuggerLocalWorkerThread(this); @@ -560,11 +556,6 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger { public CDebugger getCDebugger() { if (cdbg == null) { - String cpu = getCPU(); - if (cpu.equals("ia64") ) { - // IA-64 is not supported because of stack-walking issues - return null; - } cdbg = new LinuxCDebugger(this); } return cdbg; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java index 2225558000b..4b786eecc95 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -27,7 +27,6 @@ package sun.jvm.hotspot.debugger.linux; import java.lang.reflect.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.linux.amd64.*; -import sun.jvm.hotspot.debugger.linux.ia64.*; import sun.jvm.hotspot.debugger.linux.x86.*; import sun.jvm.hotspot.debugger.linux.ppc64.*; import sun.jvm.hotspot.debugger.linux.sparc.*; @@ -39,8 +38,6 @@ class LinuxThreadContextFactory { return new LinuxX86ThreadContext(dbg); } else if (cpu.equals("amd64")) { return new LinuxAMD64ThreadContext(dbg); - } else if (cpu.equals("ia64")) { - return new LinuxIA64ThreadContext(dbg); } else if (cpu.equals("sparc")) { return new LinuxSPARCThreadContext(dbg); } else if (cpu.equals("ppc64")) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java deleted file mode 100644 index 749cd92a619..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2003, 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. - * - */ - -package sun.jvm.hotspot.debugger.linux.ia64; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.ia64.*; -import sun.jvm.hotspot.debugger.linux.*; - -public class LinuxIA64ThreadContext extends IA64ThreadContext { - private LinuxDebugger debugger; - - public LinuxIA64ThreadContext(LinuxDebugger debugger) { - super(); - this.debugger = debugger; - } - - public void setRegisterAsAddress(int index, Address value) { - setRegister(index, debugger.getAddressValue(value)); - } - - public Address getRegisterAsAddress(int index) { - return debugger.newAddress(getRegister(index)); - } -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java index afe11938c42..fa7837cec6e 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -30,10 +30,8 @@ import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.amd64.*; import sun.jvm.hotspot.debugger.x86.*; -import sun.jvm.hotspot.debugger.ia64.*; import sun.jvm.hotspot.debugger.windbg.amd64.*; import sun.jvm.hotspot.debugger.windbg.x86.*; -import sun.jvm.hotspot.debugger.windbg.ia64.*; import sun.jvm.hotspot.debugger.win32.coff.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent; @@ -115,8 +113,6 @@ public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger threadFactory = new WindbgX86ThreadFactory(this); } else if (cpu.equals("amd64")) { threadFactory = new WindbgAMD64ThreadFactory(this); - } else if (cpu.equals("ia64")) { - threadFactory = new WindbgIA64ThreadFactory(this); } if (useCache) { @@ -231,11 +227,7 @@ public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger public CDebugger getCDebugger() throws DebuggerException { if (cdbg == null) { - // FIXME: CDebugger is not yet supported for IA64 because - // of native stack walking issues. - if (! getCPU().equals("ia64")) { - cdbg = new WindbgCDebugger(this); - } + cdbg = new WindbgCDebugger(this); } return cdbg; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java deleted file mode 100644 index 591ad9cf5a9..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2003, 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. - * - */ - -package sun.jvm.hotspot.debugger.windbg.ia64; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.ia64.*; -import sun.jvm.hotspot.debugger.windbg.*; - -class WindbgIA64Thread implements ThreadProxy { - private WindbgDebugger debugger; - private long sysId; - private boolean gotID; - private long id; - - /** The address argument must be the address of the HANDLE of the - desired thread in the target process. */ - WindbgIA64Thread(WindbgDebugger debugger, Address addr) { - this.debugger = debugger; - // FIXME: size of data fetched here should be configurable. - // However, making it so would produce a dependency on the "types" - // package from the debugger package, which is not desired. - - // another hack here is that we use sys thread id instead of handle. - // windbg can't get details based on handles it seems. - // I assume that osThread_win32 thread struct has _thread_id (which - // sys thread id) just after handle field. - - this.sysId = (int) addr.addOffsetTo(debugger.getAddressSize()).getCIntegerAt(0, 4, true); - gotID = false; - } - - WindbgIA64Thread(WindbgDebugger debugger, long sysId) { - this.debugger = debugger; - this.sysId = sysId; - gotID = false; - } - - public ThreadContext getContext() throws IllegalThreadStateException { - long[] data = debugger.getThreadIntegerRegisterSet(getThreadID()); - WindbgIA64ThreadContext context = new WindbgIA64ThreadContext(debugger); - for (int i = 0; i < data.length; i++) { - context.setRegister(i, data[i]); - } - return context; - } - - public boolean canSetContext() throws DebuggerException { - return false; - } - - public void setContext(ThreadContext thrCtx) - throws IllegalThreadStateException, DebuggerException { - throw new DebuggerException("Unimplemented"); - } - - public boolean equals(Object obj) { - if ((obj == null) || !(obj instanceof WindbgIA64Thread)) { - return false; - } - - return (((WindbgIA64Thread) obj).getThreadID() == getThreadID()); - } - - public int hashCode() { - return (int) getThreadID(); - } - - public String toString() { - return Long.toString(getThreadID()); - } - - /** Retrieves the thread ID of this thread by examining the Thread - Information Block. */ - private long getThreadID() { - if (!gotID) { - id = debugger.getThreadIdFromSysId(sysId); - } - - return id; - } -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java deleted file mode 100644 index ba739808002..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2003, 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. - * - */ - -package sun.jvm.hotspot.debugger.windbg.ia64; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.ia64.*; -import sun.jvm.hotspot.debugger.windbg.*; - -class WindbgIA64ThreadContext extends IA64ThreadContext { - private WindbgDebugger debugger; - - public WindbgIA64ThreadContext(WindbgDebugger debugger) { - super(); - this.debugger = debugger; - } - - public void setRegisterAsAddress(int index, Address value) { - setRegister(index, debugger.getAddressValue(value)); - } - - public Address getRegisterAsAddress(int index) { - return debugger.newAddress(getRegister(index)); - } -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java deleted file mode 100644 index ee2080de713..00000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2003, 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. - * - */ - -package sun.jvm.hotspot.debugger.windbg.ia64; - -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.debugger.windbg.*; - -public class WindbgIA64ThreadFactory implements WindbgThreadFactory { - private WindbgDebugger debugger; - - public WindbgIA64ThreadFactory(WindbgDebugger debugger) { - this.debugger = debugger; - } - - public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) { - return new WindbgIA64Thread(debugger, threadIdentifierAddr); - } - - public ThreadProxy createThreadWrapper(long id) { - return new WindbgIA64Thread(debugger, id); - } -} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index 1456c019951..4bcd98bd3da 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -474,20 +474,48 @@ public class InstanceKlass extends Klass { } // same as enum InnerClassAttributeOffset in VM code. - public static interface InnerClassAttributeOffset { + private static class InnerClassAttributeOffset { // from JVM spec. "InnerClasses" attribute - public static final int innerClassInnerClassInfoOffset = 0; - public static final int innerClassOuterClassInfoOffset = 1; - public static final int innerClassInnerNameOffset = 2; - public static final int innerClassAccessFlagsOffset = 3; - public static final int innerClassNextOffset = 4; - }; + public static int innerClassInnerClassInfoOffset; + public static int innerClassOuterClassInfoOffset; + public static int innerClassInnerNameOffset; + public static int innerClassAccessFlagsOffset; + public static int innerClassNextOffset; + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } - public static interface EnclosingMethodAttributeOffset { - public static final int enclosing_method_class_index_offset = 0; - public static final int enclosing_method_method_index_offset = 1; - public static final int enclosing_method_attribute_size = 2; - }; + private static synchronized void initialize(TypeDataBase db) { + innerClassInnerClassInfoOffset = db.lookupIntConstant( + "InstanceKlass::inner_class_inner_class_info_offset").intValue(); + innerClassOuterClassInfoOffset = db.lookupIntConstant( + "InstanceKlass::inner_class_outer_class_info_offset").intValue(); + innerClassInnerNameOffset = db.lookupIntConstant( + "InstanceKlass::inner_class_inner_name_offset").intValue(); + innerClassAccessFlagsOffset = db.lookupIntConstant( + "InstanceKlass::inner_class_access_flags_offset").intValue(); + innerClassNextOffset = db.lookupIntConstant( + "InstanceKlass::inner_class_next_offset").intValue(); + } + } + + private static class EnclosingMethodAttributeOffset { + public static int enclosingMethodAttributeSize; + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + private static synchronized void initialize(TypeDataBase db) { + enclosingMethodAttributeSize = db.lookupIntConstant("InstanceKlass::enclosing_method_attribute_size").intValue(); + } + } // refer to compute_modifier_flags in VM code. public long computeModifierFlags() { @@ -498,11 +526,11 @@ public class InstanceKlass extends Klass { if (length > 0) { if (Assert.ASSERTS_ENABLED) { Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 || - length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosing_method_attribute_size, + length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize, "just checking"); } for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) { - if (i == length - EnclosingMethodAttributeOffset.enclosing_method_attribute_size) { + if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) { break; } int ioff = innerClassList.at(i + @@ -547,11 +575,11 @@ public class InstanceKlass extends Klass { if (length > 0) { if (Assert.ASSERTS_ENABLED) { Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 || - length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosing_method_attribute_size, + length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize, "just checking"); } for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) { - if (i == length - EnclosingMethodAttributeOffset.enclosing_method_attribute_size) { + if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) { break; } int ioff = innerClassList.at(i + diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java index aee92a4f654..e5f72b0783c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java @@ -44,9 +44,7 @@ public class ObjectSynchronizer { Type type; try { type = db.lookupType("ObjectSynchronizer"); - AddressField blockListField; - blockListField = type.getAddressField("gBlockList"); - gBlockListAddr = blockListField.getValue(); + gBlockList = type.getAddressField("gBlockList").getValue(); blockSize = db.lookupIntConstant("ObjectSynchronizer::_BLOCKSIZE").intValue(); defaultCacheLineSize = db.lookupIntConstant("DEFAULT_CACHE_LINE_SIZE").intValue(); } catch (RuntimeException e) { } @@ -84,7 +82,7 @@ public class ObjectSynchronizer { } public static Iterator objectMonitorIterator() { - if (gBlockListAddr != null) { + if (gBlockList != null) { return new ObjectMonitorIterator(); } else { return null; @@ -97,7 +95,7 @@ public class ObjectSynchronizer { // and are not included by this Iterator. May add them later. ObjectMonitorIterator() { - blockAddr = gBlockListAddr; + blockAddr = gBlockList; index = blockSize - 1; block = new ObjectMonitor(blockAddr); } @@ -131,7 +129,7 @@ public class ObjectSynchronizer { private Address blockAddr; } - private static Address gBlockListAddr; + private static Address gBlockList; private static int blockSize; private static int defaultCacheLineSize; private static long objectMonitorTypeSize; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java index 5d16b7026b5..7818afba5c8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2017, 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 @@ -92,14 +92,29 @@ public class PerfDataEntry extends VMObject { return (flags() & 0x1) != 0; } - // NOTE: Keep this in sync with PerfData::Units enum in VM code - public interface PerfDataUnits { - public static final int U_None = 1; - public static final int U_Bytes = 2; - public static final int U_Ticks = 3; - public static final int U_Events = 4; - public static final int U_String = 5; - public static final int U_Hertz = 6; + private static class PerfDataUnits { + public static int U_None; + public static int U_Bytes; + public static int U_Ticks; + public static int U_Events; + public static int U_String; + public static int U_Hertz; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + private static synchronized void initialize(TypeDataBase db) { + U_None = db.lookupIntConstant("PerfData::U_None"); + U_Bytes = db.lookupIntConstant("PerfData::U_Bytes"); + U_Ticks = db.lookupIntConstant("PerfData::U_Ticks"); + U_Events = db.lookupIntConstant("PerfData::U_Events"); + U_String = db.lookupIntConstant("PerfData::U_String"); + U_Hertz = db.lookupIntConstant("PerfData::U_Hertz"); + } } // returns one of the constants in PerfDataUnits @@ -107,13 +122,6 @@ public class PerfDataEntry extends VMObject { return (int) dataUnitsField.getValue(addr); } - // NOTE: Keep this in sync with PerfData::Variability enum in VM code - public interface PerfDataVariability { - public static final int V_Constant = 1; - public static final int V_Monotonic = 2; - public static final int V_Variable = 3; - } - // returns one of the constants in PerfDataVariability public int dataVariability() { return (int) dataVariabilityField.getValue(addr); @@ -451,23 +459,16 @@ public class PerfDataEntry extends VMObject { } // add units - switch (dataUnits()) { - case PerfDataUnits.U_None: - break; - case PerfDataUnits.U_Bytes: + int dataUnitsValue = dataUnits(); + + if (dataUnitsValue == PerfDataUnits.U_Bytes) { str += " byte(s)"; - break; - case PerfDataUnits.U_Ticks: + } else if (dataUnitsValue == PerfDataUnits.U_Ticks) { str += " tick(s)"; - break; - case PerfDataUnits.U_Events: + } else if (dataUnitsValue == PerfDataUnits.U_Events) { str += " event(s)"; - break; - case PerfDataUnits.U_String: - break; - case PerfDataUnits.U_Hertz: + } else if (dataUnitsValue == PerfDataUnits.U_Hertz) { str += " Hz"; - break; } return str; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java index 2506d417371..f4d7827254c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2017, 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 @@ -117,10 +117,7 @@ public class VFrame { return null; } Frame s = fr.realSender(tempMap); - // ia64 in 1.4.1 only has java frames and no entryFrame - // so "s" can be null here for the first frame. if (s == null) { - Assert.that(VM.getVM().getCPU().equals("ia64"), "Only ia64 should have null here"); return null; } if (s.isFirstFrame()) { diff --git a/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h b/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h index a91f72a7e31..b926ad94102 100644 --- a/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h +++ b/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -77,14 +77,14 @@ extern int _libproc_debug; /* set non-zero to enable debugging fprintfs */ typedef uint32_t syscall_t; /* holds a syscall instruction */ #endif /* sparc */ -#if defined(__i386) || defined(__ia64) +#if defined(__i386) #define R_PC EIP #define R_SP UESP #define R_RVAL1 EAX /* register holding a function return value */ #define R_RVAL2 EDX /* 32 more bits for a 64-bit return value */ #define SYSCALL 0x9a /* syscall (lcall) instruction opcode */ typedef uchar_t syscall_t[7]; /* holds a syscall instruction */ -#endif /* __i386 || __ia64 */ +#endif /* __i386 */ #define R_RVAL R_RVAL1 /* simple function return value register */ From d6aded9c6859b0958a42236bbac9a6753676d17e Mon Sep 17 00:00:00 2001 From: Igor Veresov <iveresov@openjdk.org> Date: Thu, 30 Nov 2017 08:35:33 -0800 Subject: [PATCH 093/165] 8192756: SIGSEGV in nmethod::new_native_nmethod Add the missing null check Reviewed-by: kvn, thartmann --- src/hotspot/share/code/nmethod.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 82757c0e2ff..83cc30d79b3 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -438,14 +438,14 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method, basic_lock_sp_offset, oop_maps); NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm)); } - // verify nmethod - debug_only(if (nm) nm->verify();) // might block if (nm != NULL) { - nm->log_new_nmethod(); - } + // verify nmethod + debug_only(nm->verify();) // might block - nm->make_in_use(); + nm->log_new_nmethod(); + nm->make_in_use(); + } return nm; } From 3f3f0cb67ea8b9022933a2ebf5b79d4a1766b8c8 Mon Sep 17 00:00:00 2001 From: Roman Kennke <rkennke@openjdk.org> Date: Thu, 30 Nov 2017 13:40:07 +0100 Subject: [PATCH 094/165] 8191564: Refactor GC related servicability code into GC specific subclasses Reviewed-by: ehelin, eosterlund --- src/hotspot/share/gc/cms/cmsHeap.cpp | 80 +++- src/hotspot/share/gc/cms/cmsHeap.hpp | 13 + .../gc/cms/concurrentMarkSweepGeneration.cpp | 56 +-- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 40 +- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 14 + src/hotspot/share/gc/g1/g1FullCollector.cpp | 4 +- src/hotspot/share/gc/g1/g1FullCollector.hpp | 3 +- src/hotspot/share/gc/g1/g1FullGCScope.cpp | 4 +- src/hotspot/share/gc/g1/g1FullGCScope.hpp | 4 +- .../{services => gc/g1}/g1MemoryPool.cpp | 5 +- .../{services => gc/g1}/g1MemoryPool.hpp | 13 +- .../gc/parallel/parallelScavengeHeap.cpp | 46 ++ .../gc/parallel/parallelScavengeHeap.hpp | 20 +- src/hotspot/share/gc/parallel/psMarkSweep.cpp | 2 +- .../parallel}/psMemoryPool.cpp | 21 +- .../parallel}/psMemoryPool.hpp | 16 +- .../share/gc/parallel/psParallelCompact.cpp | 2 +- src/hotspot/share/gc/parallel/psScavenge.cpp | 2 +- src/hotspot/share/gc/serial/serialHeap.cpp | 52 ++- src/hotspot/share/gc/serial/serialHeap.hpp | 14 +- src/hotspot/share/gc/shared/collectedHeap.cpp | 4 + src/hotspot/share/gc/shared/collectedHeap.hpp | 11 +- .../share/gc/shared/genCollectedHeap.cpp | 3 +- .../share/gc/shared/genCollectedHeap.hpp | 3 + .../share/gc/shared/genMemoryPools.cpp | 92 ++++ .../share/gc/shared/genMemoryPools.hpp | 75 ++++ src/hotspot/share/gc/shared/generation.cpp | 3 +- src/hotspot/share/gc/shared/generation.hpp | 13 + src/hotspot/share/services/memoryManager.cpp | 45 +- src/hotspot/share/services/memoryManager.hpp | 102 +---- src/hotspot/share/services/memoryPool.cpp | 94 ----- src/hotspot/share/services/memoryPool.hpp | 66 +-- src/hotspot/share/services/memoryService.cpp | 392 +----------------- src/hotspot/share/services/memoryService.hpp | 80 +--- .../gc/TestMemoryMXBeansAndPoolsPresence.java | 101 +++++ 35 files changed, 678 insertions(+), 817 deletions(-) rename src/hotspot/share/{services => gc/g1}/g1MemoryPool.cpp (95%) rename src/hotspot/share/{services => gc/g1}/g1MemoryPool.hpp (92%) rename src/hotspot/share/{services => gc/parallel}/psMemoryPool.cpp (81%) rename src/hotspot/share/{services => gc/parallel}/psMemoryPool.hpp (82%) create mode 100644 src/hotspot/share/gc/shared/genMemoryPools.cpp create mode 100644 src/hotspot/share/gc/shared/genMemoryPools.hpp create mode 100644 test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java diff --git a/src/hotspot/share/gc/cms/cmsHeap.cpp b/src/hotspot/share/gc/cms/cmsHeap.cpp index da344cce24d..8b44d900312 100644 --- a/src/hotspot/share/gc/cms/cmsHeap.cpp +++ b/src/hotspot/share/gc/cms/cmsHeap.cpp @@ -23,17 +23,48 @@ */ #include "precompiled.hpp" +#include "gc/cms/compactibleFreeListSpace.hpp" +#include "gc/cms/concurrentMarkSweepGeneration.hpp" #include "gc/cms/concurrentMarkSweepThread.hpp" #include "gc/cms/cmsHeap.hpp" +#include "gc/cms/parNewGeneration.hpp" #include "gc/cms/vmCMSOperations.hpp" +#include "gc/shared/genMemoryPools.hpp" #include "gc/shared/genOopClosures.inline.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/workgroup.hpp" #include "oops/oop.inline.hpp" #include "runtime/vmThread.hpp" +#include "services/memoryManager.hpp" #include "utilities/stack.inline.hpp" -CMSHeap::CMSHeap(GenCollectorPolicy *policy) : GenCollectedHeap(policy) { +class CompactibleFreeListSpacePool : public CollectedMemoryPool { +private: + CompactibleFreeListSpace* _space; +public: + CompactibleFreeListSpacePool(CompactibleFreeListSpace* space, + const char* name, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, space->capacity(), max_size, support_usage_threshold), + _space(space) { + } + + MemoryUsage get_memory_usage() { + size_t max_heap_size = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _space->capacity(); + + return MemoryUsage(initial_size(), used, committed, max_heap_size); + } + + size_t used_in_bytes() { + return _space->used(); + } +}; + +CMSHeap::CMSHeap(GenCollectorPolicy *policy) : + GenCollectedHeap(policy), _eden_pool(NULL), _survivor_pool(NULL), _old_pool(NULL) { _workers = new WorkGang("GC Thread", ParallelGCThreads, /* are_GC_task_threads */true, /* are_ConcurrentGC_threads */false); @@ -54,6 +85,38 @@ jint CMSHeap::initialize() { return JNI_OK; } +void CMSHeap::initialize_serviceability() { + _young_manager = new GCMemoryManager("ParNew", "end of minor GC"); + _old_manager = new GCMemoryManager("ConcurrentMarkSweep", "end of major GC"); + + ParNewGeneration* young = (ParNewGeneration*) young_gen(); + _eden_pool = new ContiguousSpacePool(young->eden(), + "Par Eden Space", + young->max_eden_size(), + false); + + _survivor_pool = new SurvivorContiguousSpacePool(young, + "Par Survivor Space", + young->max_survivor_size(), + false); + + ConcurrentMarkSweepGeneration* old = (ConcurrentMarkSweepGeneration*) old_gen(); + _old_pool = new CompactibleFreeListSpacePool(old->cmsSpace(), + "CMS Old Gen", + old->reserved().byte_size(), + true); + + _young_manager->add_pool(_eden_pool); + _young_manager->add_pool(_survivor_pool); + young->set_gc_manager(_young_manager); + + _old_manager->add_pool(_eden_pool); + _old_manager->add_pool(_survivor_pool); + _old_manager->add_pool(_old_pool); + old ->set_gc_manager(_old_manager); + +} + void CMSHeap::check_gen_kinds() { assert(young_gen()->kind() == Generation::ParNew, "Wrong youngest generation type"); @@ -183,3 +246,18 @@ void CMSHeap::gc_epilogue(bool full) { GenCollectedHeap::gc_epilogue(full); always_do_update_barrier = true; }; + +GrowableArray<GCMemoryManager*> CMSHeap::memory_managers() { + GrowableArray<GCMemoryManager*> memory_managers(2); + memory_managers.append(_young_manager); + memory_managers.append(_old_manager); + return memory_managers; +} + +GrowableArray<MemoryPool*> CMSHeap::memory_pools() { + GrowableArray<MemoryPool*> memory_pools(3); + memory_pools.append(_eden_pool); + memory_pools.append(_survivor_pool); + memory_pools.append(_old_pool); + return memory_pools; +} diff --git a/src/hotspot/share/gc/cms/cmsHeap.hpp b/src/hotspot/share/gc/cms/cmsHeap.hpp index bcd30cba859..93079d7cf2b 100644 --- a/src/hotspot/share/gc/cms/cmsHeap.hpp +++ b/src/hotspot/share/gc/cms/cmsHeap.hpp @@ -29,9 +29,12 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcCause.hpp" #include "gc/shared/genCollectedHeap.hpp" +#include "utilities/growableArray.hpp" class CLDClosure; class GenCollectorPolicy; +class GCMemoryManager; +class MemoryPool; class OopsInGenClosure; class outputStream; class StrongRootsScope; @@ -80,6 +83,9 @@ public: void safepoint_synchronize_begin(); void safepoint_synchronize_end(); + virtual GrowableArray<GCMemoryManager*> memory_managers(); + virtual GrowableArray<MemoryPool*> memory_pools(); + // If "young_gen_as_roots" is false, younger generations are // not scanned as roots; in this case, the caller must be arranging to // scan the younger generations itself. (For example, a generation might @@ -92,12 +98,19 @@ public: OopsInGenClosure* root_closure, CLDClosure* cld_closure); + GCMemoryManager* old_manager() const { return _old_manager; } + private: WorkGang* _workers; + MemoryPool* _eden_pool; + MemoryPool* _survivor_pool; + MemoryPool* _old_pool; virtual void gc_prologue(bool full); virtual void gc_epilogue(bool full); + virtual void initialize_serviceability(); + // Accessor for memory state verification support NOT_PRODUCT( virtual size_t skip_header_HeapWords() { return CMSCollector::skip_header_HeapWords(); } diff --git a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp index c49112957b0..4f8f7836550 100644 --- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp +++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp @@ -8116,42 +8116,42 @@ size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) { } TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() { - + GCMemoryManager* manager = CMSHeap::heap()->old_manager(); switch (phase) { case CMSCollector::InitialMarking: - initialize(true /* fullGC */ , - cause /* cause of the GC */, - true /* recordGCBeginTime */, - true /* recordPreGCUsage */, - false /* recordPeakUsage */, - false /* recordPostGCusage */, - true /* recordAccumulatedGCTime */, - false /* recordGCEndTime */, - false /* countCollection */ ); + initialize(manager /* GC manager */ , + cause /* cause of the GC */, + true /* recordGCBeginTime */, + true /* recordPreGCUsage */, + false /* recordPeakUsage */, + false /* recordPostGCusage */, + true /* recordAccumulatedGCTime */, + false /* recordGCEndTime */, + false /* countCollection */ ); break; case CMSCollector::FinalMarking: - initialize(true /* fullGC */ , - cause /* cause of the GC */, - false /* recordGCBeginTime */, - false /* recordPreGCUsage */, - false /* recordPeakUsage */, - false /* recordPostGCusage */, - true /* recordAccumulatedGCTime */, - false /* recordGCEndTime */, - false /* countCollection */ ); + initialize(manager /* GC manager */ , + cause /* cause of the GC */, + false /* recordGCBeginTime */, + false /* recordPreGCUsage */, + false /* recordPeakUsage */, + false /* recordPostGCusage */, + true /* recordAccumulatedGCTime */, + false /* recordGCEndTime */, + false /* countCollection */ ); break; case CMSCollector::Sweeping: - initialize(true /* fullGC */ , - cause /* cause of the GC */, - false /* recordGCBeginTime */, - false /* recordPreGCUsage */, - true /* recordPeakUsage */, - true /* recordPostGCusage */, - false /* recordAccumulatedGCTime */, - true /* recordGCEndTime */, - true /* countCollection */ ); + initialize(manager /* GC manager */ , + cause /* cause of the GC */, + false /* recordGCBeginTime */, + false /* recordPreGCUsage */, + true /* recordPeakUsage */, + true /* recordPostGCusage */, + false /* recordAccumulatedGCTime */, + true /* recordGCEndTime */, + true /* countCollection */ ); break; default: diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index e164a012cae..14e942ecefa 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -44,6 +44,7 @@ #include "gc/g1/g1HeapTransition.hpp" #include "gc/g1/g1HeapVerifier.hpp" #include "gc/g1/g1HotCardCache.hpp" +#include "gc/g1/g1MemoryPool.hpp" #include "gc/g1/g1OopClosures.inline.hpp" #include "gc/g1/g1ParScanThreadState.inline.hpp" #include "gc/g1/g1Policy.hpp" @@ -1229,7 +1230,7 @@ bool G1CollectedHeap::do_full_collection(bool explicit_gc, const bool do_clear_all_soft_refs = clear_all_soft_refs || collector_policy()->should_clear_all_soft_refs(); - G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs); + G1FullCollector collector(this, &_full_gc_memory_manager, explicit_gc, do_clear_all_soft_refs); GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true); collector.prepare_collection(); @@ -1526,6 +1527,11 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) : CollectedHeap(), _young_gen_sampling_thread(NULL), _collector_policy(collector_policy), + _memory_manager("G1 Young Generation", "end of minor GC"), + _full_gc_memory_manager("G1 Old Generation", "end of major GC"), + _eden_pool(NULL), + _survivor_pool(NULL), + _old_pool(NULL), _gc_timer_stw(new (ResourceObj::C_HEAP, mtGC) STWGCTimer()), _gc_tracer_stw(new (ResourceObj::C_HEAP, mtGC) G1NewTracer()), _g1_policy(create_g1_policy(_gc_timer_stw)), @@ -1830,6 +1836,20 @@ jint G1CollectedHeap::initialize() { return JNI_OK; } +void G1CollectedHeap::initialize_serviceability() { + _eden_pool = new G1EdenPool(this); + _survivor_pool = new G1SurvivorPool(this); + _old_pool = new G1OldGenPool(this); + + _full_gc_memory_manager.add_pool(_eden_pool); + _full_gc_memory_manager.add_pool(_survivor_pool); + _full_gc_memory_manager.add_pool(_old_pool); + + _memory_manager.add_pool(_eden_pool); + _memory_manager.add_pool(_survivor_pool); + +} + void G1CollectedHeap::stop() { // Stop all concurrent threads. We do this to make sure these threads // do not continue to execute and access resources (e.g. logging) @@ -1855,6 +1875,7 @@ size_t G1CollectedHeap::conservative_max_heap_alignment() { } void G1CollectedHeap::post_initialize() { + CollectedHeap::post_initialize(); ref_processing_init(); } @@ -2954,7 +2975,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->total_workers()); TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); - TraceMemoryManagerStats tms(false /* fullGC */, gc_cause()); + TraceMemoryManagerStats tms(&_memory_manager, gc_cause()); // If the secondary_free_list is not empty, append it to the // free_list. No need to wait for the cleanup operation to finish; @@ -5368,3 +5389,18 @@ void G1CollectedHeap::rebuild_strong_code_roots() { RebuildStrongCodeRootClosure blob_cl(this); CodeCache::blobs_do(&blob_cl); } + +GrowableArray<GCMemoryManager*> G1CollectedHeap::memory_managers() { + GrowableArray<GCMemoryManager*> memory_managers(2); + memory_managers.append(&_memory_manager); + memory_managers.append(&_full_gc_memory_manager); + return memory_managers; +} + +GrowableArray<MemoryPool*> G1CollectedHeap::memory_pools() { + GrowableArray<MemoryPool*> memory_pools(3); + memory_pools.append(_eden_pool); + memory_pools.append(_survivor_pool); + memory_pools.append(_old_pool); + return memory_pools; +} diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index ff00bc114ab..d268e2ef7be 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -50,6 +50,7 @@ #include "gc/shared/plab.hpp" #include "gc/shared/preservedMarks.hpp" #include "memory/memRegion.hpp" +#include "services/memoryManager.hpp" #include "utilities/stack.hpp" // A "G1CollectedHeap" is an implementation of a java heap for HotSpot. @@ -64,6 +65,7 @@ class GenerationSpec; class G1ParScanThreadState; class G1ParScanThreadStateSet; class G1ParScanThreadState; +class MemoryPool; class ObjectClosure; class SpaceClosure; class CompactibleSpaceClosure; @@ -149,6 +151,13 @@ private: WorkGang* _workers; G1CollectorPolicy* _collector_policy; + GCMemoryManager _memory_manager; + GCMemoryManager _full_gc_memory_manager; + + MemoryPool* _eden_pool; + MemoryPool* _survivor_pool; + MemoryPool* _old_pool; + static size_t _humongous_object_threshold_in_words; // The secondary free list which contains regions that have been @@ -162,6 +171,8 @@ private: // It keeps track of the humongous regions. HeapRegionSet _humongous_set; + virtual void initialize_serviceability(); + void eagerly_reclaim_humongous_regions(); // Start a new incremental collection set for the next pause. void start_new_collection_set(); @@ -1006,6 +1017,9 @@ public: // Adaptive size policy. No such thing for g1. virtual AdaptiveSizePolicy* size_policy() { return NULL; } + virtual GrowableArray<GCMemoryManager*> memory_managers(); + virtual GrowableArray<MemoryPool*> memory_pools(); + // The rem set and barrier set. G1RemSet* g1_rem_set() const { return _g1_rem_set; } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index e47b02f2efb..3ee356884ea 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -71,9 +71,9 @@ ReferenceProcessor* G1FullCollector::reference_processor() { return _heap->ref_processor_stw(); } -G1FullCollector::G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs) : +G1FullCollector::G1FullCollector(G1CollectedHeap* heap, GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft_refs) : _heap(heap), - _scope(explicit_gc, clear_soft_refs), + _scope(memory_manager, explicit_gc, clear_soft_refs), _num_workers(heap->workers()->active_workers()), _oop_queue_set(_num_workers), _array_queue_set(_num_workers), diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 42caa812ec6..576aba5c8c6 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -39,6 +39,7 @@ class G1CMBitMap; class G1FullGCMarker; class G1FullGCScope; class G1FullGCCompactionPoint; +class GCMemoryManager; class ReferenceProcessor; // The G1FullCollector holds data associated with the current Full GC. @@ -56,7 +57,7 @@ class G1FullCollector : StackObj { ReferenceProcessorIsAliveMutator _is_alive_mutator; public: - G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs); + G1FullCollector(G1CollectedHeap* heap, GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft_refs); ~G1FullCollector(); void prepare_collection(); diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp index 87dd42b93f3..2430451df1c 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "gc/g1/g1FullGCScope.hpp" -G1FullGCScope::G1FullGCScope(bool explicit_gc, bool clear_soft) : +G1FullGCScope::G1FullGCScope(GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft) : _rm(), _explicit_gc(explicit_gc), _g1h(G1CollectedHeap::heap()), @@ -36,7 +36,7 @@ G1FullGCScope::G1FullGCScope(bool explicit_gc, bool clear_soft) : _active(), _cpu_time(), _soft_refs(clear_soft, _g1h->collector_policy()), - _memory_stats(true, _g1h->gc_cause()), + _memory_stats(memory_manager, _g1h->gc_cause()), _collector_stats(_g1h->g1mm()->full_collection_counters()), _heap_transition(_g1h) { _timer.register_gc_start(); diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp index 20dc8d21643..850ee0aea0f 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp @@ -37,6 +37,8 @@ #include "memory/allocation.hpp" #include "services/memoryService.hpp" +class GCMemoryManager; + // Class used to group scoped objects used in the Full GC together. class G1FullGCScope : public StackObj { ResourceMark _rm; @@ -54,7 +56,7 @@ class G1FullGCScope : public StackObj { G1HeapTransition _heap_transition; public: - G1FullGCScope(bool explicit_gc, bool clear_soft); + G1FullGCScope(GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft); ~G1FullGCScope(); bool is_explicit_gc(); diff --git a/src/hotspot/share/services/g1MemoryPool.cpp b/src/hotspot/share/gc/g1/g1MemoryPool.cpp similarity index 95% rename from src/hotspot/share/services/g1MemoryPool.cpp rename to src/hotspot/share/gc/g1/g1MemoryPool.cpp index 92daa7b726d..f658c0d2a27 100644 --- a/src/hotspot/share/services/g1MemoryPool.cpp +++ b/src/hotspot/share/gc/g1/g1MemoryPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2017, 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 @@ -24,8 +24,8 @@ #include "precompiled.hpp" #include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1MemoryPool.hpp" #include "gc/g1/heapRegion.hpp" -#include "services/g1MemoryPool.hpp" G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h, const char* name, @@ -33,7 +33,6 @@ G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h, size_t max_size, bool support_usage_threshold) : _g1mm(g1h->g1mm()), CollectedMemoryPool(name, - MemoryPool::Heap, init_size, max_size, support_usage_threshold) { diff --git a/src/hotspot/share/services/g1MemoryPool.hpp b/src/hotspot/share/gc/g1/g1MemoryPool.hpp similarity index 92% rename from src/hotspot/share/services/g1MemoryPool.hpp rename to src/hotspot/share/gc/g1/g1MemoryPool.hpp index fddc439e899..a6771c3fd79 100644 --- a/src/hotspot/share/services/g1MemoryPool.hpp +++ b/src/hotspot/share/gc/g1/g1MemoryPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2017, 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 @@ -22,15 +22,12 @@ * */ -#ifndef SHARE_VM_SERVICES_G1MEMORYPOOL_HPP -#define SHARE_VM_SERVICES_G1MEMORYPOOL_HPP +#ifndef SHARE_VM_GC_G1_G1MEMORYPOOL_HPP +#define SHARE_VM_GC_G1_G1MEMORYPOOL_HPP -#include "utilities/macros.hpp" -#if INCLUDE_ALL_GCS #include "gc/g1/g1MonitoringSupport.hpp" #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" -#endif // INCLUDE_ALL_GCS // This file contains the three classes that represent the memory // pools of the G1 spaces: G1EdenPool, G1SurvivorPool, and @@ -50,6 +47,8 @@ // on this model. // +class G1CollectedHeap; + // This class is shared by the three G1 memory pool classes // (G1EdenPool, G1SurvivorPool, G1OldGenPool). class G1MemoryPoolSuper : public CollectedMemoryPool { @@ -107,4 +106,4 @@ public: MemoryUsage get_memory_usage(); }; -#endif // SHARE_VM_SERVICES_G1MEMORYPOOL_HPP +#endif // SHARE_VM_GC_G1_G1MEMORYPOOL_HPP diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 166a3d953b0..4fc4bc76e3b 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -33,6 +33,7 @@ #include "gc/parallel/parallelScavengeHeap.inline.hpp" #include "gc/parallel/psAdaptiveSizePolicy.hpp" #include "gc/parallel/psMarkSweep.hpp" +#include "gc/parallel/psMemoryPool.hpp" #include "gc/parallel/psParallelCompact.inline.hpp" #include "gc/parallel/psPromotionManager.hpp" #include "gc/parallel/psScavenge.hpp" @@ -45,6 +46,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/vmThread.hpp" +#include "services/memoryManager.hpp" #include "services/memTracker.hpp" #include "utilities/vmError.hpp" @@ -119,7 +121,35 @@ jint ParallelScavengeHeap::initialize() { return JNI_OK; } +void ParallelScavengeHeap::initialize_serviceability() { + + _eden_pool = new EdenMutableSpacePool(_young_gen, + _young_gen->eden_space(), + "PS Eden Space", + false /* support_usage_threshold */); + + _survivor_pool = new SurvivorMutableSpacePool(_young_gen, + "PS Survivor Space", + false /* support_usage_threshold */); + + _old_pool = new PSGenerationPool(_old_gen, + "PS Old Gen", + true /* support_usage_threshold */); + + _young_manager = new GCMemoryManager("PS Scavenge", "end of minor GC"); + _old_manager = new GCMemoryManager("PS MarkSweep", "end of major GC"); + + _old_manager->add_pool(_eden_pool); + _old_manager->add_pool(_survivor_pool); + _old_manager->add_pool(_old_pool); + + _young_manager->add_pool(_eden_pool); + _young_manager->add_pool(_survivor_pool); + +} + void ParallelScavengeHeap::post_initialize() { + CollectedHeap::post_initialize(); // Need to init the tenuring threshold PSScavenge::initialize(); if (UseParallelOldGC) { @@ -674,3 +704,19 @@ void ParallelScavengeHeap::register_nmethod(nmethod* nm) { void ParallelScavengeHeap::verify_nmethod(nmethod* nm) { CodeCache::verify_scavenge_root_nmethod(nm); } + +GrowableArray<GCMemoryManager*> ParallelScavengeHeap::memory_managers() { + GrowableArray<GCMemoryManager*> memory_managers(2); + memory_managers.append(_young_manager); + memory_managers.append(_old_manager); + return memory_managers; +} + +GrowableArray<MemoryPool*> ParallelScavengeHeap::memory_pools() { + GrowableArray<MemoryPool*> memory_pools(3); + memory_pools.append(_eden_pool); + memory_pools.append(_survivor_pool); + memory_pools.append(_old_pool); + return memory_pools; +} + diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index bfdc55f07f7..2eca2b12dcd 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2017, 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 @@ -36,11 +36,14 @@ #include "gc/shared/gcWhen.hpp" #include "gc/shared/strongRootsScope.hpp" #include "memory/metaspace.hpp" +#include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" class AdjoiningGenerations; class GCHeapSummary; class GCTaskManager; +class MemoryManager; +class MemoryPool; class PSAdaptiveSizePolicy; class PSHeapSummary; @@ -64,6 +67,15 @@ class ParallelScavengeHeap : public CollectedHeap { // The task manager static GCTaskManager* _gc_task_manager; + GCMemoryManager* _young_manager; + GCMemoryManager* _old_manager; + + MemoryPool* _eden_pool; + MemoryPool* _survivor_pool; + MemoryPool* _old_pool; + + virtual void initialize_serviceability(); + void trace_heap(GCWhen::Type when, const GCTracer* tracer); protected: @@ -94,6 +106,9 @@ class ParallelScavengeHeap : public CollectedHeap { virtual CollectorPolicy* collector_policy() const { return _collector_policy; } + virtual GrowableArray<GCMemoryManager*> memory_managers(); + virtual GrowableArray<MemoryPool*> memory_pools(); + static PSYoungGen* young_gen() { return _young_gen; } static PSOldGen* old_gen() { return _old_gen; } @@ -244,6 +259,9 @@ class ParallelScavengeHeap : public CollectedHeap { ParStrongRootsScope(); ~ParStrongRootsScope(); }; + + GCMemoryManager* old_gc_manager() const { return _old_manager; } + GCMemoryManager* young_gc_manager() const { return _young_manager; } }; // Simple class for storing info about the heap at the start of GC, to be used diff --git a/src/hotspot/share/gc/parallel/psMarkSweep.cpp b/src/hotspot/share/gc/parallel/psMarkSweep.cpp index 185cbb2f62d..2ac25afc016 100644 --- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp +++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp @@ -172,7 +172,7 @@ bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) { heap->pre_full_gc_dump(_gc_timer); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(true /* Full GC */,gc_cause); + TraceMemoryManagerStats tms(heap->old_gc_manager(),gc_cause); if (log_is_enabled(Debug, gc, heap, exit)) { accumulated_time()->start(); diff --git a/src/hotspot/share/services/psMemoryPool.cpp b/src/hotspot/share/gc/parallel/psMemoryPool.cpp similarity index 81% rename from src/hotspot/share/services/psMemoryPool.cpp rename to src/hotspot/share/gc/parallel/psMemoryPool.cpp index 8e356cea27b..92e94efdfa2 100644 --- a/src/hotspot/share/services/psMemoryPool.cpp +++ b/src/hotspot/share/gc/parallel/psMemoryPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2017, 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 @@ -23,21 +23,12 @@ */ #include "precompiled.hpp" -#include "classfile/systemDictionary.hpp" -#include "classfile/vmSymbols.hpp" -#include "oops/oop.inline.hpp" -#include "runtime/handles.inline.hpp" -#include "runtime/javaCalls.hpp" -#include "services/lowMemoryDetector.hpp" -#include "services/management.hpp" -#include "services/memoryManager.hpp" -#include "services/psMemoryPool.hpp" +#include "gc/parallel/psMemoryPool.hpp" PSGenerationPool::PSGenerationPool(PSOldGen* old_gen, const char* name, - PoolType type, bool support_usage_threshold) : - CollectedMemoryPool(name, type, old_gen->capacity_in_bytes(), + CollectedMemoryPool(name, old_gen->capacity_in_bytes(), old_gen->reserved().byte_size(), support_usage_threshold), _old_gen(old_gen) { } @@ -58,9 +49,8 @@ MemoryUsage PSGenerationPool::get_memory_usage() { EdenMutableSpacePool::EdenMutableSpacePool(PSYoungGen* young_gen, MutableSpace* space, const char* name, - PoolType type, bool support_usage_threshold) : - CollectedMemoryPool(name, type, space->capacity_in_bytes(), + CollectedMemoryPool(name, space->capacity_in_bytes(), (young_gen->max_size() - young_gen->from_space()->capacity_in_bytes() - young_gen->to_space()->capacity_in_bytes()), support_usage_threshold), _young_gen(young_gen), @@ -82,9 +72,8 @@ MemoryUsage EdenMutableSpacePool::get_memory_usage() { // SurvivorMutableSpacePool::SurvivorMutableSpacePool(PSYoungGen* young_gen, const char* name, - PoolType type, bool support_usage_threshold) : - CollectedMemoryPool(name, type, young_gen->from_space()->capacity_in_bytes(), + CollectedMemoryPool(name, young_gen->from_space()->capacity_in_bytes(), young_gen->from_space()->capacity_in_bytes(), support_usage_threshold), _young_gen(young_gen) { } diff --git a/src/hotspot/share/services/psMemoryPool.hpp b/src/hotspot/share/gc/parallel/psMemoryPool.hpp similarity index 82% rename from src/hotspot/share/services/psMemoryPool.hpp rename to src/hotspot/share/gc/parallel/psMemoryPool.hpp index f1a900caa0f..522799d8336 100644 --- a/src/hotspot/share/services/psMemoryPool.hpp +++ b/src/hotspot/share/gc/parallel/psMemoryPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2017, 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,28 +25,22 @@ #ifndef SHARE_VM_SERVICES_PSMEMORYPOOL_HPP #define SHARE_VM_SERVICES_PSMEMORYPOOL_HPP -#include "utilities/macros.hpp" -#if INCLUDE_ALL_GCS #include "gc/parallel/mutableSpace.hpp" #include "gc/parallel/psOldGen.hpp" #include "gc/parallel/psYoungGen.hpp" -#include "gc/serial/defNewGeneration.hpp" -#include "gc/shared/space.hpp" -#include "memory/heap.hpp" #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" -#endif // INCLUDE_ALL_GCS class PSGenerationPool : public CollectedMemoryPool { private: PSOldGen* _old_gen; public: - PSGenerationPool(PSOldGen* pool, const char* name, PoolType type, bool support_usage_threshold); + PSGenerationPool(PSOldGen* pool, const char* name, bool support_usage_threshold); MemoryUsage get_memory_usage(); - size_t used_in_bytes() { return _old_gen->used_in_bytes(); } - size_t max_size() const { return _old_gen->reserved().byte_size(); } + size_t used_in_bytes() { return _old_gen->used_in_bytes(); } + size_t max_size() const { return _old_gen->reserved().byte_size(); } }; class EdenMutableSpacePool : public CollectedMemoryPool { @@ -58,7 +52,6 @@ public: EdenMutableSpacePool(PSYoungGen* young_gen, MutableSpace* space, const char* name, - PoolType type, bool support_usage_threshold); MutableSpace* space() { return _space; } @@ -77,7 +70,6 @@ private: public: SurvivorMutableSpacePool(PSYoungGen* young_gen, const char* name, - PoolType type, bool support_usage_threshold); MemoryUsage get_memory_usage(); diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index c46f0d41aa0..be23ea77681 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1772,7 +1772,7 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { heap->pre_full_gc_dump(&_gc_timer); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(true /* Full GC */,gc_cause); + TraceMemoryManagerStats tms(heap->old_gc_manager(), gc_cause); if (log_is_enabled(Debug, gc, heap, exit)) { accumulated_time()->start(); diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 64c0b7486ab..5bcb21bf9ff 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -305,7 +305,7 @@ bool PSScavenge::invoke_no_policy() { GCTraceCPUTime tcpu; GCTraceTime(Info, gc) tm("Pause Young", NULL, gc_cause, true); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(false /* not full GC */,gc_cause); + TraceMemoryManagerStats tms(heap->young_gc_manager(), gc_cause); if (log_is_enabled(Debug, gc, heap, exit)) { accumulated_time()->start(); diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index c4c17a8a386..c395e11fd27 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -23,9 +23,44 @@ */ #include "precompiled.hpp" +#include "gc/serial/defNewGeneration.hpp" #include "gc/serial/serialHeap.hpp" +#include "gc/shared/genMemoryPools.hpp" +#include "services/memoryManager.hpp" -SerialHeap::SerialHeap(GenCollectorPolicy* policy) : GenCollectedHeap(policy) {} +SerialHeap::SerialHeap(GenCollectorPolicy* policy) : + GenCollectedHeap(policy), _eden_pool(NULL), _survivor_pool(NULL), _old_pool(NULL) { + _young_manager = new GCMemoryManager("Copy", "end of minor GC"); + _old_manager = new GCMemoryManager("MarkSweepCompact", "end of major GC"); +} + +void SerialHeap::initialize_serviceability() { + + DefNewGeneration* young = (DefNewGeneration*) young_gen(); + + // Add a memory pool for each space and young gen doesn't + // support low memory detection as it is expected to get filled up. + _eden_pool = new ContiguousSpacePool(young->eden(), + "Eden Space", + young->max_eden_size(), + false /* support_usage_threshold */); + _survivor_pool = new SurvivorContiguousSpacePool(young, + "Survivor Space", + young->max_survivor_size(), + false /* support_usage_threshold */); + Generation* old = old_gen(); + _old_pool = new GenerationPool(old, "Tenured Gen", true); + + _young_manager->add_pool(_eden_pool); + _young_manager->add_pool(_survivor_pool); + young->set_gc_manager(_young_manager); + + _old_manager->add_pool(_eden_pool); + _old_manager->add_pool(_survivor_pool); + _old_manager->add_pool(_old_pool); + old->set_gc_manager(_old_manager); + +} void SerialHeap::check_gen_kinds() { assert(young_gen()->kind() == Generation::DefNew, @@ -33,3 +68,18 @@ void SerialHeap::check_gen_kinds() { assert(old_gen()->kind() == Generation::MarkSweepCompact, "Wrong generation kind"); } + +GrowableArray<GCMemoryManager*> SerialHeap::memory_managers() { + GrowableArray<GCMemoryManager*> memory_managers(2); + memory_managers.append(_young_manager); + memory_managers.append(_old_manager); + return memory_managers; +} + +GrowableArray<MemoryPool*> SerialHeap::memory_pools() { + GrowableArray<MemoryPool*> memory_pools(3); + memory_pools.append(_eden_pool); + memory_pools.append(_survivor_pool); + memory_pools.append(_old_pool); + return memory_pools; +} diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 8b96006638a..8df86f2e4bf 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -26,10 +26,20 @@ #define SHARE_VM_GC_SERIAL_SERIALHEAP_HPP #include "gc/shared/genCollectedHeap.hpp" +#include "utilities/growableArray.hpp" class GenCollectorPolicy; +class GCMemoryManager; +class MemoryPool; class SerialHeap : public GenCollectedHeap { +private: + MemoryPool* _eden_pool; + MemoryPool* _survivor_pool; + MemoryPool* _old_pool; + + virtual void initialize_serviceability(); + protected: virtual void check_gen_kinds(); @@ -44,6 +54,9 @@ public: return "Serial"; } + virtual GrowableArray<GCMemoryManager*> memory_managers(); + virtual GrowableArray<MemoryPool*> memory_pools(); + // override virtual bool is_in_closed_subset(const void* p) const { return is_in(p); @@ -52,7 +65,6 @@ public: virtual bool card_mark_must_follow_store() const { return false; } - }; #endif // SHARE_VM_GC_CMS_CMSHEAP_HPP diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index e4961555019..a32fd69a3fb 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -611,3 +611,7 @@ void CollectedHeap::initialize_reserved_region(HeapWord *start, HeapWord *end) { _reserved.set_start(start); _reserved.set_end(end); } + +void CollectedHeap::post_initialize() { + initialize_serviceability(); +} diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 197eaf9b438..7be834d815c 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -34,6 +34,7 @@ #include "utilities/debug.hpp" #include "utilities/events.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/growableArray.hpp" // A "CollectedHeap" is an implementation of a java heap for HotSpot. This // is an abstract class: there may be many different kinds of heaps. This @@ -46,6 +47,8 @@ class CollectorPolicy; class GCHeapSummary; class GCTimer; class GCTracer; +class GCMemoryManager; +class MemoryPool; class MetaspaceSummary; class Thread; class ThreadClosure; @@ -217,7 +220,7 @@ class CollectedHeap : public CHeapObj<mtInternal> { // In many heaps, there will be a need to perform some initialization activities // after the Universe is fully formed, but before general heap allocation is allowed. // This is the correct place to place such initialization methods. - virtual void post_initialize() = 0; + virtual void post_initialize(); // Stop any onging concurrent work and prepare for exit. virtual void stop() {} @@ -485,6 +488,9 @@ class CollectedHeap : public CHeapObj<mtInternal> { // Return the CollectorPolicy for the heap virtual CollectorPolicy* collector_policy() const = 0; + virtual GrowableArray<GCMemoryManager*> memory_managers() = 0; + virtual GrowableArray<MemoryPool*> memory_pools() = 0; + // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl) = 0; @@ -529,6 +535,9 @@ class CollectedHeap : public CHeapObj<mtInternal> { // Generate any dumps preceding or following a full gc private: void full_gc_dump(GCTimer* timer, bool before); + + virtual void initialize_serviceability() = 0; + public: void pre_full_gc_dump(GCTimer* timer); void post_full_gc_dump(GCTimer* timer); diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp index 5b21eb6a294..ae67feca465 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp @@ -143,6 +143,7 @@ char* GenCollectedHeap::allocate(size_t alignment, } void GenCollectedHeap::post_initialize() { + CollectedHeap::post_initialize(); ref_processing_init(); check_gen_kinds(); DefNewGeneration* def_new_gen = (DefNewGeneration*)_young_gen; @@ -270,7 +271,7 @@ void GenCollectedHeap::collect_generation(Generation* gen, bool full, size_t siz FormatBuffer<> title("Collect gen: %s", gen->short_name()); GCTraceTime(Trace, gc, phases) t1(title); TraceCollectorStats tcs(gen->counters()); - TraceMemoryManagerStats tmms(gen->kind(),gc_cause()); + TraceMemoryManagerStats tmms(gen->gc_manager(), gc_cause()); gen->stat_record()->invocations++; gen->stat_record()->accumulated_time.start(); diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.hpp b/src/hotspot/share/gc/shared/genCollectedHeap.hpp index 5eb4f749d14..e0a91302b0e 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp @@ -112,6 +112,9 @@ protected: // (gen-specific) roots processing. SubTasksDone* _process_strong_tasks; + GCMemoryManager* _young_manager; + GCMemoryManager* _old_manager; + // Helper functions for allocation HeapWord* attempt_allocation(size_t size, bool is_tlab, diff --git a/src/hotspot/share/gc/shared/genMemoryPools.cpp b/src/hotspot/share/gc/shared/genMemoryPools.cpp new file mode 100644 index 00000000000..292f2197c32 --- /dev/null +++ b/src/hotspot/share/gc/shared/genMemoryPools.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/serial/defNewGeneration.hpp" +#include "gc/shared/generation.hpp" +#include "gc/shared/genMemoryPools.hpp" +#include "gc/shared/space.hpp" + +ContiguousSpacePool::ContiguousSpacePool(ContiguousSpace* space, + const char* name, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, space->capacity(), max_size, + support_usage_threshold), _space(space) { +} + +size_t ContiguousSpacePool::used_in_bytes() { + return space()->used(); +} + +MemoryUsage ContiguousSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = _space->capacity(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +SurvivorContiguousSpacePool::SurvivorContiguousSpacePool(DefNewGeneration* young_gen, + const char* name, + size_t max_size, + bool support_usage_threshold) : + CollectedMemoryPool(name, young_gen->from()->capacity(), max_size, + support_usage_threshold), _young_gen(young_gen) { +} + +size_t SurvivorContiguousSpacePool::used_in_bytes() { + return _young_gen->from()->used(); +} + +size_t SurvivorContiguousSpacePool::committed_in_bytes() { + return _young_gen->from()->capacity(); +} + +MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() { + size_t maxSize = (available_for_allocation() ? max_size() : 0); + size_t used = used_in_bytes(); + size_t committed = committed_in_bytes(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +GenerationPool::GenerationPool(Generation* gen, + const char* name, + bool support_usage_threshold) : + CollectedMemoryPool(name, gen->capacity(), gen->max_capacity(), + support_usage_threshold), _gen(gen) { +} + +size_t GenerationPool::used_in_bytes() { + return _gen->used(); +} + +MemoryUsage GenerationPool::get_memory_usage() { + size_t used = used_in_bytes(); + size_t committed = _gen->capacity(); + size_t maxSize = (available_for_allocation() ? max_size() : 0); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} diff --git a/src/hotspot/share/gc/shared/genMemoryPools.hpp b/src/hotspot/share/gc/shared/genMemoryPools.hpp new file mode 100644 index 00000000000..93ee91bf959 --- /dev/null +++ b/src/hotspot/share/gc/shared/genMemoryPools.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#ifndef SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP +#define SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP + +#include "services/memoryPool.hpp" + +class ContiguousSpace; +class DefNewGeneration; +class Generation; + +class ContiguousSpacePool : public CollectedMemoryPool { +private: + ContiguousSpace* _space; + +public: + ContiguousSpacePool(ContiguousSpace* space, + const char* name, + size_t max_size, + bool support_usage_threshold); + + ContiguousSpace* space() { return _space; } + MemoryUsage get_memory_usage(); + size_t used_in_bytes(); +}; + +class SurvivorContiguousSpacePool : public CollectedMemoryPool { +private: + DefNewGeneration* _young_gen; + +public: + SurvivorContiguousSpacePool(DefNewGeneration* young_gen, + const char* name, + size_t max_size, + bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + + size_t used_in_bytes(); + size_t committed_in_bytes(); +}; + +class GenerationPool : public CollectedMemoryPool { +private: + Generation* _gen; +public: + GenerationPool(Generation* gen, const char* name, bool support_usage_threshold); + + MemoryUsage get_memory_usage(); + size_t used_in_bytes(); +}; + +#endif // SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP diff --git a/src/hotspot/share/gc/shared/generation.cpp b/src/hotspot/share/gc/shared/generation.cpp index 68c92230729..fbdb2f45b3f 100644 --- a/src/hotspot/share/gc/shared/generation.cpp +++ b/src/hotspot/share/gc/shared/generation.cpp @@ -44,7 +44,8 @@ #include "utilities/events.hpp" Generation::Generation(ReservedSpace rs, size_t initial_size) : - _ref_processor(NULL) { + _ref_processor(NULL), + _gc_manager(NULL) { if (!_virtual_space.initialize(rs, initial_size)) { vm_exit_during_initialization("Could not reserve enough space for " "object heap"); diff --git a/src/hotspot/share/gc/shared/generation.hpp b/src/hotspot/share/gc/shared/generation.hpp index 00d17a22a33..ea8f8d8b8ee 100644 --- a/src/hotspot/share/gc/shared/generation.hpp +++ b/src/hotspot/share/gc/shared/generation.hpp @@ -58,6 +58,7 @@ // class DefNewGeneration; +class GCMemoryManager; class GenerationSpec; class CompactibleSpace; class ContiguousSpace; @@ -86,6 +87,8 @@ class Generation: public CHeapObj<mtGC> { MemRegion _prev_used_region; // for collectors that want to "remember" a value for // used region at some specific point during collection. + GCMemoryManager* _gc_manager; + protected: // Minimum and maximum addresses for memory reserved (not necessarily // committed) for generation. @@ -554,6 +557,16 @@ public: // Performance Counter support virtual void update_counters() = 0; virtual CollectorCounters* counters() { return _gc_counters; } + + GCMemoryManager* gc_manager() const { + assert(_gc_manager != NULL, "not initialized yet"); + return _gc_manager; + } + + void set_gc_manager(GCMemoryManager* gc_manager) { + _gc_manager = gc_manager; + } + }; #endif // SHARE_VM_GC_SHARED_GENERATION_HPP diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp index 8c6fc6d6f1a..5bc16bfd837 100644 --- a/src/hotspot/share/services/memoryManager.cpp +++ b/src/hotspot/share/services/memoryManager.cpp @@ -37,7 +37,7 @@ #include "services/gcNotifier.hpp" #include "utilities/dtrace.hpp" -MemoryManager::MemoryManager() { +MemoryManager::MemoryManager(const char* name) : _name(name) { _num_pools = 0; (void)const_cast<instanceOop&>(_memory_mgr_obj = instanceOop(NULL)); } @@ -52,43 +52,11 @@ void MemoryManager::add_pool(MemoryPool* pool) { } MemoryManager* MemoryManager::get_code_cache_memory_manager() { - return (MemoryManager*) new CodeCacheMemoryManager(); + return new MemoryManager("CodeCacheManager"); } MemoryManager* MemoryManager::get_metaspace_memory_manager() { - return (MemoryManager*) new MetaspaceMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_copy_memory_manager() { - return (GCMemoryManager*) new CopyMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_msc_memory_manager() { - return (GCMemoryManager*) new MSCMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_parnew_memory_manager() { - return (GCMemoryManager*) new ParNewMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_cms_memory_manager() { - return (GCMemoryManager*) new CMSMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_psScavenge_memory_manager() { - return (GCMemoryManager*) new PSScavengeMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_psMarkSweep_memory_manager() { - return (GCMemoryManager*) new PSMarkSweepMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_g1YoungGen_memory_manager() { - return (GCMemoryManager*) new G1YoungGenMemoryManager(); -} - -GCMemoryManager* MemoryManager::get_g1OldGen_memory_manager() { - return (GCMemoryManager*) new G1OldGenMemoryManager(); + return new MemoryManager("Metaspace Manager"); } instanceOop MemoryManager::get_memory_manager_instance(TRAPS) { @@ -203,7 +171,8 @@ void GCStatInfo::clear() { } -GCMemoryManager::GCMemoryManager() : MemoryManager() { +GCMemoryManager::GCMemoryManager(const char* name, const char* gc_end_message) : + MemoryManager(name), _gc_end_message(gc_end_message) { _num_collections = 0; _last_gc_stat = NULL; _last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true, @@ -308,9 +277,7 @@ void GCMemoryManager::gc_end(bool recordPostGCUsage, } if (is_notification_enabled()) { - bool isMajorGC = this == MemoryService::get_major_gc_manager(); - GCNotifier::pushNotification(this, isMajorGC ? "end of major GC" : "end of minor GC", - GCCause::to_string(cause)); + GCNotifier::pushNotification(this, _gc_end_message, GCCause::to_string(cause)); } } } diff --git a/src/hotspot/share/services/memoryManager.hpp b/src/hotspot/share/services/memoryManager.hpp index 3b853d08579..7d8ef854813 100644 --- a/src/hotspot/share/services/memoryManager.hpp +++ b/src/hotspot/share/services/memoryManager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -52,11 +52,13 @@ private: MemoryPool* _pools[max_num_pools]; int _num_pools; + const char* _name; + protected: volatile instanceOop _memory_mgr_obj; public: - MemoryManager(); + MemoryManager(const char* name); int num_memory_pools() const { return _num_pools; } MemoryPool* get_memory_pool(int index) { @@ -70,7 +72,8 @@ public: virtual instanceOop get_memory_manager_instance(TRAPS); virtual bool is_gc_memory_manager() { return false; } - virtual const char* name() = 0; + + const char* name() const { return _name; } // GC support void oops_do(OopClosure* f); @@ -78,29 +81,6 @@ public: // Static factory methods to get a memory manager of a specific type static MemoryManager* get_code_cache_memory_manager(); static MemoryManager* get_metaspace_memory_manager(); - static GCMemoryManager* get_copy_memory_manager(); - static GCMemoryManager* get_msc_memory_manager(); - static GCMemoryManager* get_parnew_memory_manager(); - static GCMemoryManager* get_cms_memory_manager(); - static GCMemoryManager* get_psScavenge_memory_manager(); - static GCMemoryManager* get_psMarkSweep_memory_manager(); - static GCMemoryManager* get_g1YoungGen_memory_manager(); - static GCMemoryManager* get_g1OldGen_memory_manager(); -}; - -class CodeCacheMemoryManager : public MemoryManager { -private: -public: - CodeCacheMemoryManager() : MemoryManager() {} - - const char* name() { return "CodeCacheManager"; } -}; - -class MetaspaceMemoryManager : public MemoryManager { -public: - MetaspaceMemoryManager() : MemoryManager() {} - - const char* name() { return "Metaspace Manager"; } }; class GCStatInfo : public ResourceObj { @@ -162,8 +142,9 @@ private: GCStatInfo* _current_gc_stat; int _num_gc_threads; volatile bool _notification_enabled; + const char* _gc_end_message; public: - GCMemoryManager(); + GCMemoryManager(const char* name, const char* gc_end_message); ~GCMemoryManager(); void initialize_gc_stat_info(); @@ -189,71 +170,4 @@ public: bool is_notification_enabled() { return _notification_enabled; } }; -// These subclasses of GCMemoryManager are defined to include -// GC-specific information. -// TODO: Add GC-specific information -class CopyMemoryManager : public GCMemoryManager { -private: -public: - CopyMemoryManager() : GCMemoryManager() {} - - const char* name() { return "Copy"; } -}; - -class MSCMemoryManager : public GCMemoryManager { -private: -public: - MSCMemoryManager() : GCMemoryManager() {} - - const char* name() { return "MarkSweepCompact"; } -}; - -class ParNewMemoryManager : public GCMemoryManager { -private: -public: - ParNewMemoryManager() : GCMemoryManager() {} - - const char* name() { return "ParNew"; } -}; - -class CMSMemoryManager : public GCMemoryManager { -private: -public: - CMSMemoryManager() : GCMemoryManager() {} - - const char* name() { return "ConcurrentMarkSweep";} -}; - -class PSScavengeMemoryManager : public GCMemoryManager { -private: -public: - PSScavengeMemoryManager() : GCMemoryManager() {} - - const char* name() { return "PS Scavenge"; } -}; - -class PSMarkSweepMemoryManager : public GCMemoryManager { -private: -public: - PSMarkSweepMemoryManager() : GCMemoryManager() {} - - const char* name() { return "PS MarkSweep"; } -}; - -class G1YoungGenMemoryManager : public GCMemoryManager { -private: -public: - G1YoungGenMemoryManager() : GCMemoryManager() {} - - const char* name() { return "G1 Young Generation"; } -}; - -class G1OldGenMemoryManager : public GCMemoryManager { -private: -public: - G1OldGenMemoryManager() : GCMemoryManager() {} - - const char* name() { return "G1 Old Generation"; } -}; - #endif // SHARE_VM_SERVICES_MEMORYMANAGER_HPP diff --git a/src/hotspot/share/services/memoryPool.cpp b/src/hotspot/share/services/memoryPool.cpp index cec78ae67a4..8248b472225 100644 --- a/src/hotspot/share/services/memoryPool.cpp +++ b/src/hotspot/share/services/memoryPool.cpp @@ -25,8 +25,6 @@ #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" -#include "gc/serial/defNewGeneration.hpp" -#include "gc/shared/space.hpp" #include "memory/metaspace.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" @@ -38,9 +36,6 @@ #include "services/memoryPool.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" -#if INCLUDE_ALL_GCS -#include "gc/cms/compactibleFreeListSpace.hpp" -#endif MemoryPool::MemoryPool(const char* name, PoolType type, @@ -182,95 +177,6 @@ void MemoryPool::oops_do(OopClosure* f) { } } -ContiguousSpacePool::ContiguousSpacePool(ContiguousSpace* space, - const char* name, - PoolType type, - size_t max_size, - bool support_usage_threshold) : - CollectedMemoryPool(name, type, space->capacity(), max_size, - support_usage_threshold), _space(space) { -} - -size_t ContiguousSpacePool::used_in_bytes() { - return space()->used(); -} - -MemoryUsage ContiguousSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); - size_t used = used_in_bytes(); - size_t committed = _space->capacity(); - - return MemoryUsage(initial_size(), used, committed, maxSize); -} - -SurvivorContiguousSpacePool::SurvivorContiguousSpacePool(DefNewGeneration* young_gen, - const char* name, - PoolType type, - size_t max_size, - bool support_usage_threshold) : - CollectedMemoryPool(name, type, young_gen->from()->capacity(), max_size, - support_usage_threshold), _young_gen(young_gen) { -} - -size_t SurvivorContiguousSpacePool::used_in_bytes() { - return _young_gen->from()->used(); -} - -size_t SurvivorContiguousSpacePool::committed_in_bytes() { - return _young_gen->from()->capacity(); -} - -MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); - size_t used = used_in_bytes(); - size_t committed = committed_in_bytes(); - - return MemoryUsage(initial_size(), used, committed, maxSize); -} - -#if INCLUDE_ALL_GCS -CompactibleFreeListSpacePool::CompactibleFreeListSpacePool(CompactibleFreeListSpace* space, - const char* name, - PoolType type, - size_t max_size, - bool support_usage_threshold) : - CollectedMemoryPool(name, type, space->capacity(), max_size, - support_usage_threshold), _space(space) { -} - -size_t CompactibleFreeListSpacePool::used_in_bytes() { - return _space->used(); -} - -MemoryUsage CompactibleFreeListSpacePool::get_memory_usage() { - size_t maxSize = (available_for_allocation() ? max_size() : 0); - size_t used = used_in_bytes(); - size_t committed = _space->capacity(); - - return MemoryUsage(initial_size(), used, committed, maxSize); -} -#endif // INCLUDE_ALL_GCS - -GenerationPool::GenerationPool(Generation* gen, - const char* name, - PoolType type, - bool support_usage_threshold) : - CollectedMemoryPool(name, type, gen->capacity(), gen->max_capacity(), - support_usage_threshold), _gen(gen) { -} - -size_t GenerationPool::used_in_bytes() { - return _gen->used(); -} - -MemoryUsage GenerationPool::get_memory_usage() { - size_t used = used_in_bytes(); - size_t committed = _gen->capacity(); - size_t maxSize = (available_for_allocation() ? max_size() : 0); - - return MemoryUsage(initial_size(), used, committed, maxSize); -} - CodeHeapPool::CodeHeapPool(CodeHeap* codeHeap, const char* name, bool support_usage_threshold) : MemoryPool(name, NonHeap, codeHeap->capacity(), codeHeap->max_capacity(), support_usage_threshold, false), _codeHeap(codeHeap) { diff --git a/src/hotspot/share/services/memoryPool.hpp b/src/hotspot/share/services/memoryPool.hpp index 0144f61786c..b1c21580b43 100644 --- a/src/hotspot/share/services/memoryPool.hpp +++ b/src/hotspot/share/services/memoryPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -37,12 +37,8 @@ // both heap and non-heap memory. // Forward declaration -class CompactibleFreeListSpace; -class ContiguousSpace; class MemoryManager; class SensorInfo; -class Generation; -class DefNewGeneration; class ThresholdSupport; class MemoryPool : public CHeapObj<mtInternal> { @@ -144,67 +140,11 @@ class MemoryPool : public CHeapObj<mtInternal> { class CollectedMemoryPool : public MemoryPool { public: - CollectedMemoryPool(const char* name, PoolType type, size_t init_size, size_t max_size, bool support_usage_threshold) : - MemoryPool(name, type, init_size, max_size, support_usage_threshold, true) {}; + CollectedMemoryPool(const char* name, size_t init_size, size_t max_size, bool support_usage_threshold) : + MemoryPool(name, MemoryPool::Heap, init_size, max_size, support_usage_threshold, true) {}; bool is_collected_pool() { return true; } }; -class ContiguousSpacePool : public CollectedMemoryPool { -private: - ContiguousSpace* _space; - -public: - ContiguousSpacePool(ContiguousSpace* space, const char* name, PoolType type, size_t max_size, bool support_usage_threshold); - - ContiguousSpace* space() { return _space; } - MemoryUsage get_memory_usage(); - size_t used_in_bytes(); -}; - -class SurvivorContiguousSpacePool : public CollectedMemoryPool { -private: - DefNewGeneration* _young_gen; - -public: - SurvivorContiguousSpacePool(DefNewGeneration* young_gen, - const char* name, - PoolType type, - size_t max_size, - bool support_usage_threshold); - - MemoryUsage get_memory_usage(); - - size_t used_in_bytes(); - size_t committed_in_bytes(); -}; - -#if INCLUDE_ALL_GCS -class CompactibleFreeListSpacePool : public CollectedMemoryPool { -private: - CompactibleFreeListSpace* _space; -public: - CompactibleFreeListSpacePool(CompactibleFreeListSpace* space, - const char* name, - PoolType type, - size_t max_size, - bool support_usage_threshold); - - MemoryUsage get_memory_usage(); - size_t used_in_bytes(); -}; -#endif // INCLUDE_ALL_GCS - - -class GenerationPool : public CollectedMemoryPool { -private: - Generation* _gen; -public: - GenerationPool(Generation* gen, const char* name, PoolType type, bool support_usage_threshold); - - MemoryUsage get_memory_usage(); - size_t used_in_bytes(); -}; - class CodeHeapPool: public MemoryPool { private: CodeHeap* _codeHeap; diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp index d579cd91738..e96e2a0d151 100644 --- a/src/hotspot/share/services/memoryService.cpp +++ b/src/hotspot/share/services/memoryService.cpp @@ -25,13 +25,7 @@ #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" -#include "gc/parallel/mutableSpace.hpp" -#include "gc/serial/defNewGeneration.hpp" -#include "gc/serial/tenuredGeneration.hpp" -#include "gc/shared/collectorPolicy.hpp" -#include "gc/shared/genCollectedHeap.hpp" -#include "gc/shared/generation.hpp" -#include "gc/shared/generationSpec.hpp" +#include "gc/shared/collectedHeap.hpp" #include "logging/logConfiguration.hpp" #include "memory/heap.hpp" #include "memory/memRegion.hpp" @@ -46,24 +40,12 @@ #include "services/memoryService.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -#if INCLUDE_ALL_GCS -#include "gc/cms/concurrentMarkSweepGeneration.hpp" -#include "gc/cms/parNewGeneration.hpp" -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/parallel/parallelScavengeHeap.hpp" -#include "gc/parallel/psOldGen.hpp" -#include "gc/parallel/psYoungGen.hpp" -#include "services/g1MemoryPool.hpp" -#include "services/psMemoryPool.hpp" -#endif // INCLUDE_ALL_GCS GrowableArray<MemoryPool*>* MemoryService::_pools_list = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryPool*>(init_pools_list_size, true); GrowableArray<MemoryManager*>* MemoryService::_managers_list = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryManager*>(init_managers_list_size, true); -GCMemoryManager* MemoryService::_minor_gc_manager = NULL; -GCMemoryManager* MemoryService::_major_gc_manager = NULL; MemoryManager* MemoryService::_code_cache_manager = NULL; GrowableArray<MemoryPool*>* MemoryService::_code_heap_pools = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryPool*>(init_code_heap_pools_size, true); @@ -84,311 +66,28 @@ void GcThreadCountClosure::do_thread(Thread* thread) { } void MemoryService::set_universe_heap(CollectedHeap* heap) { - CollectedHeap::Name kind = heap->kind(); - switch (kind) { - case CollectedHeap::SerialHeap : - case CollectedHeap::CMSHeap : { - add_gen_collected_heap_info(GenCollectedHeap::heap()); - break; - } -#if INCLUDE_ALL_GCS - case CollectedHeap::ParallelScavengeHeap : { - add_parallel_scavenge_heap_info(ParallelScavengeHeap::heap()); - break; - } - case CollectedHeap::G1CollectedHeap : { - add_g1_heap_info(G1CollectedHeap::heap()); - break; - } -#endif // INCLUDE_ALL_GCS - default: { - guarantee(false, "Unrecognized kind of heap"); - } - } + ResourceMark rm; // For internal allocations in GrowableArray. + + GrowableArray<MemoryPool*> gc_mem_pools = heap->memory_pools(); + _pools_list->appendAll(&gc_mem_pools); // set the GC thread count GcThreadCountClosure gctcc; heap->gc_threads_do(&gctcc); int count = gctcc.count(); - if (count > 0) { - _minor_gc_manager->set_num_gc_threads(count); - _major_gc_manager->set_num_gc_threads(count); - } - // All memory pools and memory managers are initialized. - // - _minor_gc_manager->initialize_gc_stat_info(); - _major_gc_manager->initialize_gc_stat_info(); -} + GrowableArray<GCMemoryManager*> gc_memory_managers = heap->memory_managers(); + for (int i = 0; i < gc_memory_managers.length(); i++) { + GCMemoryManager* gc_manager = gc_memory_managers.at(i); -// Add memory pools for GenCollectedHeap -// This function currently only supports two generations collected heap. -// The collector for GenCollectedHeap will have two memory managers. -void MemoryService::add_gen_collected_heap_info(GenCollectedHeap* heap) { - CollectorPolicy* policy = heap->collector_policy(); - - assert(policy->is_generation_policy(), "Only support two generations"); - GenCollectorPolicy* gen_policy = policy->as_generation_policy(); - if (gen_policy != NULL) { - Generation::Name kind = gen_policy->young_gen_spec()->name(); - switch (kind) { - case Generation::DefNew: - _minor_gc_manager = MemoryManager::get_copy_memory_manager(); - break; -#if INCLUDE_ALL_GCS - case Generation::ParNew: - _minor_gc_manager = MemoryManager::get_parnew_memory_manager(); - break; -#endif // INCLUDE_ALL_GCS - default: - guarantee(false, "Unrecognized generation spec"); - break; - } - if (policy->is_mark_sweep_policy()) { - _major_gc_manager = MemoryManager::get_msc_memory_manager(); -#if INCLUDE_ALL_GCS - } else if (policy->is_concurrent_mark_sweep_policy()) { - _major_gc_manager = MemoryManager::get_cms_memory_manager(); -#endif // INCLUDE_ALL_GCS - } else { - guarantee(false, "Unknown two-gen policy"); - } - } else { - guarantee(false, "Non two-gen policy"); - } - _managers_list->append(_minor_gc_manager); - _managers_list->append(_major_gc_manager); - - add_generation_memory_pool(heap->young_gen(), _major_gc_manager, _minor_gc_manager); - add_generation_memory_pool(heap->old_gen(), _major_gc_manager); -} - -#if INCLUDE_ALL_GCS -// Add memory pools for ParallelScavengeHeap -// This function currently only supports two generations collected heap. -// The collector for ParallelScavengeHeap will have two memory managers. -void MemoryService::add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap) { - // Two managers to keep statistics about _minor_gc_manager and _major_gc_manager GC. - _minor_gc_manager = MemoryManager::get_psScavenge_memory_manager(); - _major_gc_manager = MemoryManager::get_psMarkSweep_memory_manager(); - _managers_list->append(_minor_gc_manager); - _managers_list->append(_major_gc_manager); - - add_psYoung_memory_pool(heap->young_gen(), _major_gc_manager, _minor_gc_manager); - add_psOld_memory_pool(heap->old_gen(), _major_gc_manager); -} - -void MemoryService::add_g1_heap_info(G1CollectedHeap* g1h) { - assert(UseG1GC, "sanity"); - - _minor_gc_manager = MemoryManager::get_g1YoungGen_memory_manager(); - _major_gc_manager = MemoryManager::get_g1OldGen_memory_manager(); - _managers_list->append(_minor_gc_manager); - _managers_list->append(_major_gc_manager); - - add_g1YoungGen_memory_pool(g1h, _major_gc_manager, _minor_gc_manager); - add_g1OldGen_memory_pool(g1h, _major_gc_manager); -} -#endif // INCLUDE_ALL_GCS - -MemoryPool* MemoryService::add_gen(Generation* gen, - const char* name, - bool is_heap, - bool support_usage_threshold) { - - MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); - GenerationPool* pool = new GenerationPool(gen, name, type, support_usage_threshold); - _pools_list->append(pool); - return (MemoryPool*) pool; -} - -MemoryPool* MemoryService::add_space(ContiguousSpace* space, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold) { - MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); - ContiguousSpacePool* pool = new ContiguousSpacePool(space, name, type, max_size, support_usage_threshold); - - _pools_list->append(pool); - return (MemoryPool*) pool; -} - -MemoryPool* MemoryService::add_survivor_spaces(DefNewGeneration* young_gen, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold) { - MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); - SurvivorContiguousSpacePool* pool = new SurvivorContiguousSpacePool(young_gen, name, type, max_size, support_usage_threshold); - - _pools_list->append(pool); - return (MemoryPool*) pool; -} - -#if INCLUDE_ALL_GCS -MemoryPool* MemoryService::add_cms_space(CompactibleFreeListSpace* space, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold) { - MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap); - CompactibleFreeListSpacePool* pool = new CompactibleFreeListSpacePool(space, name, type, max_size, support_usage_threshold); - _pools_list->append(pool); - return (MemoryPool*) pool; -} -#endif // INCLUDE_ALL_GCS - -// Add memory pool(s) for one generation -void MemoryService::add_generation_memory_pool(Generation* gen, - MemoryManager* major_mgr, - MemoryManager* minor_mgr) { - guarantee(gen != NULL, "No generation for memory pool"); - Generation::Name kind = gen->kind(); - int index = _pools_list->length(); - - switch (kind) { - case Generation::DefNew: { - assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); - DefNewGeneration* young_gen = (DefNewGeneration*) gen; - // Add a memory pool for each space and young gen doesn't - // support low memory detection as it is expected to get filled up. - MemoryPool* eden = add_space(young_gen->eden(), - "Eden Space", - true, /* is_heap */ - young_gen->max_eden_size(), - false /* support_usage_threshold */); - MemoryPool* survivor = add_survivor_spaces(young_gen, - "Survivor Space", - true, /* is_heap */ - young_gen->max_survivor_size(), - false /* support_usage_threshold */); - break; - } - -#if INCLUDE_ALL_GCS - case Generation::ParNew: - { - assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); - // Add a memory pool for each space and young gen doesn't - // support low memory detection as it is expected to get filled up. - ParNewGeneration* parnew_gen = (ParNewGeneration*) gen; - MemoryPool* eden = add_space(parnew_gen->eden(), - "Par Eden Space", - true /* is_heap */, - parnew_gen->max_eden_size(), - false /* support_usage_threshold */); - MemoryPool* survivor = add_survivor_spaces(parnew_gen, - "Par Survivor Space", - true, /* is_heap */ - parnew_gen->max_survivor_size(), - false /* support_usage_threshold */); - - break; - } -#endif // INCLUDE_ALL_GCS - - case Generation::MarkSweepCompact: { - assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager"); - add_gen(gen, - "Tenured Gen", - true, /* is_heap */ - true /* support_usage_threshold */); - break; - } - -#if INCLUDE_ALL_GCS - case Generation::ConcurrentMarkSweep: - { - assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager"); - ConcurrentMarkSweepGeneration* cms = (ConcurrentMarkSweepGeneration*) gen; - MemoryPool* pool = add_cms_space(cms->cmsSpace(), - "CMS Old Gen", - true, /* is_heap */ - cms->reserved().byte_size(), - true /* support_usage_threshold */); - break; - } -#endif // INCLUDE_ALL_GCS - - default: - assert(false, "should not reach here"); - // no memory pool added for others - break; - } - - assert(major_mgr != NULL, "Should have at least one manager"); - // Link managers and the memory pools together - for (int i = index; i < _pools_list->length(); i++) { - MemoryPool* pool = _pools_list->at(i); - major_mgr->add_pool(pool); - if (minor_mgr != NULL) { - minor_mgr->add_pool(pool); + if (count > 0) { + gc_manager->set_num_gc_threads(count); } + gc_manager->initialize_gc_stat_info(); + _managers_list->append(gc_manager); } } - -#if INCLUDE_ALL_GCS -void MemoryService::add_psYoung_memory_pool(PSYoungGen* young_gen, MemoryManager* major_mgr, MemoryManager* minor_mgr) { - assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers"); - - // Add a memory pool for each space and young gen doesn't - // support low memory detection as it is expected to get filled up. - EdenMutableSpacePool* eden = new EdenMutableSpacePool(young_gen, - young_gen->eden_space(), - "PS Eden Space", - MemoryPool::Heap, - false /* support_usage_threshold */); - - SurvivorMutableSpacePool* survivor = new SurvivorMutableSpacePool(young_gen, - "PS Survivor Space", - MemoryPool::Heap, - false /* support_usage_threshold */); - - major_mgr->add_pool(eden); - major_mgr->add_pool(survivor); - minor_mgr->add_pool(eden); - minor_mgr->add_pool(survivor); - _pools_list->append(eden); - _pools_list->append(survivor); -} - -void MemoryService::add_psOld_memory_pool(PSOldGen* old_gen, MemoryManager* mgr) { - PSGenerationPool* old_gen_pool = new PSGenerationPool(old_gen, - "PS Old Gen", - MemoryPool::Heap, - true /* support_usage_threshold */); - mgr->add_pool(old_gen_pool); - _pools_list->append(old_gen_pool); -} - -void MemoryService::add_g1YoungGen_memory_pool(G1CollectedHeap* g1h, - MemoryManager* major_mgr, - MemoryManager* minor_mgr) { - assert(major_mgr != NULL && minor_mgr != NULL, "should have two managers"); - - G1EdenPool* eden = new G1EdenPool(g1h); - G1SurvivorPool* survivor = new G1SurvivorPool(g1h); - - major_mgr->add_pool(eden); - major_mgr->add_pool(survivor); - minor_mgr->add_pool(eden); - minor_mgr->add_pool(survivor); - _pools_list->append(eden); - _pools_list->append(survivor); -} - -void MemoryService::add_g1OldGen_memory_pool(G1CollectedHeap* g1h, - MemoryManager* mgr) { - assert(mgr != NULL, "should have one manager"); - - G1OldGenPool* old_gen = new G1OldGenPool(g1h); - mgr->add_pool(old_gen); - _pools_list->append(old_gen); -} -#endif // INCLUDE_ALL_GCS - void MemoryService::add_code_heap_memory_pool(CodeHeap* heap, const char* name) { // Create new memory pool for this heap MemoryPool* code_heap_pool = new CodeHeapPool(heap, name, true /* support_usage_threshold */); @@ -463,18 +162,11 @@ void MemoryService::track_memory_pool_usage(MemoryPool* pool) { } } -void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime, +void MemoryService::gc_begin(GCMemoryManager* manager, bool recordGCBeginTime, bool recordAccumulatedGCTime, bool recordPreGCUsage, bool recordPeakUsage) { - GCMemoryManager* mgr; - if (fullGC) { - mgr = _major_gc_manager; - } else { - mgr = _minor_gc_manager; - } - assert(mgr->is_gc_memory_manager(), "Sanity check"); - mgr->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime); + manager->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime); // Track the peak memory usage when GC begins if (recordPeakUsage) { @@ -485,22 +177,13 @@ void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime, } } -void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage, +void MemoryService::gc_end(GCMemoryManager* manager, bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, GCCause::Cause cause) { - - GCMemoryManager* mgr; - if (fullGC) { - mgr = (GCMemoryManager*) _major_gc_manager; - } else { - mgr = (GCMemoryManager*) _minor_gc_manager; - } - assert(mgr->is_gc_memory_manager(), "Sanity check"); - // register the GC end statistics and memory usage - mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection, cause); + manager->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, + countCollection, cause); } void MemoryService::oops_do(OopClosure* f) { @@ -551,36 +234,7 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { return obj; } -// GC manager type depends on the type of Generation. Depending on the space -// availability and vm options the gc uses major gc manager or minor gc -// manager or both. The type of gc manager depends on the generation kind. -// For DefNew and ParNew generation doing scavenge gc uses minor gc manager (so -// _fullGC is set to false ) and for other generation kinds doing -// mark-sweep-compact uses major gc manager (so _fullGC is set to true). -TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause) { - switch (kind) { - case Generation::DefNew: -#if INCLUDE_ALL_GCS - case Generation::ParNew: -#endif // INCLUDE_ALL_GCS - _fullGC = false; - break; - case Generation::MarkSweepCompact: -#if INCLUDE_ALL_GCS - case Generation::ConcurrentMarkSweep: -#endif // INCLUDE_ALL_GCS - _fullGC = true; - break; - default: - _fullGC = false; - assert(false, "Unrecognized gc generation kind."); - } - // this has to be called in a stop the world pause and represent - // an entire gc pause, start to finish: - initialize(_fullGC, cause, true, true, true, true, true, true, true); -} - -TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, +TraceMemoryManagerStats::TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, @@ -589,14 +243,14 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection) { - initialize(fullGC, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, + initialize(gc_memory_manager, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, countCollection); } // for a subclass to create then initialize an instance before invoking // the MemoryService -void TraceMemoryManagerStats::initialize(bool fullGC, +void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, @@ -605,7 +259,7 @@ void TraceMemoryManagerStats::initialize(bool fullGC, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection) { - _fullGC = fullGC; + _gc_memory_manager = gc_memory_manager; _recordGCBeginTime = recordGCBeginTime; _recordPreGCUsage = recordPreGCUsage; _recordPeakUsage = recordPeakUsage; @@ -615,11 +269,11 @@ void TraceMemoryManagerStats::initialize(bool fullGC, _countCollection = countCollection; _cause = cause; - MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime, + MemoryService::gc_begin(_gc_memory_manager, _recordGCBeginTime, _recordAccumulatedGCTime, _recordPreGCUsage, _recordPeakUsage); } TraceMemoryManagerStats::~TraceMemoryManagerStats() { - MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime, + MemoryService::gc_end(_gc_memory_manager, _recordPostGCUsage, _recordAccumulatedGCTime, _recordGCEndTime, _countCollection, _cause); } diff --git a/src/hotspot/share/services/memoryService.hpp b/src/hotspot/share/services/memoryService.hpp index 86a6a95fa90..4ebc0813f23 100644 --- a/src/hotspot/share/services/memoryService.hpp +++ b/src/hotspot/share/services/memoryService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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,6 @@ #define SHARE_VM_SERVICES_MEMORYSERVICE_HPP #include "gc/shared/gcCause.hpp" -#include "gc/shared/generation.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" #include "runtime/handles.hpp" @@ -37,16 +36,7 @@ class MemoryPool; class MemoryManager; class GCMemoryManager; class CollectedHeap; -class Generation; -class DefNewGeneration; -class PSYoungGen; -class PSOldGen; class CodeHeap; -class ContiguousSpace; -class CompactibleFreeListSpace; -class GenCollectedHeap; -class ParallelScavengeHeap; -class G1CollectedHeap; // VM Monitoring and Management Support @@ -61,10 +51,6 @@ private: static GrowableArray<MemoryPool*>* _pools_list; static GrowableArray<MemoryManager*>* _managers_list; - // memory managers for minor and major GC statistics - static GCMemoryManager* _major_gc_manager; - static GCMemoryManager* _minor_gc_manager; - // memory manager and code heap pools for the CodeCache static MemoryManager* _code_cache_manager; static GrowableArray<MemoryPool*>* _code_heap_pools; @@ -72,51 +58,6 @@ private: static MemoryPool* _metaspace_pool; static MemoryPool* _compressed_class_pool; - static void add_generation_memory_pool(Generation* gen, - MemoryManager* major_mgr, - MemoryManager* minor_mgr); - static void add_generation_memory_pool(Generation* gen, - MemoryManager* major_mgr) { - add_generation_memory_pool(gen, major_mgr, NULL); - } - - - static void add_psYoung_memory_pool(PSYoungGen* young_gen, - MemoryManager* major_mgr, - MemoryManager* minor_mgr); - static void add_psOld_memory_pool(PSOldGen* old_gen, - MemoryManager* mgr); - - static void add_g1YoungGen_memory_pool(G1CollectedHeap* g1h, - MemoryManager* major_mgr, - MemoryManager* minor_mgr); - static void add_g1OldGen_memory_pool(G1CollectedHeap* g1h, - MemoryManager* mgr); - - static MemoryPool* add_space(ContiguousSpace* space, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold); - static MemoryPool* add_survivor_spaces(DefNewGeneration* young_gen, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold); - static MemoryPool* add_gen(Generation* gen, - const char* name, - bool is_heap, - bool support_usage_threshold); - static MemoryPool* add_cms_space(CompactibleFreeListSpace* space, - const char* name, - bool is_heap, - size_t max_size, - bool support_usage_threshold); - - static void add_gen_collected_heap_info(GenCollectedHeap* heap); - static void add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap); - static void add_g1_heap_info(G1CollectedHeap* g1h); - public: static void set_universe_heap(CollectedHeap* heap); static void add_code_heap_memory_pool(CodeHeap* heap, const char* name); @@ -155,10 +96,10 @@ public: } static void track_memory_pool_usage(MemoryPool* pool); - static void gc_begin(bool fullGC, bool recordGCBeginTime, + static void gc_begin(GCMemoryManager* manager, bool recordGCBeginTime, bool recordAccumulatedGCTime, bool recordPreGCUsage, bool recordPeakUsage); - static void gc_end(bool fullGC, bool recordPostGCUsage, + static void gc_end(GCMemoryManager* manager, bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, GCCause::Cause cause); @@ -170,19 +111,11 @@ public: // Create an instance of java/lang/management/MemoryUsage static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS); - - static const GCMemoryManager* get_minor_gc_manager() { - return _minor_gc_manager; - } - - static const GCMemoryManager* get_major_gc_manager() { - return _major_gc_manager; - } }; class TraceMemoryManagerStats : public StackObj { private: - bool _fullGC; + GCMemoryManager* _gc_memory_manager; bool _recordGCBeginTime; bool _recordPreGCUsage; bool _recordPeakUsage; @@ -193,7 +126,7 @@ private: GCCause::Cause _cause; public: TraceMemoryManagerStats() {} - TraceMemoryManagerStats(bool fullGC, + TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, bool recordGCBeginTime = true, bool recordPreGCUsage = true, @@ -203,7 +136,7 @@ public: bool recordGCEndTime = true, bool countCollection = true); - void initialize(bool fullGC, + void initialize(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, bool recordGCBeginTime, bool recordPreGCUsage, @@ -213,7 +146,6 @@ public: bool recordGCEndTime, bool countCollection); - TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause); ~TraceMemoryManagerStats(); }; diff --git a/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java b/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java new file mode 100644 index 00000000000..e923dd4ec1c --- /dev/null +++ b/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.List; +import java.util.ArrayList; +import java.lang.management.*; +import static jdk.test.lib.Asserts.*; +import java.util.stream.*; + +/* @test TestMemoryMXBeansAndPoolsPresence + * @bug 8191564 + * @summary Tests that GarbageCollectorMXBeans and GC MemoryPools are created. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @requires vm.gc == null + * @run main/othervm -XX:+UseG1GC TestMemoryMXBeansAndPoolsPresence G1 + * @run main/othervm -XX:+UseConcMarkSweepGC TestMemoryMXBeansAndPoolsPresence CMS + * @run main/othervm -XX:+UseParallelGC TestMemoryMXBeansAndPoolsPresence Parallel + * @run main/othervm -XX:+UseSerialGC TestMemoryMXBeansAndPoolsPresence Serial + */ + +class GCBeanDescription { + public String name; + public String[] poolNames; + + public GCBeanDescription(String name, String[] poolNames) { + this.name = name; + this.poolNames = poolNames; + } +} + +public class TestMemoryMXBeansAndPoolsPresence { + public static void test(GCBeanDescription... expectedBeans) { + List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans(); + + List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + assertEQ(expectedBeans.length, gcBeans.size()); + + for (GCBeanDescription desc : expectedBeans) { + List<GarbageCollectorMXBean> beans = gcBeans.stream() + .filter(b -> b.getName().equals(desc.name)) + .collect(Collectors.toList()); + assertEQ(beans.size(), 1); + + GarbageCollectorMXBean bean = beans.get(0); + assertEQ(desc.name, bean.getName()); + + String[] pools = bean.getMemoryPoolNames(); + assertEQ(desc.poolNames.length, pools.length); + for (int i = 0; i < desc.poolNames.length; i++) { + assertEQ(desc.poolNames[i], pools[i]); + } + } + } + + public static void main(String[] args) { + switch (args[0]) { + case "G1": + test(new GCBeanDescription("G1 Young Generation", new String[] {"G1 Eden Space", "G1 Survivor Space"}), + new GCBeanDescription("G1 Old Generation", new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"})); + break; + case "CMS": + test(new GCBeanDescription("ParNew", new String[] {"Par Eden Space", "Par Survivor Space"}), + new GCBeanDescription("ConcurrentMarkSweep", new String[] {"Par Eden Space", "Par Survivor Space", "CMS Old Gen"})); + break; + case "Parallel": + test(new GCBeanDescription("PS Scavenge", new String[] {"PS Eden Space", "PS Survivor Space"}), + new GCBeanDescription("PS MarkSweep", new String[] {"PS Eden Space", "PS Survivor Space", "PS Old Gen"})); + break; + case "Serial": + test(new GCBeanDescription("Copy", new String[] {"Eden Space", "Survivor Space"}), + new GCBeanDescription("MarkSweepCompact", new String[] {"Eden Space", "Survivor Space", "Tenured Gen"})); + break; + default: + assertTrue(false); + break; + + } + } +} From 6dc1d8c06d98e127b022886172e16b90bf357c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20=C3=96sterlund?= <eosterlund@openjdk.org> Date: Thu, 30 Nov 2017 20:37:20 +0100 Subject: [PATCH 095/165] 8186787: clang-4.0 SIGSEGV in Unsafe_PutByte Reviewed-by: coleenp, dholmes, aph, dsamersoff, kbarrett --- src/hotspot/share/prims/unsafe.cpp | 41 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 455eaaf5873..a8d13138507 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -146,18 +146,25 @@ jlong Unsafe_field_offset_from_byte_offset(jlong byte_offset) { * Normalizes values and wraps accesses in * JavaThread::doing_unsafe_access() if needed. */ +template <typename T> class MemoryAccess : StackObj { JavaThread* _thread; oop _obj; ptrdiff_t _offset; - // Resolves and returns the address of the memory access - void* addr() { - return index_oop_from_field_offset_long(_obj, _offset); + // Resolves and returns the address of the memory access. + // This raw memory access may fault, so we make sure it happens within the + // guarded scope by making the access volatile at least. Since the store + // of Thread::set_doing_unsafe_access() is also volatile, these accesses + // can not be reordered by the compiler. Therefore, if the access triggers + // a fault, we will know that Thread::doing_unsafe_access() returns true. + volatile T* addr() { + void* addr = index_oop_from_field_offset_long(_obj, _offset); + return static_cast<volatile T*>(addr); } - template <typename T> - T normalize_for_write(T x) { + template <typename U> + U normalize_for_write(U x) { return x; } @@ -165,8 +172,8 @@ class MemoryAccess : StackObj { return x & 1; } - template <typename T> - T normalize_for_read(T x) { + template <typename U> + U normalize_for_read(U x) { return x; } @@ -199,11 +206,10 @@ public: assert_field_offset_sane(_obj, offset); } - template <typename T> T get() { if (oopDesc::is_null(_obj)) { GuardUnsafeAccess guard(_thread); - T ret = RawAccess<>::load((T*)addr()); + T ret = RawAccess<>::load(addr()); return normalize_for_read(ret); } else { T ret = HeapAccess<>::load_at(_obj, _offset); @@ -211,22 +217,20 @@ public: } } - template <typename T> void put(T x) { if (oopDesc::is_null(_obj)) { GuardUnsafeAccess guard(_thread); - RawAccess<>::store((T*)addr(), normalize_for_write(x)); + RawAccess<>::store(addr(), normalize_for_write(x)); } else { HeapAccess<>::store_at(_obj, _offset, normalize_for_write(x)); } } - template <typename T> T get_volatile() { if (oopDesc::is_null(_obj)) { GuardUnsafeAccess guard(_thread); - volatile T ret = RawAccess<MO_SEQ_CST>::load((volatile T*)addr()); + volatile T ret = RawAccess<MO_SEQ_CST>::load(addr()); return normalize_for_read(ret); } else { T ret = HeapAccess<MO_SEQ_CST>::load_at(_obj, _offset); @@ -234,11 +238,10 @@ public: } } - template <typename T> void put_volatile(T x) { if (oopDesc::is_null(_obj)) { GuardUnsafeAccess guard(_thread); - RawAccess<MO_SEQ_CST>::store((volatile T*)addr(), normalize_for_write(x)); + RawAccess<MO_SEQ_CST>::store(addr(), normalize_for_write(x)); } else { HeapAccess<MO_SEQ_CST>::store_at(_obj, _offset, normalize_for_write(x)); } @@ -296,11 +299,11 @@ UNSAFE_LEAF(jint, Unsafe_unalignedAccess0(JNIEnv *env, jobject unsafe)) { #define DEFINE_GETSETOOP(java_type, Type) \ \ UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ - return MemoryAccess(thread, obj, offset).get<java_type>(); \ + return MemoryAccess<java_type>(thread, obj, offset).get(); \ } UNSAFE_END \ \ UNSAFE_ENTRY(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ - MemoryAccess(thread, obj, offset).put<java_type>(x); \ + MemoryAccess<java_type>(thread, obj, offset).put(x); \ } UNSAFE_END \ \ // END DEFINE_GETSETOOP. @@ -319,11 +322,11 @@ DEFINE_GETSETOOP(jdouble, Double); #define DEFINE_GETSETOOP_VOLATILE(java_type, Type) \ \ UNSAFE_ENTRY(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \ - return MemoryAccess(thread, obj, offset).get_volatile<java_type>(); \ + return MemoryAccess<java_type>(thread, obj, offset).get_volatile(); \ } UNSAFE_END \ \ UNSAFE_ENTRY(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \ - MemoryAccess(thread, obj, offset).put_volatile<java_type>(x); \ + MemoryAccess<java_type>(thread, obj, offset).put_volatile(x); \ } UNSAFE_END \ \ // END DEFINE_GETSETOOP_VOLATILE. From d470bc0546a92f913e1ba47b9bf8a89c2e80a268 Mon Sep 17 00:00:00 2001 From: Eric Caspole <ecaspole@openjdk.org> Date: Thu, 30 Nov 2017 16:00:34 -0500 Subject: [PATCH 096/165] 8192821: Make LogCompilation into a maven project Add a maven project setup while preserving make build. Reviewed-by: kvn --- src/utils/LogCompilation/Makefile | 2 +- src/utils/LogCompilation/README | 7 ++ src/utils/LogCompilation/pom.xml | 110 ++++++++++++++++++ .../hotspot/tools/compiler/BasicLogEvent.java | 0 .../sun/hotspot/tools/compiler/CallSite.java | 0 .../hotspot/tools/compiler/Compilation.java | 0 .../sun/hotspot/tools/compiler/Constants.java | 0 .../tools/compiler/LogCleanupReader.java | 0 .../tools/compiler/LogCompilation.java | 0 .../sun/hotspot/tools/compiler/LogEvent.java | 0 .../sun/hotspot/tools/compiler/LogParser.java | 8 +- .../tools/compiler/MakeNotEntrantEvent.java | 0 .../sun/hotspot/tools/compiler/Method.java | 0 .../sun/hotspot/tools/compiler/NMethod.java | 0 .../com/sun/hotspot/tools/compiler/Phase.java | 0 .../hotspot/tools/compiler/UncommonTrap.java | 0 .../tools/compiler/UncommonTrapEvent.java | 0 17 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 src/utils/LogCompilation/pom.xml rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/BasicLogEvent.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/CallSite.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/Compilation.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/Constants.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/LogCleanupReader.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/LogCompilation.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/LogEvent.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/LogParser.java (99%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/Method.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/NMethod.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/Phase.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/UncommonTrap.java (100%) rename src/utils/LogCompilation/src/{ => main/java}/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java (100%) diff --git a/src/utils/LogCompilation/Makefile b/src/utils/LogCompilation/Makefile index f04795e44b1..5f9ca083842 100644 --- a/src/utils/LogCompilation/Makefile +++ b/src/utils/LogCompilation/Makefile @@ -25,7 +25,7 @@ PKGLIST = \ com.sun.hotspot.tools.compiler #END PKGLIST -FILELIST = com/sun/hotspot/tools/compiler/*.java +FILELIST = main/java/com/sun/hotspot/tools/compiler/*.java ifneq "x$(ALT_BOOTDIR)" "x" BOOTDIR := $(ALT_BOOTDIR) diff --git a/src/utils/LogCompilation/README b/src/utils/LogCompilation/README index 790173120d6..db9b20aec47 100644 --- a/src/utils/LogCompilation/README +++ b/src/utils/LogCompilation/README @@ -16,3 +16,10 @@ More information about the LogCompilation output can be found at https://wiki.openjdk.java.net/display/HotSpot/LogCompilation+overview https://wiki.openjdk.java.net/display/HotSpot/PrintCompilation https://wiki.openjdk.java.net/display/HotSpot/LogCompilation+tool + +The project layout is now for Maven. To build the project with Maven do: + + mvn clean install + +The build also copies the resulting target jar to ./logc.jar for easy +interop with the Makefile build. \ No newline at end of file diff --git a/src/utils/LogCompilation/pom.xml b/src/utils/LogCompilation/pom.xml new file mode 100644 index 00000000000..ca5c3aa0a1f --- /dev/null +++ b/src/utils/LogCompilation/pom.xml @@ -0,0 +1,110 @@ +<!-- + Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - 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. + + - Neither the name of Oracle 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.sun.hotspot.tools.compiler</groupId> + <artifactId>LogCompilation</artifactId> + <packaging>jar</packaging> + <version>1.0-SNAPSHOT</version> + <name>LogCompilation</name> + <url>http://maven.apache.org</url> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.8.2</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>com.sun.hotspot.tools.compiler.LogCompilation</mainClass> + </transformer> + </transformers> + <createDependencyReducedPom>false</createDependencyReducedPom> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <version>2.5.3</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.0.0-M1</version> + </plugin> + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.8</version> + <executions> + <execution> + <id>copy</id> + <phase>package</phase> + <configuration> + <target> + <copy file="${basedir}/target/${artifactId}-${version}.jar" tofile="${basedir}/logc.jar"/> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/BasicLogEvent.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/BasicLogEvent.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/CallSite.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/CallSite.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Constants.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Constants.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Constants.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Constants.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCleanupReader.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCleanupReader.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogEvent.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogEvent.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java similarity index 99% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java index bd98f02b283..85ce379e16f 100644 --- a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java @@ -632,7 +632,7 @@ public class LogParser extends DefaultHandler implements ErrorHandler { /** * Entry point for log file parsing with a file reader. - * {@see #parse(String,boolean)} + * {@link #parse(String,boolean)} */ public static ArrayList<LogEvent> parse(Reader reader, boolean cleanup) throws Exception { // Create the XML input factory @@ -825,7 +825,7 @@ public class LogParser extends DefaultHandler implements ErrorHandler { * an {@linkplain #site initial scope} with a bogus bytecode index and the * right inline ID, and push the scope with the inline ID attached. Note * that most of late inlining processing happens in - * {@link #endElement()}.</li> + * {@link #endElement(String,String,String)}.</li> * <li><b>jvms:</b> record a {@linkplain Jvms JVMState}. Depending on the * context in which this event is encountered, this can mean adding * information to the currently being processed trap, lock elimination, or @@ -1182,11 +1182,11 @@ public class LogParser extends DefaultHandler implements ErrorHandler { * {@code true} here. (It will be reset when parsing the inlined methods is * done; this happens for the successful case in this method as well, when * {@code parse} elements are processed; and for inlining failures, in - * {@link #startElement()}, when {@code inline_fail} elements are + * {@link #startElement(String,String,String,Attributes)}, when {@code inline_fail} elements are * processed.)</li> * <li><b>task:</b> perform cleanup at the end of a compilation. Note that * the explicit {@code task_done} event is handled in - * {@link #startElement()}.</li> + * {@link #startElement(String,String,String,Attributes)}.</li> * </ul> */ @Override diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Method.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Method.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Phase.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Phase.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrap.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrap.java diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java similarity index 100% rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java From 40f06dc0e3e0e29991291f4d2e9ea2b32ce3d83f Mon Sep 17 00:00:00 2001 From: Calvin Cheung <ccheung@openjdk.org> Date: Thu, 30 Nov 2017 14:25:33 -0800 Subject: [PATCH 097/165] 8174101: Bootclasspath append should not invalidate CDS archive Reviewed-by: iklam, jiangli --- .../share/classfile/sharedPathsMiscInfo.cpp | 3 ++- .../SharedArchiveFile/BootAppendTests.java | 27 +++++++++++++++++++ .../runtime/appcds/BootClassPathMismatch.java | 21 +++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp index 06d5a50600b..98a76d1455d 100644 --- a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp +++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp @@ -139,7 +139,8 @@ bool SharedPathsMiscInfo::check() { bool SharedPathsMiscInfo::check(jint type, const char* path) { switch (type) { case BOOT: - if (os::file_name_strcmp(path, Arguments::get_sysclasspath()) != 0) { + // In the future we should perform the check based on the content of the mapped archive. + if (UseAppCDS && os::file_name_strcmp(path, Arguments::get_sysclasspath()) != 0) { return fail("[BOOT classpath mismatch, actual =", Arguments::get_sysclasspath()); } break; diff --git a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java index c6614fa4d31..9cf2534d485 100644 --- a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java +++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java @@ -86,6 +86,9 @@ public class BootAppendTests { logTestCase("5"); testBootAppendClass(); + + logTestCase("6"); + testBootAppendExtraDir(); } private static void logTestCase(String msg) { @@ -241,4 +244,28 @@ public class BootAppendTests { } } } + + // Test #6: This is similar to Test #5. During runtime, an extra dir + // is appended to the bootclasspath. It should not invalidate + // the shared archive. + public static void testBootAppendExtraDir() throws Exception { + for (String mode : modes) { + CDSOptions opts = (new CDSOptions()) + .setXShareMode(mode).setUseVersion(false) + .addPrefix("-Xbootclasspath/a:" + bootAppendJar + File.pathSeparator + appJar, + "-showversion", "--limit-modules=java.base", "-cp", appJar) + .addSuffix("-Xlog:class+load=info", + APP_CLASS, BOOT_APPEND_CLASS_NAME); + + OutputAnalyzer out = CDSTestUtils.runWithArchive(opts); + CDSTestUtils.checkExec(out, opts, "[class,load] nonjdk.myPackage.MyClass"); + + // If CDS is enabled, the nonjdk.myPackage.MyClass should be loaded + // from the shared archive. + if (mode.equals("on")) { + CDSTestUtils.checkExec(out, opts, + "[class,load] nonjdk.myPackage.MyClass source: shared objects file"); + } + } + } } diff --git a/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java index 04e6bb2625e..4e2bbdbce49 100644 --- a/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java +++ b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java @@ -52,6 +52,7 @@ public class BootClassPathMismatch { BootClassPathMismatch test = new BootClassPathMismatch(); test.testBootClassPathMismatch(); + test.testBootClassPathMismatch2(); test.testBootClassPathMatch(); } @@ -78,6 +79,26 @@ public class BootClassPathMismatch { } } + /* Error should be detected if: + * dump time: <no bootclasspath specified> + * run-time : -Xbootclasspath/a:${testdir}/hello.jar + */ + public void testBootClassPathMismatch2() throws Exception { + String appJar = JarBuilder.getOrCreateHelloJar(); + String appClasses[] = {"Hello"}; + OutputAnalyzer dumpOutput = TestCommon.dump(appJar, appClasses); + OutputAnalyzer execOutput = TestCommon.exec( + appJar, "-verbose:class", "-Xbootclasspath/a:" + appJar, "Hello"); + try { + TestCommon.checkExec(execOutput, mismatchMessage); + } catch (java.lang.RuntimeException re) { + String cause = re.getMessage(); + if (!mismatchMessage.equals(cause)) { + throw re; + } + } + } + /* No error if: * dump time: -Xbootclasspath/a:${testdir}/hello.jar * run-time : -Xbootclasspath/a:${testdir}/hello.jar From 15ea766de22e5863df7739de0a82fa47258791c1 Mon Sep 17 00:00:00 2001 From: Dean Long <dlong@openjdk.org> Date: Thu, 30 Nov 2017 10:40:48 -0800 Subject: [PATCH 098/165] 8191437: AOT doesn't work easily after thread local handshakes Reviewed-by: kvn, rehn, aph --- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 15 +++-- src/hotspot/share/aot/aotCodeHeap.cpp | 1 + src/hotspot/share/aot/aotCodeHeap.hpp | 5 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 + src/hotspot/share/runtime/arguments.cpp | 19 ------ .../commandLineFlagConstraintsRuntime.cpp | 4 -- .../jaotc/binformat/BinaryContainer.java | 12 +++- .../src/jdk/tools/jaotc/MarkProcessor.java | 6 ++ .../aarch64/AArch64HotSpotEpilogueOp.java | 12 +++- .../aarch64/AArch64HotSpotLIRGenerator.java | 5 +- .../aarch64/AArch64HotSpotNodeLIRBuilder.java | 5 +- .../aarch64/AArch64HotSpotReturnOp.java | 7 +- .../aarch64/AArch64HotSpotSafepointOp.java | 29 ++++++-- .../amd64/AMD64HotSpotLIRGenerator.java | 3 +- .../amd64/AMD64HotSpotNodeLIRBuilder.java | 5 +- .../hotspot/amd64/AMD64HotSpotReturnOp.java | 8 ++- .../amd64/AMD64HotSpotSafepointOp.java | 34 ++++++++-- .../sparc/SPARCHotSpotLIRGenerator.java | 5 +- .../sparc/SPARCHotSpotNodeLIRBuilder.java | 9 ++- .../hotspot/sparc/SPARCHotSpotReturnOp.java | 12 ++-- .../sparc/SPARCHotSpotSafepointOp.java | 66 +++++++++++++++++-- .../hotspot/GraalHotSpotVMConfig.java | 4 +- .../handshake/HandshakeTransitionTest.java | 9 ++- 23 files changed, 201 insertions(+), 75 deletions(-) diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index cf85d5807a2..3f0d57bcce0 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -3388,26 +3388,29 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // No exception case __ bind(noException); - Label no_adjust, bail; + Label no_adjust, bail, no_prefix; if (SafepointMechanism::uses_thread_local_poll() && !cause_return) { // If our stashed return pc was modified by the runtime we avoid touching it __ cmpptr(rbx, Address(rbp, wordSize)); __ jccb(Assembler::notEqual, no_adjust); -#ifdef ASSERT // Verify the correct encoding of the poll we're about to skip. // See NativeInstruction::is_safepoint_poll() __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_rex_b_prefix); - __ jcc(Assembler::notEqual, bail); - __ cmpb(Address(rbx, 1), NativeTstRegMem::instruction_code_memXregl); + __ jcc(Assembler::notEqual, no_prefix); + __ addptr(rbx, 1); + __ bind(no_prefix); +#ifdef ASSERT + __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_code_memXregl); __ jcc(Assembler::notEqual, bail); // Mask out the modrm bits - __ testb(Address(rbx, 2), NativeTstRegMem::modrm_mask); + __ testb(Address(rbx, 1), NativeTstRegMem::modrm_mask); // rax encodes to 0, so if the bits are nonzero it's incorrect __ jcc(Assembler::notZero, bail); #endif // Adjust return pc forward to step over the safepoint poll instruction - __ addptr(Address(rbp, wordSize), 3); + __ addptr(rbx, 2); + __ movptr(Address(rbp, wordSize), rbx); } __ bind(no_adjust); diff --git a/src/hotspot/share/aot/aotCodeHeap.cpp b/src/hotspot/share/aot/aotCodeHeap.cpp index 2a9b3982cd2..e22e228acf1 100644 --- a/src/hotspot/share/aot/aotCodeHeap.cpp +++ b/src/hotspot/share/aot/aotCodeHeap.cpp @@ -167,6 +167,7 @@ void AOTLib::verify_config() { verify_flag(_config->_compactFields, CompactFields, "CompactFields"); verify_flag(_config->_enableContended, EnableContended, "EnableContended"); verify_flag(_config->_restrictContended, RestrictContended, "RestrictContended"); + verify_flag(_config->_threadLocalHandshakes, ThreadLocalHandshakes, "ThreadLocalHandshakes"); if (!TieredCompilation && _config->_tieredAOT) { handle_config_error("Shared file %s error: Expected to run with tiered compilation on", _name); diff --git a/src/hotspot/share/aot/aotCodeHeap.hpp b/src/hotspot/share/aot/aotCodeHeap.hpp index 7bfd5c67531..ae6e9ff2556 100644 --- a/src/hotspot/share/aot/aotCodeHeap.hpp +++ b/src/hotspot/share/aot/aotCodeHeap.hpp @@ -92,7 +92,7 @@ typedef struct { } AOTHeader; typedef struct { - enum { CONFIG_SIZE = 7 * jintSize + 11 }; + enum { CONFIG_SIZE = 7 * jintSize + 12 }; // 7 int values int _config_size; int _narrowOopShift; @@ -101,7 +101,7 @@ typedef struct { int _fieldsAllocationStyle; int _objectAlignment; int _codeSegmentSize; - // byte[11] array map to boolean values here + // byte[12] array map to boolean values here bool _debug_VM; bool _useCompressedOops; bool _useCompressedClassPointers; @@ -113,6 +113,7 @@ typedef struct { bool _enableContended; bool _restrictContended; bool _omitAssertions; + bool _threadLocalHandshakes; } AOTConfiguration; class AOTLib : public CHeapObj<mtCode> { diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 66a9d859ed0..0f4ffa27113 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -317,6 +317,7 @@ \ nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \ nonstatic_field(Thread, _allocated_bytes, jlong) \ + nonstatic_field(Thread, _polling_page, address) \ \ nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \ diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 0f5a32717e6..2540390b5c9 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -4397,26 +4397,7 @@ jint Arguments::apply_ergo() { } #endif - bool aot_enabled = UseAOT && AOTLibrary != NULL; - bool jvmci_enabled = NOT_JVMCI(false) JVMCI_ONLY(EnableJVMCI || UseJVMCICompiler); - bool handshakes_supported = SafepointMechanism::supports_thread_local_poll() && !aot_enabled && !jvmci_enabled && ThreadLocalHandshakes; // ThreadLocalHandshakesConstraintFunc handles the constraints. - // Here we try to figure out if a mutual exclusive option have been set that conflict with a default. - if (handshakes_supported) { - FLAG_SET_DEFAULT(UseAOT, false); // Clear the AOT flag to make sure it doesn't try to initialize. - } else { - if (FLAG_IS_DEFAULT(ThreadLocalHandshakes) && ThreadLocalHandshakes) { - if (aot_enabled) { - // If user enabled AOT but ThreadLocalHandshakes is at default set it to false. - log_debug(ergo)("Disabling ThreadLocalHandshakes for UseAOT."); - FLAG_SET_DEFAULT(ThreadLocalHandshakes, false); - } else if (jvmci_enabled){ - // If user enabled JVMCI but ThreadLocalHandshakes is at default set it to false. - log_debug(ergo)("Disabling ThreadLocalHandshakes for EnableJVMCI/UseJVMCICompiler."); - FLAG_SET_DEFAULT(ThreadLocalHandshakes, false); - } - } - } if (FLAG_IS_DEFAULT(ThreadLocalHandshakes) || !SafepointMechanism::supports_thread_local_poll()) { log_debug(ergo)("ThreadLocalHandshakes %s", ThreadLocalHandshakes ? "enabled." : "disabled."); } else { diff --git a/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp index 9df5d2bbec8..55483983094 100644 --- a/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp +++ b/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp @@ -138,10 +138,6 @@ Flag::Error ThreadLocalHandshakesConstraintFunc(bool value, bool verbose) { CommandLineError::print(verbose, "ThreadLocalHandshakes not yet supported on this platform\n"); return Flag::VIOLATES_CONSTRAINT; } - if (UseAOT JVMCI_ONLY(|| EnableJVMCI || UseJVMCICompiler)) { - CommandLineError::print(verbose, "ThreadLocalHandshakes not yet supported in combination with AOT or JVMCI\n"); - return Flag::VIOLATES_CONSTRAINT; - } } return Flag::SUCCESS; } diff --git a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java index 0971479577e..b0e8799559f 100644 --- a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java +++ b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java @@ -65,6 +65,8 @@ public final class BinaryContainer implements SymbolTable { private final int codeEntryAlignment; + private final boolean threadLocalHandshakes; + /** * Container holding code bits and any other related information. */ @@ -279,6 +281,8 @@ public final class BinaryContainer implements SymbolTable { this.codeEntryAlignment = graalHotSpotVMConfig.codeEntryAlignment; + this.threadLocalHandshakes = graalHotSpotVMConfig.threadLocalHandshakes; + // Section unique name is limited to 8 characters due to limitation on Windows. // Name could be longer but only first 8 characters are stored on Windows. @@ -323,7 +327,8 @@ public final class BinaryContainer implements SymbolTable { TieredAOT.getValue(graalOptions), graalHotSpotVMConfig.enableContended, graalHotSpotVMConfig.restrictContended, - graphBuilderConfig.omitAssertions() + graphBuilderConfig.omitAssertions(), + graalHotSpotVMConfig.threadLocalHandshakes }; int[] intFlags = { graalHotSpotVMConfig.getOopEncoding().getShift(), @@ -434,6 +439,11 @@ public final class BinaryContainer implements SymbolTable { return codeEntryAlignment; } + public boolean getThreadLocalHandshakes() { + return threadLocalHandshakes; + } + + /** * Gets the global AOT symbol associated with the function name. * diff --git a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java index 397d86e7378..16d5dadffc5 100644 --- a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java +++ b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java @@ -45,6 +45,7 @@ final class MarkProcessor { * @param methodInfo compiled method info * @param mark mark being processed */ + @SuppressWarnings("fallthrough") void process(CompiledMethodInfo methodInfo, Mark mark) { MarkId markId = MarkId.getEnum((int) mark.id); switch (markId) { @@ -53,6 +54,11 @@ final class MarkProcessor { break; case POLL_FAR: case POLL_RETURN_FAR: + if (binaryContainer.getThreadLocalHandshakes()) { + // skip relocation + break; + } + // fallthrough case CARD_TABLE_ADDRESS: case HEAP_TOP_ADDRESS: case HEAP_END_ADDRESS: diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java index 79be3d65623..182f6f4c5bd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -37,10 +37,18 @@ import jdk.vm.ci.code.Register; abstract class AArch64HotSpotEpilogueOp extends AArch64BlockEndOp { private final GraalHotSpotVMConfig config; + private final Register thread; + + protected AArch64HotSpotEpilogueOp(LIRInstructionClass<? extends AArch64HotSpotEpilogueOp> c, GraalHotSpotVMConfig config, Register thread) { + super(c); + this.config = config; + this.thread = thread; + } protected AArch64HotSpotEpilogueOp(LIRInstructionClass<? extends AArch64HotSpotEpilogueOp> c, GraalHotSpotVMConfig config) { super(c); this.config = config; + this.thread = null; // no safepoint } protected void leaveFrame(CompilationResultBuilder crb, AArch64MacroAssembler masm, boolean emitSafepoint) { @@ -49,7 +57,7 @@ abstract class AArch64HotSpotEpilogueOp extends AArch64BlockEndOp { if (emitSafepoint) { try (ScratchRegister sc = masm.getScratchRegister()) { Register scratch = sc.getRegister(); - AArch64HotSpotSafepointOp.emitCode(crb, masm, config, true, scratch, null); + AArch64HotSpotSafepointOp.emitCode(crb, masm, config, true, thread, scratch, null); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java index 1803ea3adbb..2ed6e3408c0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -342,7 +342,8 @@ public class AArch64HotSpotLIRGenerator extends AArch64LIRGenerator implements H operand = resultOperandFor(kind, input.getValueKind()); emitMove(operand, input); } - append(new AArch64HotSpotReturnOp(operand, getStub() != null, config)); + Register thread = getProviders().getRegisters().getThreadRegister(); + append(new AArch64HotSpotReturnOp(operand, getStub() != null, config, thread)); } /** diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java index 7865e886a87..5527d764061 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -118,8 +118,9 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen @Override public void visitSafepointNode(SafepointNode i) { LIRFrameState info = state(i); + Register thread = getGen().getProviders().getRegisters().getThreadRegister(); Variable scratch = gen.newVariable(LIRKind.value(getGen().target().arch.getWordKind())); - append(new AArch64HotSpotSafepointOp(info, getGen().config, scratch)); + append(new AArch64HotSpotSafepointOp(info, getGen().config, thread, scratch)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java index 283e3fa7cf2..3065f491c6d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; +import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.Value; /** @@ -46,8 +47,8 @@ public final class AArch64HotSpotReturnOp extends AArch64HotSpotEpilogueOp { @Use({REG, ILLEGAL}) private Value result; private final boolean isStub; - public AArch64HotSpotReturnOp(Value result, boolean isStub, GraalHotSpotVMConfig config) { - super(TYPE, config); + public AArch64HotSpotReturnOp(Value result, boolean isStub, GraalHotSpotVMConfig config, Register thread) { + super(TYPE, config, thread); assert validReturnValue(result); this.result = result; this.isStub = isStub; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java index a2023faf776..f075f28bf8b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -50,18 +50,20 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction { @Temp protected AllocatableValue scratchValue; private final GraalHotSpotVMConfig config; + private final Register thread; - public AArch64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, AllocatableValue scratch) { + public AArch64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, Register thread, AllocatableValue scratch) { super(TYPE); this.state = state; this.config = config; + this.thread = thread; this.scratchValue = scratch; } @Override public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { Register scratch = asRegister(scratchValue); - emitCode(crb, masm, config, false, scratch, state); + emitCode(crb, masm, config, false, thread, scratch, state); } /** @@ -76,7 +78,15 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction { return !NumUtil.isSignedNbit(21, pollingPageAddress - config.codeCacheLowBound) || !NumUtil.isSignedNbit(21, pollingPageAddress - config.codeCacheHighBound); } - public static void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register scratch, LIRFrameState state) { + public static void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register thread, Register scratch, LIRFrameState state) { + if (config.threadLocalHandshakes) { + emitThreadLocalPoll(crb, masm, config, onReturn, thread, scratch, state); + } else { + emitGlobalPoll(crb, masm, config, onReturn, scratch, state); + } + } + + private static void emitGlobalPoll(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register scratch, LIRFrameState state) { if (isPollingPageFar(config)) { crb.recordMark(onReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR); masm.movNativeAddress(scratch, config.safepointPollingAddress); @@ -94,4 +104,15 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction { } } + private static void emitThreadLocalPoll(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register thread, Register scratch, + LIRFrameState state) { + assert config.threadPollingPageOffset >= 0; + masm.ldr(64, scratch, masm.makeAddress(thread, config.threadPollingPageOffset, 8)); + crb.recordMark(onReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR); + if (state != null) { + crb.recordInfopoint(masm.position(), state, InfopointReason.SAFEPOINT); + } + masm.ldr(32, zr, AArch64Address.createBaseRegisterOnlyAddress(scratch)); + } + } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java index 3a26ff997a7..9082a2bc874 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java @@ -264,7 +264,8 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp if (pollOnReturnScratchRegister == null) { pollOnReturnScratchRegister = findPollOnReturnScratchRegister(); } - append(new AMD64HotSpotReturnOp(operand, getStub() != null, pollOnReturnScratchRegister, config)); + Register thread = getProviders().getRegisters().getThreadRegister(); + append(new AMD64HotSpotReturnOp(operand, getStub() != null, thread, pollOnReturnScratchRegister, config)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java index 93a2c360fa8..4ce46a5f5fc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -121,7 +121,8 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H @Override public void visitSafepointNode(SafepointNode i) { LIRFrameState info = state(i); - append(new AMD64HotSpotSafepointOp(info, getGen().config, this)); + Register thread = getGen().getProviders().getRegisters().getThreadRegister(); + append(new AMD64HotSpotSafepointOp(info, getGen().config, this, thread)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java index ffcdc620df1..3428e278828 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -45,13 +45,15 @@ final class AMD64HotSpotReturnOp extends AMD64HotSpotEpilogueBlockEndOp implemen public static final LIRInstructionClass<AMD64HotSpotReturnOp> TYPE = LIRInstructionClass.create(AMD64HotSpotReturnOp.class); @Use({REG, ILLEGAL}) protected Value value; private final boolean isStub; + private final Register thread; private final Register scratchForSafepointOnReturn; private final GraalHotSpotVMConfig config; - AMD64HotSpotReturnOp(Value value, boolean isStub, Register scratchForSafepointOnReturn, GraalHotSpotVMConfig config) { + AMD64HotSpotReturnOp(Value value, boolean isStub, Register thread, Register scratchForSafepointOnReturn, GraalHotSpotVMConfig config) { super(TYPE); this.value = value; this.isStub = isStub; + this.thread = thread; this.scratchForSafepointOnReturn = scratchForSafepointOnReturn; this.config = config; } @@ -61,7 +63,7 @@ final class AMD64HotSpotReturnOp extends AMD64HotSpotEpilogueBlockEndOp implemen leaveFrameAndRestoreRbp(crb, masm); if (!isStub) { // Every non-stub compile method must have a poll before the return. - AMD64HotSpotSafepointOp.emitCode(crb, masm, config, true, null, scratchForSafepointOnReturn); + AMD64HotSpotSafepointOp.emitCode(crb, masm, config, true, null, thread, scratchForSafepointOnReturn); /* * We potentially return to the interpreter, and that's an AVX-SSE transition. The only diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java index 7a5ee813a89..59ae1546014 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -58,12 +58,14 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { @Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp; private final GraalHotSpotVMConfig config; + private final Register thread; - public AMD64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, NodeLIRBuilderTool tool) { + public AMD64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, NodeLIRBuilderTool tool, Register thread) { super(TYPE); this.state = state; this.config = config; - if (isPollingPageFar(config) || ImmutableCode.getValue(tool.getOptions())) { + this.thread = thread; + if (config.threadLocalHandshakes || isPollingPageFar(config) || ImmutableCode.getValue(tool.getOptions())) { temp = tool.getLIRGeneratorTool().newVariable(LIRKind.value(tool.getLIRGeneratorTool().target().arch.getWordKind())); } else { // Don't waste a register if it's unneeded @@ -73,7 +75,15 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) { - emitCode(crb, asm, config, false, state, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null); + emitCode(crb, asm, config, false, state, thread, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null); + } + + public static void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread, Register scratch) { + if (config.threadLocalHandshakes) { + emitThreadLocalPoll(crb, asm, config, atReturn, state, thread, scratch); + } else { + emitGlobalPoll(crb, asm, config, atReturn, state, scratch); + } } /** @@ -85,7 +95,7 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { return config.forceUnreachable || !isInt(pollingPageAddress - config.codeCacheLowBound) || !isInt(pollingPageAddress - config.codeCacheHighBound); } - public static void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register scratch) { + private static void emitGlobalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register scratch) { assert !atReturn || state == null : "state is unneeded at return"; if (ImmutableCode.getValue(crb.getOptions())) { JavaKind hostWordKind = JavaKind.Long; @@ -123,4 +133,18 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { asm.testl(rax, new AMD64Address(rip, 0)); } } + + private static void emitThreadLocalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread, + Register scratch) { + assert !atReturn || state == null : "state is unneeded at return"; + + assert config.threadPollingPageOffset >= 0; + asm.movptr(scratch, new AMD64Address(thread, config.threadPollingPageOffset)); + crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR); + final int pos = asm.position(); + if (state != null) { + crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT); + } + asm.testl(rax, new AMD64Address(scratch)); + } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java index 883a91dd2fe..793935718a5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java @@ -190,7 +190,8 @@ public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSp operand = resultOperandFor(javaKind, input.getValueKind()); emitMove(operand, input); } - append(new SPARCHotSpotReturnOp(operand, getStub() != null, config, getSafepointAddressValue())); + Register thread = getProviders().getRegisters().getThreadRegister(); + append(new SPARCHotSpotReturnOp(operand, getStub() != null, config, thread, getSafepointAddressValue())); } @Override @@ -383,7 +384,7 @@ public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSp public AllocatableValue getSafepointAddressValue() { if (this.safepointAddressValue == null) { - this.safepointAddressValue = newVariable(LIRKind.value(target().arch.getWordKind())); + this.safepointAddressValue = SPARCHotSpotSafepointOp.getSafepointAddressValue(this); } return this.safepointAddressValue; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java index 09dc9cf5f42..2e94bbadafc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017, 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 @@ -84,7 +84,8 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H @Override public void visitSafepointNode(SafepointNode i) { LIRFrameState info = state(i); - append(new SPARCHotSpotSafepointOp(info, getGen().config, gen)); + Register thread = getGen().getProviders().getRegisters().getThreadRegister(); + append(new SPARCHotSpotSafepointOp(info, getGen().config, thread, gen)); } @Override @@ -141,9 +142,7 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H @Override protected void emitPrologue(StructuredGraph graph) { super.emitPrologue(graph); - AllocatableValue var = getGen().getSafepointAddressValue(); - append(new SPARCHotSpotSafepointOp.SPARCLoadSafepointPollAddress(var, getGen().config)); - getGen().append(((HotSpotDebugInfoBuilder) getDebugInfoBuilder()).lockStack()); + SPARCHotSpotSafepointOp.emitPrologue(this, getGen()); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java index 134da9043f8..f27d72fd136 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -24,7 +24,6 @@ package org.graalvm.compiler.hotspot.sparc; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL; import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; -import static jdk.vm.ci.code.ValueUtil.asRegister; import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; @@ -33,6 +32,7 @@ import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; import org.graalvm.compiler.lir.sparc.SPARCControlFlow.ReturnOp; +import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.Value; /** @@ -44,15 +44,17 @@ final class SPARCHotSpotReturnOp extends SPARCHotSpotEpilogueOp { public static final SizeEstimate SIZE = SizeEstimate.create(2); @Use({REG, ILLEGAL}) protected Value value; - @Use({REG}) protected Value safepointPollAddress; + @Use({REG, ILLEGAL}) protected Value safepointPollAddress; private final boolean isStub; private final GraalHotSpotVMConfig config; + private final Register thread; - SPARCHotSpotReturnOp(Value value, boolean isStub, GraalHotSpotVMConfig config, Value safepointPoll) { + SPARCHotSpotReturnOp(Value value, boolean isStub, GraalHotSpotVMConfig config, Register thread, Value safepointPoll) { super(TYPE, SIZE); this.value = value; this.isStub = isStub; this.config = config; + this.thread = thread; this.safepointPollAddress = safepointPoll; } @@ -60,7 +62,7 @@ final class SPARCHotSpotReturnOp extends SPARCHotSpotEpilogueOp { public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { if (!isStub) { // Every non-stub compile method must have a poll before the return. - SPARCHotSpotSafepointOp.emitCode(crb, masm, config, true, null, asRegister(safepointPollAddress)); + SPARCHotSpotSafepointOp.emitCode(crb, masm, config, true, null, thread, safepointPollAddress); } ReturnOp.emitCodeHelper(crb, masm); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java index 4b0bf193314..b50dabf883e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -27,7 +27,10 @@ import static jdk.vm.ci.sparc.SPARC.g0; import org.graalvm.compiler.asm.sparc.SPARCAddress; import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler; +import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister; +import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; +import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder; import org.graalvm.compiler.lir.LIRFrameState; import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; @@ -39,6 +42,7 @@ import jdk.vm.ci.code.Register; import jdk.vm.ci.code.ValueUtil; import jdk.vm.ci.code.site.InfopointReason; import jdk.vm.ci.meta.AllocatableValue; +import jdk.vm.ci.meta.Value; /** * Emits a safepoint poll. @@ -49,23 +53,37 @@ public class SPARCHotSpotSafepointOp extends SPARCLIRInstruction { public static final SizeEstimate SIZE = SizeEstimate.create(9); @State protected LIRFrameState state; - @Use({OperandFlag.REG}) AllocatableValue safepointPollAddress; + @Use({OperandFlag.REG, OperandFlag.ILLEGAL}) AllocatableValue safepointPollAddress; private final GraalHotSpotVMConfig config; + private final Register thread; - public SPARCHotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, LIRGeneratorTool tool) { + public SPARCHotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, Register thread, LIRGeneratorTool tool) { super(TYPE, SIZE); this.state = state; this.config = config; + this.thread = thread; SPARCHotSpotLIRGenerator lirGen = (SPARCHotSpotLIRGenerator) tool; safepointPollAddress = lirGen.getSafepointAddressValue(); } @Override public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) { - emitCode(crb, masm, config, false, state, asRegister(safepointPollAddress)); + emitCode(crb, masm, config, false, state, thread, safepointPollAddress); } - public static void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register safepointPollAddress) { + public static void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread, + Value safepointPollAddress) { + if (config.threadLocalHandshakes) { + emitThreadLocalPoll(crb, masm, config, atReturn, state, thread); + } else { + emitGlobalPoll(crb, masm, config, atReturn, state, asRegister(safepointPollAddress)); + } + } + + /** + * Emit a global safepoint poll. + */ + private static void emitGlobalPoll(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register safepointPollAddress) { crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR); if (state != null) { final int pos = masm.position(); @@ -74,6 +92,44 @@ public class SPARCHotSpotSafepointOp extends SPARCLIRInstruction { masm.ldx(new SPARCAddress(safepointPollAddress, 0), g0); } + /** + * Emit a thread-local safepoint poll. + */ + private static void emitThreadLocalPoll(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread) { + assert !atReturn || state == null : "state is unneeded at return"; + + assert config.threadPollingPageOffset >= 0; + + try (ScratchRegister scratchReg = masm.getScratchRegister()) { + Register scratch = scratchReg.getRegister(); + + masm.ldx(new SPARCAddress(thread, config.threadPollingPageOffset), scratch); + + crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR); + if (state != null) { + final int pos = masm.position(); + crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT); + } + masm.ldx(new SPARCAddress(scratch, 0), g0); + } + } + + static AllocatableValue getSafepointAddressValue(SPARCHotSpotLIRGenerator gen) { + if (gen.config.threadLocalHandshakes) { + return Value.ILLEGAL; + } else { + return gen.newVariable(LIRKind.value(gen.target().arch.getWordKind())); + } + } + + static void emitPrologue(SPARCHotSpotNodeLIRBuilder lir, SPARCHotSpotLIRGenerator gen) { + if (!gen.config.threadLocalHandshakes) { + AllocatableValue var = gen.getSafepointAddressValue(); + lir.append(new SPARCHotSpotSafepointOp.SPARCLoadSafepointPollAddress(var, gen.config)); + gen.append(((HotSpotDebugInfoBuilder) lir.getDebugInfoBuilder()).lockStack()); + } + } + public static class SPARCLoadSafepointPollAddress extends SPARCLIRInstruction { public static final LIRInstructionClass<SPARCLoadSafepointPollAddress> TYPE = LIRInstructionClass.create(SPARCLoadSafepointPollAddress.class); public static final SizeEstimate SIZE = SizeEstimate.create(2); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java index 15e709c3056..97c0c281bc4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -165,6 +165,7 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess { public final boolean usePopCountInstruction = getFlag("UsePopCountInstruction", Boolean.class); public final boolean useAESIntrinsics = getFlag("UseAESIntrinsics", Boolean.class); public final boolean useCRC32Intrinsics = getFlag("UseCRC32Intrinsics", Boolean.class); + public final boolean threadLocalHandshakes = getFlag("ThreadLocalHandshakes", Boolean.class, false); private final boolean useMultiplyToLenIntrinsic = getFlag("UseMultiplyToLenIntrinsic", Boolean.class); private final boolean useSHA1Intrinsics = getFlag("UseSHA1Intrinsics", Boolean.class); @@ -594,6 +595,7 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess { public final int basicLockSize = getFieldValue("CompilerToVM::Data::sizeof_BasicLock", Integer.class, "int"); public final int basicLockDisplacedHeaderOffset = getFieldOffset("BasicLock::_displaced_header", Integer.class, "markOop"); + public final int threadPollingPageOffset = getFieldOffset("Thread::_polling_page", Integer.class, "address", -1); public final int threadAllocatedBytesOffset = getFieldOffset("Thread::_allocated_bytes", Integer.class, "jlong"); public final int tlabRefillWasteIncrement = getFlag("TLABWasteIncrement", Integer.class); diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java index 4c9194e2504..3bcac9bbdf1 100644 --- a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java +++ b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java @@ -29,13 +29,17 @@ import java.time.Duration; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; +import sun.hotspot.WhiteBox; + /* * @test HandshakeTransitionTest * @summary This does a sanity test of the poll in the native wrapper. * @requires vm.debug * @library /testlibrary /test/lib * @build HandshakeTransitionTest - * @run main/native HandshakeTransitionTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/native -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeTransitionTest */ public class HandshakeTransitionTest { @@ -44,6 +48,7 @@ public class HandshakeTransitionTest { public static void main(String[] args) throws Exception { String lib = System.getProperty("test.nativepath"); + WhiteBox wb = WhiteBox.getWhiteBox(); ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( true, @@ -54,6 +59,8 @@ public class HandshakeTransitionTest { "-XX:ParallelGCThreads=1", "-XX:ConcGCThreads=1", "-XX:CICompilerCount=2", + "-XX:+UnlockExperimentalVMOptions", + (wb.getBooleanVMFlag("UseJVMCICompiler") ? "-XX:+UseJVMCICompiler" : "-XX:-UseJVMCICompiler"), "HandshakeTransitionTest$Test"); From b468df82f91bbc2d047f6abf5690750bad8dd4f8 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" <dcubed@openjdk.org> Date: Thu, 30 Nov 2017 18:38:20 -0500 Subject: [PATCH 099/165] 8191787: move private inline functions from thread.inline.hpp -> thread.cpp Reviewed-by: coleenp, dholmes --- src/hotspot/share/runtime/thread.cpp | 30 ++++++++++++++++ src/hotspot/share/runtime/thread.hpp | 38 ++++++++++----------- src/hotspot/share/runtime/thread.inline.hpp | 34 ++---------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 097f453854b..d4191b0d89f 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -3561,6 +3561,10 @@ static inline void *prefetch_and_load_ptr(void **addr, intx prefetch_interval) { MACRO_current_p++, \ X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval)) +inline ThreadsList* Threads::get_smr_java_thread_list() { + return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list); +} + // All JavaThreads #define ALL_JAVA_THREADS(X) DO_JAVA_THREADS(get_smr_java_thread_list(), X) @@ -3772,6 +3776,14 @@ ThreadsList *Threads::acquire_stable_list_nested_path(Thread *self) { return node->t_list(); } +inline void Threads::add_smr_deleted_thread_times(uint add_value) { + Atomic::add(add_value, &_smr_deleted_thread_times); +} + +inline void Threads::inc_smr_deleted_thread_cnt() { + Atomic::inc(&_smr_deleted_thread_cnt); +} + // Release a stable ThreadsList. // void Threads::release_stable_list(Thread *self) { @@ -3871,6 +3883,24 @@ void Threads::release_stable_list_wake_up(char *log_str) { } } +inline void Threads::update_smr_deleted_thread_time_max(uint new_value) { + while (true) { + uint cur_value = _smr_deleted_thread_time_max; + if (new_value <= cur_value) { + // No need to update max value so we're done. + break; + } + if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) { + // Updated max value so we're done. Otherwise try it all again. + break; + } + } +} + +inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) { + return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list); +} + void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) { TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime)); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index df8e81d3776..db1d465c751 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -2127,19 +2127,22 @@ class Threads: AllStatic { friend class VMStructs; private: // Safe Memory Reclamation (SMR) support: + // The coordination between Threads::release_stable_list() and + // Threads::smr_delete() uses the smr_delete_lock in order to + // reduce the traffic on the Threads_lock. static Monitor* _smr_delete_lock; // The '_cnt', '_max' and '_times" fields are enabled via // -XX:+EnableThreadSMRStatistics (see thread.cpp for a // description about each field): static uint _smr_delete_lock_wait_cnt; static uint _smr_delete_lock_wait_max; + // The smr_delete_notify flag is used for proper double-check + // locking in order to reduce the traffic on the smr_delete_lock. static volatile uint _smr_delete_notify; static volatile uint _smr_deleted_thread_cnt; static volatile uint _smr_deleted_thread_time_max; static volatile uint _smr_deleted_thread_times; static ThreadsList* volatile _smr_java_thread_list; - static ThreadsList* get_smr_java_thread_list(); - static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list); static uint64_t _smr_java_thread_list_alloc_cnt; static uint64_t _smr_java_thread_list_free_cnt; static uint _smr_java_thread_list_max; @@ -2163,7 +2166,21 @@ class Threads: AllStatic { static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS); static void initialize_jsr292_core_classes(TRAPS); + static ThreadsList *acquire_stable_list_fast_path(Thread *self); + static ThreadsList *acquire_stable_list_nested_path(Thread *self); + static void add_smr_deleted_thread_times(uint add_value); + static void clear_smr_delete_notify(); + static ThreadsList* get_smr_java_thread_list(); + static void inc_smr_deleted_thread_cnt(); + static void release_stable_list_fast_path(Thread *self); + static void release_stable_list_nested_path(Thread *self); + static void release_stable_list_wake_up(char *log_str); + static void set_smr_delete_notify(); + static Monitor* smr_delete_lock() { return _smr_delete_lock; } + static bool smr_delete_notify(); static void smr_free_list(ThreadsList* threads); + static void update_smr_deleted_thread_time_max(uint new_value); + static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list); public: // Thread management @@ -2176,30 +2193,13 @@ class Threads: AllStatic { // SMR support: static ThreadsList *acquire_stable_list(Thread *self, bool is_ThreadsListSetter); - static ThreadsList *acquire_stable_list_fast_path(Thread *self); - static ThreadsList *acquire_stable_list_nested_path(Thread *self); static void release_stable_list(Thread *self); - static void release_stable_list_fast_path(Thread *self); - static void release_stable_list_nested_path(Thread *self); - static void release_stable_list_wake_up(char *log_str); static bool is_a_protected_JavaThread(JavaThread *thread); static bool is_a_protected_JavaThread_with_lock(JavaThread *thread) { MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock); return is_a_protected_JavaThread(thread); } static void smr_delete(JavaThread *thread); - // The coordination between Threads::release_stable_list() and - // Threads::smr_delete() uses the smr_delete_lock in order to - // reduce the traffic on the Threads_lock. - static Monitor* smr_delete_lock() { return _smr_delete_lock; } - // The smr_delete_notify flag is used for proper double-check - // locking in order to reduce the traffic on the smr_delete_lock. - static bool smr_delete_notify(); - static void set_smr_delete_notify(); - static void clear_smr_delete_notify(); - static void inc_smr_deleted_thread_cnt(); - static void update_smr_deleted_thread_time_max(uint new_value); - static void add_smr_deleted_thread_times(uint add_value); static void inc_smr_tlh_cnt(); static void update_smr_tlh_time_max(uint new_value); static void add_smr_tlh_times(uint add_value); diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index 50d327bcb66..3821d9317bf 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -212,34 +212,8 @@ inline void JavaThread::set_terminated_value() { OrderAccess::release_store((volatile jint *) &_terminated, (jint) _thread_terminated); } -inline ThreadsList* Threads::get_smr_java_thread_list() { - return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list); -} - -inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) { - return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list); -} - -inline void Threads::inc_smr_deleted_thread_cnt() { - Atomic::inc(&_smr_deleted_thread_cnt); -} - -inline void Threads::update_smr_deleted_thread_time_max(uint new_value) { - while (true) { - uint cur_value = _smr_deleted_thread_time_max; - if (new_value <= cur_value) { - // No need to update max value so we're done. - break; - } - if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) { - // Updated max value so we're done. Otherwise try it all again. - break; - } - } -} - -inline void Threads::add_smr_deleted_thread_times(uint add_value) { - Atomic::add(add_value, &_smr_deleted_thread_times); +inline void Threads::add_smr_tlh_times(uint add_value) { + Atomic::add(add_value, &_smr_tlh_times); } inline void Threads::inc_smr_tlh_cnt() { @@ -260,8 +234,4 @@ inline void Threads::update_smr_tlh_time_max(uint new_value) { } } -inline void Threads::add_smr_tlh_times(uint add_value) { - Atomic::add(add_value, &_smr_tlh_times); -} - #endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP From e473a43bb8e11a16a6002624aaaf389a762e5367 Mon Sep 17 00:00:00 2001 From: Chris Plummer <cjplummer@openjdk.org> Date: Thu, 30 Nov 2017 16:28:47 -0800 Subject: [PATCH 100/165] 8192840: serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java failing on Windows Make error message searching logic more general so it works on Windows Reviewed-by: dcubed, sspitsyn, dholmes --- .../serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java index 76268f012eb..3920bb3691e 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java +++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java @@ -37,7 +37,8 @@ public class AttachNoEntry extends AttachFailedTestBase { OutputAnalyzer output = null; output = executor.execute("JVMTI.agent_load " + libpath); - output.shouldContain("Agent_OnAttach is not available"); + output.shouldContain("Agent_OnAttach"); + output.shouldContain("is not available"); } catch (Exception e) { throw new RuntimeException(e); } From 6311c909f6d8e4ab7d211f9db6a7499801673955 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga <ysuenaga@openjdk.org> Date: Fri, 1 Dec 2017 10:04:31 +0900 Subject: [PATCH 101/165] 8190837: BasicType and BasicTypeSize should refer to HotSpot values Reviewed-by: dholmes, jgeorge --- src/hotspot/share/runtime/vmStructs.cpp | 40 ++++ .../sun/jvm/hotspot/runtime/BasicType.java | 219 +++++++++++------- .../jvm/hotspot/runtime/BasicTypeSize.java | 60 +++-- .../jvm/hotspot/runtime/PerfDataEntry.java | 104 +++------ 4 files changed, 253 insertions(+), 170 deletions(-) diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index e11f75cd0cb..9fb22233abb 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -2643,6 +2643,46 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor; declare_constant(Deoptimization::_reason_shift) \ declare_constant(Deoptimization::_debug_id_shift) \ \ + /******************************************/ \ + /* BasicType enum (globalDefinitions.hpp) */ \ + /******************************************/ \ + \ + declare_constant(T_BOOLEAN) \ + declare_constant(T_CHAR) \ + declare_constant(T_FLOAT) \ + declare_constant(T_DOUBLE) \ + declare_constant(T_BYTE) \ + declare_constant(T_SHORT) \ + declare_constant(T_INT) \ + declare_constant(T_LONG) \ + declare_constant(T_OBJECT) \ + declare_constant(T_ARRAY) \ + declare_constant(T_VOID) \ + declare_constant(T_ADDRESS) \ + declare_constant(T_NARROWOOP) \ + declare_constant(T_METADATA) \ + declare_constant(T_NARROWKLASS) \ + declare_constant(T_CONFLICT) \ + declare_constant(T_ILLEGAL) \ + \ + /**********************************************/ \ + /* BasicTypeSize enum (globalDefinitions.hpp) */ \ + /**********************************************/ \ + \ + declare_constant(T_BOOLEAN_size) \ + declare_constant(T_CHAR_size) \ + declare_constant(T_FLOAT_size) \ + declare_constant(T_DOUBLE_size) \ + declare_constant(T_BYTE_size) \ + declare_constant(T_SHORT_size) \ + declare_constant(T_INT_size) \ + declare_constant(T_LONG_size) \ + declare_constant(T_OBJECT_size) \ + declare_constant(T_ARRAY_size) \ + declare_constant(T_NARROWOOP_size) \ + declare_constant(T_NARROWKLASS_size) \ + declare_constant(T_VOID_size) \ + \ /*********************/ \ /* Matcher (C2 only) */ \ /*********************/ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java index 93de9426d04..7c25edc41d6 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java @@ -24,133 +24,159 @@ package sun.jvm.hotspot.runtime; +import java.util.Observer; +import sun.jvm.hotspot.types.TypeDataBase; + + /** Encapsulates the BasicType enum in globalDefinitions.hpp in the VM. */ public class BasicType { - public static final int tBoolean = 4; - public static final int tChar = 5; - public static final int tFloat = 6; - public static final int tDouble = 7; - public static final int tByte = 8; - public static final int tShort = 9; - public static final int tInt = 10; - public static final int tLong = 11; - public static final int tObject = 12; - public static final int tArray = 13; - public static final int tVoid = 14; - public static final int tAddress = 15; - public static final int tNarrowOop = 16; - public static final int tMetadata = 17; - public static final int tNarrowKlass = 18; - public static final int tConflict = 19; - public static final int tIllegal = 99; + public static final BasicType T_BOOLEAN = new BasicType(); + public static final BasicType T_CHAR = new BasicType(); + public static final BasicType T_FLOAT = new BasicType(); + public static final BasicType T_DOUBLE = new BasicType(); + public static final BasicType T_BYTE = new BasicType(); + public static final BasicType T_SHORT = new BasicType(); + public static final BasicType T_INT = new BasicType(); + public static final BasicType T_LONG = new BasicType(); + public static final BasicType T_OBJECT = new BasicType(); + public static final BasicType T_ARRAY = new BasicType(); + public static final BasicType T_VOID = new BasicType(); + public static final BasicType T_ADDRESS = new BasicType(); + public static final BasicType T_NARROWOOP = new BasicType(); + public static final BasicType T_METADATA = new BasicType(); + public static final BasicType T_NARROWKLASS = new BasicType(); + public static final BasicType T_CONFLICT = new BasicType(); + public static final BasicType T_ILLEGAL = new BasicType(); - public static final BasicType T_BOOLEAN = new BasicType(tBoolean); - public static final BasicType T_CHAR = new BasicType(tChar); - public static final BasicType T_FLOAT = new BasicType(tFloat); - public static final BasicType T_DOUBLE = new BasicType(tDouble); - public static final BasicType T_BYTE = new BasicType(tByte); - public static final BasicType T_SHORT = new BasicType(tShort); - public static final BasicType T_INT = new BasicType(tInt); - public static final BasicType T_LONG = new BasicType(tLong); - public static final BasicType T_OBJECT = new BasicType(tObject); - public static final BasicType T_ARRAY = new BasicType(tArray); - public static final BasicType T_VOID = new BasicType(tVoid); - public static final BasicType T_ADDRESS = new BasicType(tAddress); - public static final BasicType T_NARROWOOP = new BasicType(tNarrowOop); - public static final BasicType T_METADATA = new BasicType(tMetadata); - public static final BasicType T_NARROWKLASS = new BasicType(tNarrowKlass); - public static final BasicType T_CONFLICT = new BasicType(tConflict); - public static final BasicType T_ILLEGAL = new BasicType(tIllegal); + static { + VM.registerVMInitializedObserver( + (o, d) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) { + T_BOOLEAN.setType(db.lookupIntConstant("T_BOOLEAN").intValue()); + T_CHAR.setType(db.lookupIntConstant("T_CHAR").intValue()); + T_FLOAT.setType(db.lookupIntConstant("T_FLOAT").intValue()); + T_DOUBLE.setType(db.lookupIntConstant("T_DOUBLE").intValue()); + T_BYTE.setType(db.lookupIntConstant("T_BYTE").intValue()); + T_SHORT.setType(db.lookupIntConstant("T_SHORT").intValue()); + T_INT.setType(db.lookupIntConstant("T_INT").intValue()); + T_LONG.setType(db.lookupIntConstant("T_LONG").intValue()); + T_OBJECT.setType(db.lookupIntConstant("T_OBJECT").intValue()); + T_ARRAY.setType(db.lookupIntConstant("T_ARRAY").intValue()); + T_VOID.setType(db.lookupIntConstant("T_VOID").intValue()); + T_ADDRESS.setType(db.lookupIntConstant("T_ADDRESS").intValue()); + T_NARROWOOP.setType(db.lookupIntConstant("T_NARROWOOP").intValue()); + T_METADATA.setType(db.lookupIntConstant("T_METADATA").intValue()); + T_NARROWKLASS.setType(db.lookupIntConstant("T_NARROWKLASS").intValue()); + T_CONFLICT.setType(db.lookupIntConstant("T_CONFLICT").intValue()); + T_ILLEGAL.setType(db.lookupIntConstant("T_ILLEGAL").intValue()); + } public static int getTBoolean() { - return tBoolean; + return T_BOOLEAN.getType(); } public static int getTChar() { - return tChar; + return T_CHAR.getType(); } public static int getTFloat() { - return tFloat; + return T_FLOAT.getType(); } public static int getTDouble() { - return tDouble; + return T_DOUBLE.getType(); } public static int getTByte() { - return tByte; + return T_BYTE.getType(); } public static int getTShort() { - return tShort; + return T_SHORT.getType(); } public static int getTInt() { - return tInt; + return T_INT.getType(); } public static int getTLong() { - return tLong; + return T_LONG.getType(); } public static int getTObject() { - return tObject; + return T_OBJECT.getType(); } public static int getTArray() { - return tArray; + return T_ARRAY.getType(); } public static int getTVoid() { - return tVoid; + return T_VOID.getType(); } public static int getTAddress() { - return tAddress; + return T_ADDRESS.getType(); } public static int getTNarrowOop() { - return tNarrowOop; + return T_NARROWOOP.getType(); } public static int getTMetadata() { - return tMetadata; + return T_METADATA.getType(); } public static int getTNarrowKlass() { - return tNarrowKlass; + return T_NARROWKLASS.getType(); } /** For stack value type with conflicting contents */ public static int getTConflict() { - return tConflict; + return T_CONFLICT.getType(); } public static int getTIllegal() { - return tIllegal; + return T_ILLEGAL.getType(); } public static BasicType intToBasicType(int i) { - switch(i) { - case tBoolean: return T_BOOLEAN; - case tChar: return T_CHAR; - case tFloat: return T_FLOAT; - case tDouble: return T_DOUBLE; - case tByte: return T_BYTE; - case tShort: return T_SHORT; - case tInt: return T_INT; - case tLong: return T_LONG; - case tObject: return T_OBJECT; - case tArray: return T_ARRAY; - case tVoid: return T_VOID; - case tAddress: return T_ADDRESS; - case tNarrowOop: return T_NARROWOOP; - case tMetadata: return T_METADATA; - case tNarrowKlass: return T_NARROWKLASS; - default: return T_ILLEGAL; + if (i == T_BOOLEAN.getType()) { + return T_BOOLEAN; + } else if (i == T_CHAR.getType()) { + return T_CHAR; + } else if (i == T_FLOAT.getType()) { + return T_FLOAT; + } else if (i == T_DOUBLE.getType()) { + return T_DOUBLE; + } else if (i == T_BYTE.getType()) { + return T_BYTE; + } else if (i == T_SHORT.getType()) { + return T_SHORT; + } else if (i == T_INT.getType()) { + return T_INT; + } else if (i == T_LONG.getType()) { + return T_LONG; + } else if (i == T_OBJECT.getType()) { + return T_OBJECT; + } else if (i == T_ARRAY.getType()) { + return T_ARRAY; + } else if (i == T_VOID.getType()) { + return T_VOID; + } else if (i == T_ADDRESS.getType()) { + return T_ADDRESS; + } else if (i == T_NARROWOOP.getType()) { + return T_NARROWOOP; + } else if (i == T_METADATA.getType()) { + return T_METADATA; + } else if (i == T_NARROWKLASS.getType()) { + return T_NARROWKLASS; + } else { + return T_ILLEGAL; } } @@ -180,29 +206,48 @@ public class BasicType { } public String getName() { - switch (type) { - case tBoolean: return "boolean"; - case tChar: return "char"; - case tFloat: return "float"; - case tDouble: return "double"; - case tByte: return "byte"; - case tShort: return "short"; - case tInt: return "int"; - case tLong: return "long"; - case tObject: return "object"; - case tArray: return "array"; - case tVoid: return "void"; - case tAddress: return "address"; - case tNarrowOop: return "narrow oop"; - case tMetadata: return "metadata"; - case tNarrowKlass: return "narrow klass"; - case tConflict: return "conflict"; - default: return "ILLEGAL TYPE"; + if (type == T_BOOLEAN.getType()) { + return "boolean"; + } else if (type == T_CHAR.getType()) { + return "char"; + } else if (type == T_FLOAT.getType()) { + return "float"; + } else if (type == T_DOUBLE.getType()) { + return "double"; + } else if (type == T_BYTE.getType()) { + return "byte"; + } else if (type == T_SHORT.getType()) { + return "short"; + } else if (type == T_INT.getType()) { + return "int"; + } else if (type == T_LONG.getType()) { + return "long"; + } else if (type == T_OBJECT.getType()) { + return "object"; + } else if (type == T_ARRAY.getType()) { + return "array"; + } else if (type == T_VOID.getType()) { + return "void"; + } else if (type == T_ADDRESS.getType()) { + return "address"; + } else if (type == T_NARROWOOP.getType()) { + return "narrow oop"; + } else if (type == T_METADATA.getType()) { + return "metadata"; + } else if (type == T_NARROWKLASS.getType()) { + return "narrow klass"; + } else if (type == T_CONFLICT.getType()) { + return "conflict"; + } else { + return "ILLEGAL TYPE"; } } //-- Internals only below this point - private BasicType(int type) { + private BasicType() { + } + + private void setType(int type) { this.type = type; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java index 2a18795ab99..e09d6f5e1ce 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2017, 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 @@ -24,22 +24,48 @@ package sun.jvm.hotspot.runtime; +import java.util.Observer; +import sun.jvm.hotspot.types.TypeDataBase; + + /** Encapsulates the BasicTypeSize enum in globalDefinitions.hpp in the VM. */ public class BasicTypeSize { - private static boolean initialized = false; - private static int tBooleanSize = 1; - private static int tCharSize = 1; - private static int tFloatSize = 1; - private static int tDoubleSize = 2; - private static int tByteSize = 1; - private static int tShortSize = 1; - private static int tIntSize = 1; - private static int tLongSize = 2; - private static int tObjectSize = 1; - private static int tArraySize = 1; - private static int tVoidSize = 0; + private static int tBooleanSize; + private static int tCharSize; + private static int tFloatSize; + private static int tDoubleSize; + private static int tByteSize; + private static int tShortSize; + private static int tIntSize; + private static int tLongSize; + private static int tObjectSize; + private static int tArraySize; + private static int tNarrowOopSize; + private static int tNarrowKlassSize; + private static int tVoidSize; + + static { + VM.registerVMInitializedObserver( + (o, d) -> initialize(VM.getVM().getTypeDataBase())); + } + + private static synchronized void initialize(TypeDataBase db) { + tBooleanSize = db.lookupIntConstant("T_BOOLEAN_size").intValue(); + tCharSize = db.lookupIntConstant("T_INT_size").intValue(); + tFloatSize = db.lookupIntConstant("T_FLOAT_size").intValue(); + tDoubleSize = db.lookupIntConstant("T_DOUBLE_size").intValue(); + tByteSize = db.lookupIntConstant("T_BYTE_size").intValue(); + tShortSize = db.lookupIntConstant("T_SHORT_size").intValue(); + tIntSize = db.lookupIntConstant("T_INT_size").intValue(); + tLongSize = db.lookupIntConstant("T_LONG_size").intValue(); + tObjectSize = db.lookupIntConstant("T_OBJECT_size").intValue(); + tArraySize = db.lookupIntConstant("T_ARRAY_size").intValue(); + tNarrowOopSize = db.lookupIntConstant("T_NARROWOOP_size").intValue(); + tNarrowKlassSize = db.lookupIntConstant("T_NARROWKLASS_size").intValue(); + tVoidSize = db.lookupIntConstant("T_VOID_size").intValue(); + } public static int getTBooleanSize() { return tBooleanSize; @@ -81,6 +107,14 @@ public class BasicTypeSize { return tArraySize; } + public static int getTNarrowOopSize() { + return tNarrowOopSize; + } + + public static int getTNarrowKlassSize() { + return tNarrowKlassSize; + } + public static int getTVoidSize() { return tVoidSize; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java index 7818afba5c8..a8da801532d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java @@ -139,7 +139,7 @@ public class PerfDataEntry extends VMObject { public boolean booleanValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tBoolean, "not a boolean"); + dataType() == BasicType.getTBoolean(), "not a boolean"); } return addr.getJBooleanAt(dataOffset()); } @@ -147,7 +147,7 @@ public class PerfDataEntry extends VMObject { public char charValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tChar, "not a char"); + dataType() == BasicType.getTChar(), "not a char"); } return addr.getJCharAt(dataOffset()); } @@ -155,7 +155,7 @@ public class PerfDataEntry extends VMObject { public byte byteValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tByte, "not a byte"); + dataType() == BasicType.getTByte(), "not a byte"); } return addr.getJByteAt(dataOffset()); @@ -164,7 +164,7 @@ public class PerfDataEntry extends VMObject { public short shortValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tShort, "not a short"); + dataType() == BasicType.getTShort(), "not a short"); } return addr.getJShortAt(dataOffset()); } @@ -172,7 +172,7 @@ public class PerfDataEntry extends VMObject { public int intValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tInt, "not an int"); + dataType() == BasicType.getTInt(), "not an int"); } return addr.getJIntAt(dataOffset()); } @@ -180,7 +180,7 @@ public class PerfDataEntry extends VMObject { public long longValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tLong, "not a long"); + dataType() == BasicType.getTLong(), "not a long"); } return addr.getJLongAt(dataOffset()); } @@ -188,7 +188,7 @@ public class PerfDataEntry extends VMObject { public float floatValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tFloat, "not a float"); + dataType() == BasicType.getTFloat(), "not a float"); } return addr.getJFloatAt(dataOffset()); } @@ -196,7 +196,7 @@ public class PerfDataEntry extends VMObject { public double doubleValue() { if (Assert.ASSERTS_ENABLED) { Assert.that(vectorLength() == 0 && - dataType() == BasicType.tDouble, "not a double"); + dataType() == BasicType.getTDouble(), "not a double"); } return addr.getJDoubleAt(dataOffset()); } @@ -205,7 +205,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tBoolean, "not a boolean vector"); + dataType() == BasicType.getTBoolean(), "not a boolean vector"); } boolean[] res = new boolean[len]; final int off = dataOffset(); @@ -220,7 +220,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tChar, "not a char vector"); + dataType() == BasicType.getTChar(), "not a char vector"); } char[] res = new char[len]; final int off = dataOffset(); @@ -235,7 +235,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tByte, "not a byte vector"); + dataType() == BasicType.getTByte(), "not a byte vector"); } byte[] res = new byte[len]; final int off = dataOffset(); @@ -250,7 +250,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tShort, "not a short vector"); + dataType() == BasicType.getTShort(), "not a short vector"); } short[] res = new short[len]; final int off = dataOffset(); @@ -265,7 +265,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tInt, "not an int vector"); + dataType() == BasicType.getTInt(), "not an int vector"); } int[] res = new int[len]; final int off = dataOffset(); @@ -280,7 +280,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tLong, "not a long vector"); + dataType() == BasicType.getTLong(), "not a long vector"); } long[] res = new long[len]; final int off = dataOffset(); @@ -295,7 +295,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tFloat, "not a float vector"); + dataType() == BasicType.getTFloat(), "not a float vector"); } float[] res = new float[len]; final int off = dataOffset(); @@ -310,7 +310,7 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); if (Assert.ASSERTS_ENABLED) { Assert.that(len > 0 && - dataType() == BasicType.tDouble, "not a double vector"); + dataType() == BasicType.getTDouble(), "not a double vector"); } double[] res = new double[len]; final int off = dataOffset(); @@ -327,38 +327,27 @@ public class PerfDataEntry extends VMObject { int len = vectorLength(); String str = null; if (len == 0) { // scalar - switch (dataType) { - case BasicType.tBoolean: + if (dataType == BasicType.getTBoolean()) { str = Boolean.toString(booleanValue()); - break; - case BasicType.tChar: + } else if (dataType == BasicType.getTChar()) { str = "'" + Character.toString(charValue()) + "'"; - break; - case BasicType.tByte: + } else if (dataType == BasicType.getTByte()) { str = Byte.toString(byteValue()); - break; - case BasicType.tShort: + } else if (dataType == BasicType.getTShort()) { str = Short.toString(shortValue()); - break; - case BasicType.tInt: + } else if (dataType == BasicType.getTInt()) { str = Integer.toString(intValue()); - break; - case BasicType.tLong: + } else if (dataType == BasicType.getTLong()) { str = Long.toString(longValue()); - break; - case BasicType.tFloat: + } else if (dataType == BasicType.getTFloat()) { str = Float.toString(floatValue()); - break; - case BasicType.tDouble: + } else if (dataType == BasicType.getTDouble()) { str = Double.toString(doubleValue()); - break; - default: + } else { str = "<unknown scalar value>"; - break; } } else { // vector - switch (dataType) { - case BasicType.tBoolean: { + if (dataType == BasicType.getTBoolean()) { boolean[] res = booleanArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -368,26 +357,17 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - case BasicType.tChar: { + } else if (dataType == BasicType.getTChar()) { // char[] is returned as a String str = new String(charArrayValue()); - break; - } - - case BasicType.tByte: { + } else if (dataType == BasicType.getTByte()) { // byte[] is returned as a String try { str = new String(byteArrayValue(), "US-ASCII"); } catch (java.io.UnsupportedEncodingException e) { str = "can't decode string : " + e.getMessage(); } - break; - } - - case BasicType.tShort: { + } else if (dataType == BasicType.getTShort()) { short[] res = shortArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -397,10 +377,7 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - case BasicType.tInt: { + } else if (dataType == BasicType.getTInt()) { int[] res = intArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -410,10 +387,7 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - case BasicType.tLong: { + } else if (dataType == BasicType.getTLong()) { long[] res = longArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -423,10 +397,7 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - case BasicType.tFloat: { + } else if (dataType == BasicType.getTFloat()) { float[] res = floatArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -436,10 +407,7 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - case BasicType.tDouble: { + } else if (dataType == BasicType.getTDouble()) { double[] res = doubleArrayValue(); StringBuffer buf = new StringBuffer(); buf.append('['); @@ -449,12 +417,8 @@ public class PerfDataEntry extends VMObject { } buf.append(']'); str = buf.toString(); - break; - } - - default: + } else { str = "<unknown vector value>"; - break; } } From 52ecf42bdc03271dc55c6c86489a82f84b42e8cb Mon Sep 17 00:00:00 2001 From: Mikhailo Seledtsov <mseledtsov@openjdk.org> Date: Thu, 30 Nov 2017 20:45:03 -0800 Subject: [PATCH 102/165] 8191943: [TESTBUG] docker/TestCPUAwareness fails on machine with 2 CPUs Updated test cases to account for available processors Reviewed-by: bobv, dholmes --- .../containers/docker/TestCPUAwareness.java | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java index 66768266aa7..3a4e2bc183f 100644 --- a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java @@ -33,28 +33,28 @@ * @build Common * @run driver TestCPUAwareness */ +import java.util.List; import jdk.test.lib.containers.docker.DockerRunOptions; import jdk.test.lib.containers.docker.DockerTestUtils; public class TestCPUAwareness { private static final String imageName = Common.imageName("cpu"); + private static final int availableCPUs = Runtime.getRuntime().availableProcessors(); public static void main(String[] args) throws Exception { if (!DockerTestUtils.canTestDocker()) { return; } - int availableCPUs = Runtime.getRuntime().availableProcessors(); System.out.println("Test Environment: detected availableCPUs = " + availableCPUs); DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); try { // cpuset, period, shares, expected Active Processor Count - testAPCCombo("0", 200*1000, 100*1000, 4*1024, 1); - testAPCCombo("0,1", 200*1000, 100*1000, 4*1024, 2); - testAPCCombo("0,1", 200*1000, 100*1000, 1*1024, 2); + testComboWithCpuSets(); + // cpu shares - it should be safe to use CPU shares exceeding available CPUs testCpuShares(256, 1); testCpuShares(2048, 2); testCpuShares(4096, 4); @@ -70,9 +70,11 @@ public class TestCPUAwareness { testActiveProcessorCount(1, 1); testActiveProcessorCount(2, 2); + // cpu quota and period testCpuQuotaAndPeriod(50*1000, 100*1000); testCpuQuotaAndPeriod(100*1000, 100*1000); testCpuQuotaAndPeriod(150*1000, 100*1000); + testCpuQuotaAndPeriod(400*1000, 100*1000); } finally { DockerTestUtils.removeDockerImage(imageName); @@ -80,6 +82,31 @@ public class TestCPUAwareness { } + private static void testComboWithCpuSets() throws Exception { + String cpuSetStr = CPUSetsReader.readFromProcStatus("Cpus_allowed_list"); + System.out.println("cpuSetStr = " + cpuSetStr); + + if (cpuSetStr == null) { + System.out.printf("The cpuset test cases are skipped"); + } else { + List<Integer> cpuSet = CPUSetsReader.parseCpuSet(cpuSetStr); + + // Test subset of cpuset with one element + if (cpuSet.size() >= 1) { + String testCpuSet = CPUSetsReader.listToString(cpuSet, 1); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, 1); + } + + // Test subset of cpuset with two elements + if (cpuSet.size() >= 2) { + String testCpuSet = CPUSetsReader.listToString(cpuSet, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, 2); + testAPCCombo(testCpuSet, 200*1000, 100*1000, 1*1024, 2); + } + } + } + + private static void testActiveProcessorCount(int valueToSet, int expectedValue) throws Exception { Common.logNewTestCase("Test ActiveProcessorCount: valueToSet = " + valueToSet); @@ -99,6 +126,16 @@ public class TestCPUAwareness { } + // Expected active processor count can not exceed available CPU count + private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { + if (expectedAPC > availableCPUs) { + expectedAPC = availableCPUs; + System.out.println("Adjusted expectedAPC = " + expectedAPC); + } + return expectedAPC; + } + + private static void testCpuQuotaAndPeriod(int quota, int period) throws Exception { Common.logNewTestCase("test cpu quota and period: "); @@ -107,6 +144,7 @@ public class TestCPUAwareness { int expectedAPC = (int) Math.ceil((float) quota / (float) period); System.out.println("expectedAPC = " + expectedAPC); + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); DockerRunOptions opts = Common.newOpts(imageName) .addDockerOpts("--cpu-period=" + period) @@ -129,6 +167,8 @@ public class TestCPUAwareness { System.out.println("shares = " + period); System.out.println("expectedAPC = " + expectedAPC); + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); + DockerRunOptions opts = Common.newOpts(imageName) .addDockerOpts("--cpuset-cpus", "" + cpuset) .addDockerOpts("--cpu-period=" + period) @@ -141,6 +181,10 @@ public class TestCPUAwareness { private static void testCpuShares(int shares, int expectedAPC) throws Exception { Common.logNewTestCase("test cpu shares, shares = " + shares); + System.out.println("expectedAPC = " + expectedAPC); + + expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); + DockerRunOptions opts = Common.newOpts(imageName) .addDockerOpts("--cpu-shares=" + shares); Common.run(opts) From 686dc5c9776e8aadf8ee47a61e40531800a8b513 Mon Sep 17 00:00:00 2001 From: Mikhailo Seledtsov <mseledtsov@openjdk.org> Date: Thu, 30 Nov 2017 21:28:12 -0800 Subject: [PATCH 103/165] 8192866: [TESTBUG] Move UseAppCDS.java from the closed ProblemList.txt to the open one Moved UseAppCDS.java to the open problem list Reviewed-by: dholmes, ccheung --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index be874cd589f..e3c301a7633 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -76,6 +76,7 @@ runtime/CompressedOops/UseCompressedOops.java 8079353 generic-all # This test is disabled since it will stress NMT and timeout during normal testing runtime/NMT/MallocStressTest.java 8166548 generic-all runtime/SharedArchiveFile/DefaultUseWithClient.java 8154204 generic-all +runtime/AppCDS/UseAppCDS.java 8165603 windows-all ############################################################################# From 9a3de631aa8f7cc89e0a7df64249af5a6927d203 Mon Sep 17 00:00:00 2001 From: Jini George <jgeorge@openjdk.org> Date: Fri, 1 Dec 2017 11:40:39 +0530 Subject: [PATCH 104/165] 8191914: New SA test timeout on windows Avoid test hangs in a few SA tests by ensuring that OutputAnalyzer gets created before waitFor() gets called Reviewed-by: dholmes, sballal --- test/hotspot/jtreg/ProblemList.txt | 1 - .../serviceability/sa/JhsdbThreadInfoTest.java | 4 ++-- .../serviceability/sa/TestClhsdbJstackLock.java | 14 ++++---------- .../serviceability/sa/TestJhsdbJstackLock.java | 2 +- .../jtreg/serviceability/sa/TestPrintMdo.java | 14 ++++---------- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index e3c301a7633..d1c9d42e0a4 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -86,7 +86,6 @@ serviceability/jdwp/AllModulesCommandTest.java 8170541 generic-all serviceability/sa/sadebugd/SADebugDTest.java 8163805 generic-all serviceability/jvmti/ModuleAwareAgents/ClassFileLoadHook/MAAClassFileLoadHook.java 8173936 generic-all serviceability/sa/TestRevPtrsForInvokeDynamic.java 8191270 generic-all -serviceability/sa/TestJhsdbJstackLock.java 8191914 windows-all ############################################################################# # :hotspot_misc diff --git a/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java b/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java index 0c916001288..000e8081993 100644 --- a/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java +++ b/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java @@ -56,10 +56,10 @@ public class JhsdbThreadInfoTest { pb.command(jhsdbLauncher.getCommand()); Process jhsdb = pb.start(); - jhsdb.waitFor(); - OutputAnalyzer out = new OutputAnalyzer(jhsdb); + jhsdb.waitFor(); + System.out.println(out.getStdout()); System.err.println(out.getStderr()); diff --git a/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java index 2132a4e36fe..5f9a43f3267 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java +++ b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java @@ -129,22 +129,16 @@ public class TestClhsdbJstackLock { throw new Error("Problem issuing the jstack command: " + str, ioe); } + OutputAnalyzer output = new OutputAnalyzer(p); + try { p.waitFor(); } catch (InterruptedException ie) { + p.destroyForcibly(); throw new Error("Problem awaiting the child process: " + ie, ie); } - int exitValue = p.exitValue(); - if (exitValue != 0) { - String output; - try { - output = new OutputAnalyzer(p).getOutput(); - } catch (IOException ioe) { - throw new Error("Can't get failed clhsdb process output: " + ioe, ioe); - } - throw new AssertionError("clhsdb wasn't run successfully: " + output); - } + output.shouldHaveExitValue(0); } public static void main (String... args) throws Exception { diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java index 423d56bef35..739ae29ba0b 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java @@ -65,10 +65,10 @@ public class TestJhsdbJstackLock { ProcessBuilder pb = new ProcessBuilder(); pb.command(launcher.getCommand()); Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); jhsdb.waitFor(); - OutputAnalyzer out = new OutputAnalyzer(jhsdb); System.out.println(out.getStdout()); System.err.println(out.getStderr()); diff --git a/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java b/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java index 48b0b0840b8..56e18f0409a 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java +++ b/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java @@ -132,22 +132,16 @@ public class TestPrintMdo { throw new Error("Problem issuing the printmdo command: " + str, ioe); } + OutputAnalyzer output = new OutputAnalyzer(p); + try { p.waitFor(); } catch (InterruptedException ie) { + p.destroyForcibly(); throw new Error("Problem awaiting the child process: " + ie, ie); } - int exitValue = p.exitValue(); - if (exitValue != 0) { - String output; - try { - output = new OutputAnalyzer(p).getOutput(); - } catch (IOException ioe) { - throw new Error("Can't get failed clhsdb process output: " + ioe, ioe); - } - throw new AssertionError("clhsdb wasn't run successfully: " + output); - } + output.shouldHaveExitValue(0); } public static void main (String... args) throws Exception { From ea04c5cfb36fb7f40f12400295535072c66aa681 Mon Sep 17 00:00:00 2001 From: Stefan Johansson <sjohanss@openjdk.org> Date: Fri, 1 Dec 2017 08:56:22 +0100 Subject: [PATCH 105/165] 8191821: Finer granularity for GC verification Reviewed-by: tschatzl, poonam, sangheki --- src/hotspot/share/gc/g1/g1Arguments.cpp | 7 + src/hotspot/share/gc/g1/g1Arguments.hpp | 1 + src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 13 +- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 20 +- src/hotspot/share/gc/g1/g1FullCollector.cpp | 6 +- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 53 +++- src/hotspot/share/gc/g1/g1HeapVerifier.hpp | 22 +- src/hotspot/share/gc/shared/gcArguments.cpp | 25 ++ src/hotspot/share/gc/shared/gcArguments.hpp | 8 + src/hotspot/share/memory/universe.cpp | 1 + src/hotspot/share/runtime/globals.hpp | 4 + .../gtest/gc/g1/test_g1HeapVerifier.cpp | 77 ++++++ .../hotspot/jtreg/gc/g1/TestVerifyGCType.java | 256 ++++++++++++++++++ 13 files changed, 457 insertions(+), 36 deletions(-) create mode 100644 test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp create mode 100644 test/hotspot/jtreg/gc/g1/TestVerifyGCType.java diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 9fcb602e0ce..b9ad5a02951 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -26,6 +26,7 @@ #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorPolicy.hpp" +#include "gc/g1/g1HeapVerifier.hpp" #include "gc/g1/heapRegion.hpp" #include "gc/shared/gcArguments.inline.hpp" #include "runtime/globals.hpp" @@ -104,6 +105,12 @@ void G1Arguments::initialize_flags() { #endif } +bool G1Arguments::parse_verification_type(const char* type) { + G1CollectedHeap::heap()->verifier()->parse_verification_type(type); + // Always return true because we want to parse all values. + return true; +} + CollectedHeap* G1Arguments::create_heap() { return create_heap_with_policy<G1CollectedHeap, G1CollectorPolicy>(); } diff --git a/src/hotspot/share/gc/g1/g1Arguments.hpp b/src/hotspot/share/gc/g1/g1Arguments.hpp index 3f87c638c49..2dd753eb2a5 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.hpp +++ b/src/hotspot/share/gc/g1/g1Arguments.hpp @@ -32,6 +32,7 @@ class CollectedHeap; class G1Arguments : public GCArguments { public: virtual void initialize_flags(); + virtual bool parse_verification_type(const char* type); virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); }; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 14e942ecefa..7245bf55388 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1084,7 +1084,6 @@ void G1CollectedHeap::print_hrm_post_compaction() { PostCompactionPrinterClosure cl(hr_printer()); heap_region_iterate(&cl); } - } void G1CollectedHeap::abort_concurrent_cycle() { @@ -1133,7 +1132,7 @@ void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) { assert(!GCCause::is_user_requested_gc(gc_cause()) || explicit_gc, "invariant"); assert(used() == recalculate_used(), "Should be equal"); _verifier->verify_region_sets_optional(); - _verifier->verify_before_gc(); + _verifier->verify_before_gc(G1HeapVerifier::G1VerifyFull); _verifier->check_bitmaps("Full GC Start"); } @@ -1174,7 +1173,7 @@ void G1CollectedHeap::verify_after_full_collection() { check_gc_time_stamps(); _hrm.verify_optional(); _verifier->verify_region_sets_optional(); - _verifier->verify_after_gc(); + _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull); // Clear the previous marking bitmap, if needed for bitmap verification. // Note we cannot do this when we clear the next marking bitmap in // G1ConcurrentMark::abort() above since VerifyDuringGC verifies the @@ -2958,13 +2957,17 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { GCTraceCPUTime tcpu; + G1HeapVerifier::G1VerifyType verify_type; FormatBuffer<> gc_string("Pause "); if (collector_state()->during_initial_mark_pause()) { gc_string.append("Initial Mark"); + verify_type = G1HeapVerifier::G1VerifyInitialMark; } else if (collector_state()->gcs_are_young()) { gc_string.append("Young"); + verify_type = G1HeapVerifier::G1VerifyYoungOnly; } else { gc_string.append("Mixed"); + verify_type = G1HeapVerifier::G1VerifyMixed; } GCTraceTime(Info, gc) tm(gc_string, NULL, gc_cause(), true); @@ -3005,7 +3008,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { heap_region_iterate(&v_cl); } - _verifier->verify_before_gc(); + _verifier->verify_before_gc(verify_type); _verifier->check_bitmaps("GC Start"); @@ -3165,7 +3168,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { heap_region_iterate(&v_cl); } - _verifier->verify_after_gc(); + _verifier->verify_after_gc(verify_type); _verifier->check_bitmaps("GC End"); assert(!ref_processor_stw()->discovery_enabled(), "Postcondition"); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 2ef2c2938b3..a63013bdec4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1015,9 +1015,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) { SvcGCMarker sgcm(SvcGCMarker::OTHER); if (VerifyDuringGC) { - HandleMark hm; // handle scope - g1h->prepare_for_verify(); - Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)"); + g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (before)"); } g1h->verifier()->check_bitmaps("Remark Start"); @@ -1038,9 +1036,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) { // Verify the heap w.r.t. the previous marking bitmap. if (VerifyDuringGC) { - HandleMark hm; // handle scope - g1h->prepare_for_verify(); - Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (overflow)"); + g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (overflow)"); } // Clear the marking state because we will be restarting @@ -1055,9 +1051,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) { true /* expected_active */); if (VerifyDuringGC) { - HandleMark hm; // handle scope - g1h->prepare_for_verify(); - Universe::verify(VerifyOption_G1UseNextMarking, "During GC (after)"); + g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UseNextMarking, "During GC (after)"); } g1h->verifier()->check_bitmaps("Remark End"); assert(!restart_for_overflow(), "sanity"); @@ -1189,9 +1183,7 @@ void G1ConcurrentMark::cleanup() { g1h->verifier()->verify_region_sets_optional(); if (VerifyDuringGC) { - HandleMark hm; // handle scope - g1h->prepare_for_verify(); - Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)"); + g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (before)"); } g1h->verifier()->check_bitmaps("Cleanup Start"); @@ -1263,9 +1255,7 @@ void G1ConcurrentMark::cleanup() { Universe::update_heap_info_at_gc(); if (VerifyDuringGC) { - HandleMark hm; // handle scope - g1h->prepare_for_verify(); - Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (after)"); + g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (after)"); } g1h->verifier()->check_bitmaps("Cleanup End"); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 3ee356884ea..ffe54d28332 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -245,8 +245,8 @@ void G1FullCollector::run_task(AbstractGangTask* task) { } void G1FullCollector::verify_after_marking() { - if (!VerifyDuringGC) { - //Only do verification if VerifyDuringGC is set. + if (!VerifyDuringGC || !_heap->verifier()->should_verify(G1HeapVerifier::G1VerifyFull)) { + // Only do verification if VerifyDuringGC and G1VerifyFull is set. return; } @@ -265,6 +265,6 @@ void G1FullCollector::verify_after_marking() { // fail. At the end of the GC, the original mark word values // (including hash values) are restored to the appropriate // objects. - GCTraceTime(Info, gc, verify)("During GC (full)"); + GCTraceTime(Info, gc, verify)("Verifying During GC (full)"); _heap->verify(VerifyOption_G1UseFullMarking); } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 70e5ed29c65..ca1a22c78e0 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -376,6 +376,37 @@ public: } }; +void G1HeapVerifier::parse_verification_type(const char* type) { + if (strcmp(type, "young-only") == 0) { + enable_verification_type(G1VerifyYoungOnly); + } else if (strcmp(type, "initial-mark") == 0) { + enable_verification_type(G1VerifyInitialMark); + } else if (strcmp(type, "mixed") == 0) { + enable_verification_type(G1VerifyMixed); + } else if (strcmp(type, "remark") == 0) { + enable_verification_type(G1VerifyRemark); + } else if (strcmp(type, "cleanup") == 0) { + enable_verification_type(G1VerifyCleanup); + } else if (strcmp(type, "full") == 0) { + enable_verification_type(G1VerifyFull); + } else { + log_warning(gc, verify)("VerifyGCType: '%s' is unknown. Available types are: " + "young-only, initial-mark, mixed, remark, cleanup and full", type); + } +} + +void G1HeapVerifier::enable_verification_type(G1VerifyType type) { + // First enable will clear _enabled_verification_types. + if (_enabled_verification_types == G1VerifyAll) { + _enabled_verification_types = type; + } else { + _enabled_verification_types |= type; + } +} + +bool G1HeapVerifier::should_verify(G1VerifyType type) { + return (_enabled_verification_types & type) == type; +} void G1HeapVerifier::verify(VerifyOption vo) { if (!SafepointSynchronize::is_at_safepoint()) { @@ -541,28 +572,32 @@ void G1HeapVerifier::prepare_for_verify() { } } -double G1HeapVerifier::verify(bool guard, const char* msg) { +double G1HeapVerifier::verify(G1VerifyType type, VerifyOption vo, const char* msg) { double verify_time_ms = 0.0; - if (guard && _g1h->total_collections() >= VerifyGCStartAt) { + if (should_verify(type) && _g1h->total_collections() >= VerifyGCStartAt) { double verify_start = os::elapsedTime(); HandleMark hm; // Discard invalid handles created during verification prepare_for_verify(); - Universe::verify(VerifyOption_G1UsePrevMarking, msg); + Universe::verify(vo, msg); verify_time_ms = (os::elapsedTime() - verify_start) * 1000; } return verify_time_ms; } -void G1HeapVerifier::verify_before_gc() { - double verify_time_ms = verify(VerifyBeforeGC, "Before GC"); - _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms); +void G1HeapVerifier::verify_before_gc(G1VerifyType type) { + if (VerifyBeforeGC) { + double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "Before GC"); + _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms); + } } -void G1HeapVerifier::verify_after_gc() { - double verify_time_ms = verify(VerifyAfterGC, "After GC"); - _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms); +void G1HeapVerifier::verify_after_gc(G1VerifyType type) { + if (VerifyAfterGC) { + double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "After GC"); + _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms); + } } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp index 6d3bb3899f8..ee7cb0a316b 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp @@ -34,6 +34,7 @@ class G1CollectedHeap; class G1HeapVerifier : public CHeapObj<mtGC> { private: G1CollectedHeap* _g1h; + int _enabled_verification_types; // verify_region_sets() performs verification over the region // lists. It will be compiled in the product code to be used when @@ -41,8 +42,21 @@ private: void verify_region_sets(); public: + enum G1VerifyType { + G1VerifyYoungOnly = 1, // -XX:VerifyGCType=young-only + G1VerifyInitialMark = 2, // -XX:VerifyGCType=initial-mark + G1VerifyMixed = 4, // -XX:VerifyGCType=mixed + G1VerifyRemark = 8, // -XX:VerifyGCType=remark + G1VerifyCleanup = 16, // -XX:VerifyGCType=cleanup + G1VerifyFull = 32, // -XX:VerifyGCType=full + G1VerifyAll = -1 + }; - G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap) { } + G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap), _enabled_verification_types(G1VerifyAll) { } + + void parse_verification_type(const char* type); + void enable_verification_type(G1VerifyType type); + bool should_verify(G1VerifyType type); // Perform verification. @@ -73,9 +87,9 @@ public: #endif // HEAP_REGION_SET_FORCE_VERIFY void prepare_for_verify(); - double verify(bool guard, const char* msg); - void verify_before_gc(); - void verify_after_gc(); + double verify(G1VerifyType type, VerifyOption vo, const char* msg); + void verify_before_gc(G1VerifyType type); + void verify_after_gc(G1VerifyType type); #ifndef PRODUCT // Make sure that the given bitmap has no marked objects in the diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp index 90432c5f48b..8162faf0c67 100644 --- a/src/hotspot/share/gc/shared/gcArguments.cpp +++ b/src/hotspot/share/gc/shared/gcArguments.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/serial/serialArguments.hpp" +#include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "runtime/arguments.hpp" #include "runtime/globals.hpp" @@ -84,6 +85,12 @@ void GCArguments::select_gc_ergonomically() { #endif // INCLUDE_ALL_GCS } +bool GCArguments::parse_verification_type(const char* type) { + log_warning(gc, verify)("VerifyGCType is not supported by this collector."); + // Return false to avoid multiple warnings. + return false; +} + void GCArguments::initialize_flags() { #if INCLUDE_ALL_GCS if (MinHeapFreeRatio == 100) { @@ -99,6 +106,24 @@ void GCArguments::initialize_flags() { #endif // INCLUDE_ALL_GCS } +void GCArguments::post_heap_initialize() { + if (strlen(VerifyGCType) > 0) { + const char delimiter[] = " ,\n"; + size_t length = strlen(VerifyGCType); + char* type_list = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); + strncpy(type_list, VerifyGCType, length + 1); + char* token = strtok(type_list, delimiter); + while (token != NULL) { + bool success = parse_verification_type(token); + if (!success) { + break; + } + token = strtok(NULL, delimiter); + } + FREE_C_HEAP_ARRAY(char, type_list); + } +} + jint GCArguments::initialize() { assert(!is_initialized(), "GC arguments already initialized"); diff --git a/src/hotspot/share/gc/shared/gcArguments.hpp b/src/hotspot/share/gc/shared/gcArguments.hpp index b04c12d4a85..3a3f89be136 100644 --- a/src/hotspot/share/gc/shared/gcArguments.hpp +++ b/src/hotspot/share/gc/shared/gcArguments.hpp @@ -46,8 +46,16 @@ public: static bool is_initialized(); static GCArguments* arguments(); + void post_heap_initialize(); + virtual void initialize_flags(); + // Collector specific function to allow finer grained verification + // through VerifyGCType. If not overridden the default version will + // warn that the flag is not supported for the given collector. + // Returns true if parsing should continue, false otherwise. + virtual bool parse_verification_type(const char* type); + virtual size_t conservative_max_heap_alignment() = 0; virtual CollectedHeap* create_heap() = 0; diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 6ddf3e42e79..0dbad080700 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -768,6 +768,7 @@ jint Universe::initialize_heap() { } log_info(gc)("Using %s", _collectedHeap->name()); + GCArguments::arguments()->post_heap_initialize(); ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size()); #ifdef _LP64 diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 2222ddfe618..10183f60dcf 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2268,6 +2268,10 @@ public: diagnostic(bool, VerifyDuringGC, false, \ "Verify memory system during GC (between phases)") \ \ + diagnostic(ccstrlist, VerifyGCType, "", \ + "GC type(s) to verify when Verify*GC is enabled." \ + "Available types are collector specific.") \ + \ diagnostic(ccstrlist, VerifySubSet, "", \ "Memory sub-systems to verify when Verify*GC flag(s) " \ "are enabled. One or more sub-systems can be specified " \ diff --git a/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp b/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp new file mode 100644 index 00000000000..9ef0f14f3e6 --- /dev/null +++ b/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/g1/g1HeapVerifier.hpp" +#include "logging/logConfiguration.hpp" +#include "unittest.hpp" + +TEST(G1HeapVerifier, parse) { + G1HeapVerifier verifier(NULL); + + LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(gc, verify)); + + // Default is to verify everything. + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyAll)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull)); + + // Setting one will disable all other. + verifier.parse_verification_type("full"); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll)); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly)); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark)); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed)); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark)); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull)); + + // Verify case sensitivity. + verifier.parse_verification_type("YOUNG-ONLY"); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly)); + verifier.parse_verification_type("young-only"); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly)); + + // Verify perfect match + verifier.parse_verification_type("mixedgc"); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed)); + verifier.parse_verification_type("mixe"); + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed)); + verifier.parse_verification_type("mixed"); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed)); + + // Verify the last three + verifier.parse_verification_type("initial-mark"); + verifier.parse_verification_type("remark"); + verifier.parse_verification_type("cleanup"); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark)); + ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup)); + + // Enabling all is not the same as G1VerifyAll + ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll)); +} diff --git a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java new file mode 100644 index 00000000000..35cf30374f9 --- /dev/null +++ b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2017, 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 TestVerifyGCType + * @summary Test the VerifyGCType flag to ensure basic functionality. + * @key gc + * @requires vm.gc.G1 + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestVerifyGCType + */ + +import java.util.ArrayList; +import java.util.Collections; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import sun.hotspot.WhiteBox; + +public class TestVerifyGCType { + public static final String VERIFY_TAG = "[gc,verify]"; + public static final String VERIFY_BEFORE = "Verifying Before GC"; + public static final String VERIFY_DURING = "Verifying During GC"; + public static final String VERIFY_AFTER = "Verifying After GC"; + + public static void main(String args[]) throws Exception { + testAllVerificationEnabled(); + testAllExplicitlyEnabled(); + testFullAndRemark(); + testConcurrentMark(); + testBadVerificationType(); + testUnsupportedCollector(); + } + + private static void testAllVerificationEnabled() throws Exception { + // Test with all verification enabled + OutputAnalyzer output = testWithVerificationType(new String[0]); + output.shouldHaveExitValue(0); + + verifyCollection("Pause Young", true, false, true, output.getStdout()); + verifyCollection("Pause Initial Mark", true, false, true, output.getStdout()); + verifyCollection("Pause Mixed", true, false, true, output.getStdout()); + verifyCollection("Pause Remark", false, true, false, output.getStdout()); + verifyCollection("Pause Cleanup", false, true, false, output.getStdout()); + verifyCollection("Pause Full", true, true, true, output.getStdout()); + } + + private static void testAllExplicitlyEnabled() throws Exception { + OutputAnalyzer output; + // Test with all explicitly enabled + output = testWithVerificationType(new String[] { + "young-only", "initial-mark", "mixed", "remark", "cleanup", "full"}); + output.shouldHaveExitValue(0); + + verifyCollection("Pause Young", true, false, true, output.getStdout()); + verifyCollection("Pause Initial Mark", true, false, true, output.getStdout()); + verifyCollection("Pause Mixed", true, false, true, output.getStdout()); + verifyCollection("Pause Remark", false, true, false, output.getStdout()); + verifyCollection("Pause Cleanup", false, true, false, output.getStdout()); + verifyCollection("Pause Full", true, true, true, output.getStdout()); + } + + private static void testFullAndRemark() throws Exception { + OutputAnalyzer output; + // Test with full and remark + output = testWithVerificationType(new String[] {"remark", "full"}); + output.shouldHaveExitValue(0); + + verifyCollection("Pause Young", false, false, false, output.getStdout()); + verifyCollection("Pause Initial Mark", false, false, false, output.getStdout()); + verifyCollection("Pause Mixed", false, false, false, output.getStdout()); + verifyCollection("Pause Remark", false, true, false, output.getStdout()); + verifyCollection("Pause Cleanup", false, false, false, output.getStdout()); + verifyCollection("Pause Full", true, true, true, output.getStdout()); + } + + private static void testConcurrentMark() throws Exception { + OutputAnalyzer output; + // Test with full and remark + output = testWithVerificationType(new String[] {"initial-mark", "cleanup", "remark"}); + output.shouldHaveExitValue(0); + + verifyCollection("Pause Young", false, false, false, output.getStdout()); + verifyCollection("Pause Initial Mark", true, false, true, output.getStdout()); + verifyCollection("Pause Mixed", false, false, false, output.getStdout()); + verifyCollection("Pause Remark", false, true, false, output.getStdout()); + verifyCollection("Pause Cleanup", false, true, false, output.getStdout()); + verifyCollection("Pause Full", false, false, false, output.getStdout()); + } + + private static void testBadVerificationType() throws Exception { + OutputAnalyzer output; + // Test bad type + output = testWithVerificationType(new String[] {"old"}); + output.shouldHaveExitValue(0); + + output.shouldMatch("VerifyGCType: '.*' is unknown. Available types are: young-only, initial-mark, mixed, remark, cleanup and full"); + verifyCollection("Pause Young", true, false, true, output.getStdout()); + verifyCollection("Pause Initial Mark", true, false, true, output.getStdout()); + verifyCollection("Pause Mixed", true, false, true, output.getStdout()); + verifyCollection("Pause Remark", false, true, false, output.getStdout()); + verifyCollection("Pause Cleanup", false, true, false, output.getStdout()); + verifyCollection("Pause Full", true, true, true, output.getStdout()); + } + + private static void testUnsupportedCollector() throws Exception { + OutputAnalyzer output; + // Test bad gc + output = testWithBadGC(); + output.shouldHaveExitValue(0); + output.shouldMatch("VerifyGCType is not supported by this collector."); + } + + private static OutputAnalyzer testWithVerificationType(String[] types) throws Exception { + ArrayList<String> basicOpts = new ArrayList<>(); + Collections.addAll(basicOpts, new String[] { + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UseG1GC", + "-XX:+WhiteBoxAPI", + "-XX:+ExplicitGCInvokesConcurrent", + "-Xlog:gc,gc+start,gc+verify=info", + "-XX:+VerifyBeforeGC", + "-XX:+VerifyAfterGC", + "-XX:+VerifyDuringGC"}); + + for(String verifyType : types) { + basicOpts.add("-XX:VerifyGCType="+verifyType); + } + + basicOpts.add(TriggerGCs.class.getName()); + + ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(basicOpts.toArray( + new String[basicOpts.size()])); + OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); + return analyzer; + } + + private static OutputAnalyzer testWithBadGC() throws Exception { + ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(new String[] { + "-XX:+UseParallelGC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:VerifyGCType=full", + "-version"}); + + OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); + return analyzer; + } + + private static void verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data) { + CollectionInfo ci = CollectionInfo.parseFirst(name, data); + Asserts.assertTrue(ci != null, "Expected GC not found: " + name); + + // Verify Before + verifyType(ci, expectBefore, VERIFY_BEFORE); + // Verify During + verifyType(ci, expectDuring, VERIFY_DURING); + // Verify After + verifyType(ci, expectAfter, VERIFY_AFTER); + } + + private static void verifyType(CollectionInfo ci, boolean shouldExist, String pattern) { + if (shouldExist) { + Asserts.assertTrue(ci.containsVerification(pattern), "Missing expected verification for: " + ci.getName()); + } else { + Asserts.assertFalse(ci.containsVerification(pattern), "Found unexpected verification for: " + ci.getName()); + } + } + + public static class CollectionInfo { + String name; + ArrayList<String> verification; + public CollectionInfo(String name) { + this.name = name; + this.verification = new ArrayList<>(); + System.out.println("Created CollectionInfo: " + name); + } + + public String getName() { + return name; + } + + public void addVerification(String verify) { + System.out.println("Adding: " + verify); + verification.add(verify); + } + + public boolean containsVerification(String contains) { + for (String entry : verification) { + if (entry.contains(contains)) { + return true; + } + } + return false; + } + + static CollectionInfo parseFirst(String name, String data) { + CollectionInfo result = null; + int firstIndex = data.indexOf(name); + if (firstIndex == -1) { + return result; + } + int nextIndex = data.indexOf(name, firstIndex + 1); + if (nextIndex == -1) { + return result; + } + // Found an entry for this name + result = new CollectionInfo(name); + String collectionData = data.substring(firstIndex, nextIndex + name.length()); + for (String line : collectionData.split(System.getProperty("line.separator"))) { + if (line.contains(VERIFY_TAG)) { + result.addVerification(line); + } + } + return result; + } + } + + public static class TriggerGCs { + public static void main(String args[]) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + // Trigger the different GCs using the WhiteBox API and System.gc() + // to start a concurrent cycle with -XX:+ExplicitGCInvokesConcurrent. + wb.fullGC(); // full + System.gc(); // initial-mark, remark and cleanup + // Sleep to make sure concurrent cycle is done + Thread.sleep(1000); + wb.youngGC(); // young-only + wb.youngGC(); // mixed + } + } +} From b24fe21f431b243c179b2e5cbf7d4453e3b36593 Mon Sep 17 00:00:00 2001 From: Jamsheed Mohammed C M <jcm@openjdk.org> Date: Fri, 1 Dec 2017 00:57:01 -0800 Subject: [PATCH 106/165] 8006887: Comment about LIR_OprDesc.value in c1_LIR.hpp is incorrect Pointer bit description corrected Reviewed-by: kvn, dlong --- src/hotspot/share/c1/c1_LIR.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp index 757c5e79274..980cd4d07dc 100644 --- a/src/hotspot/share/c1/c1_LIR.hpp +++ b/src/hotspot/share/c1/c1_LIR.hpp @@ -196,8 +196,8 @@ class LIR_OprDesc: public CompilationResourceObj { // data opr-type opr-kind // +--------------+-------+-------+ // [max...........|7 6 5 4|3 2 1 0] - // ^ - // is_pointer bit + // ^ + // is_pointer bit // // lowest bit cleared, means it is a structure pointer // we need 4 bits to represent types From 4969284dc640870ff48e06268cd5ef288919df63 Mon Sep 17 00:00:00 2001 From: Jini George <jgeorge@openjdk.org> Date: Fri, 1 Dec 2017 18:19:39 +0530 Subject: [PATCH 107/165] 8191538: SA: tests for clhsdb commands: vmstructsdump, field, symboltable and symbol Create tests for the clhsdb commands: vmstructsdump, field, symboltable and symbol Reviewed-by: sspitsyn, sballal --- .../jtreg/serviceability/sa/ClhsdbField.java | 72 ++++++++++++ .../serviceability/sa/ClhsdbSymbolTable.java | 110 ++++++++++++++++++ .../sa/ClhsdbVmStructsDump.java | 72 ++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbField.java create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java create mode 100644 test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java new file mode 100644 index 00000000000..280c0e64e4f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jdk.test.lib.apps.LingeredApp; + +/* + * @test + * @bug 8191538 + * @summary Test clhsdb 'field' command + * @library /test/lib + * @run main/othervm ClhsdbField + */ + +public class ClhsdbField { + + public static void main(String[] args) throws Exception { + System.out.println("Starting ClhsdbField test"); + + LingeredApp theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + theApp = LingeredApp.startApp(); + System.out.println("Started LingeredApp with pid " + theApp.getPid()); + + List<String> cmds = List.of("field"); + + Map<String, List<String>> expStrMap = new HashMap<>(); + expStrMap.put("field", List.of( + "field ConstantPool _pool_holder InstanceKlass*", + "field InstanceKlass _methods Array<Method*>*", + "field InstanceKlass _constants ConstantPool*", + "field Klass _name Symbol*", + "field JavaThread _next JavaThread*", + "field JavaThread _osthread OSThread*", + "field JVMState _bci", + "field TenuredGeneration _the_space ContiguousSpace*", + "field VirtualSpace _low_boundary char*", + "field MethodCounters _backedge_counter InvocationCounter", + "field nmethod _entry_bci int", + "field Universe _collectedHeap CollectedHeap")); + test.run(theApp.getPid(), cmds, expStrMap, null); + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java new file mode 100644 index 00000000000..679d83243ac --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import jdk.test.lib.apps.LingeredApp; + +/* + * @test + * @bug 8191538 + * @summary Test the clhsdb 'symboltable' and 'symbol' commands + * @library /test/lib + * @run main/othervm ClhsdbSymbolTable + */ + +public class ClhsdbSymbolTable { + + public static void main(String[] args) throws Exception { + System.out.println("Starting the ClhsdbSymbolTable test"); + + LingeredApp theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + + theApp = LingeredApp.startApp(); + System.out.println("Started LingeredApp with pid " + theApp.getPid()); + + // Test the symboltable command + List<String> cmds = List.of( + "symboltable main", + "symboltable java/lang/Class", + "symboltable java/lang/Object", + "symboltable java/lang/String", + "symboltable java/util/List", + "symboltable jdk/test/lib/apps/LingeredApp"); + + Map<String, List<String>> expStrMap = new HashMap<>(); + expStrMap.put("symboltable main", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + expStrMap.put("symboltable java/lang/Class", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + expStrMap.put("symboltable java/lang/Object", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + expStrMap.put("symboltable java/lang/String", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + expStrMap.put("symboltable java/util/List", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + expStrMap.put("symboltable jdk/test/lib/apps/LingeredApp", List.of( + "sun.jvm.hotspot.oops.Symbol@")); + String consolidatedOutput = + test.run(theApp.getPid(), cmds, expStrMap, null); + + // Test the 'symbol' command passing in the address obtained from + // the 'symboltable' command + expStrMap = new HashMap<>(); + cmds = new ArrayList<String>(); + int expectedStringsIdx = 0; + String expectedStrings[] = {"#main", + "#java/lang/Class", "#java/lang/Object", + "#java/lang/String", "#java/util/List", + "#jdk/test/lib/apps/LingeredApp"}; + if (consolidatedOutput != null) { + // Output could be null due to attach permission issues + // and if we are skipping this. + String[] singleCommandOutputs = consolidatedOutput.split("hsdb>"); + + for (String singleCommandOutput : singleCommandOutputs) { + if (singleCommandOutput.contains("@")) { + String[] tokens = singleCommandOutput.split("@"); + String addressString = tokens[1].replace("\n",""); + + // tokens[1] represents the address of the symbol + String cmd = "symbol " + addressString; + cmds.add(cmd); + expStrMap.put(cmd, List.of + (expectedStrings[expectedStringsIdx++])); + } + } + test.run(theApp.getPid(), cmds, expStrMap, null); + } + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java new file mode 100644 index 00000000000..02e832d885f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jdk.test.lib.apps.LingeredApp; + +/* + * @test + * @bug 8191538 + * @summary Test clhsdb 'vmstructsdump' command + * @library /test/lib + * @run main/othervm ClhsdbVmStructsDump + */ + +public class ClhsdbVmStructsDump { + + public static void main(String[] args) throws Exception { + System.out.println("Starting ClhsdbVmStructsDump test"); + + LingeredApp theApp = null; + try { + ClhsdbLauncher test = new ClhsdbLauncher(); + theApp = LingeredApp.startApp(); + System.out.println("Started LingeredApp with pid " + theApp.getPid()); + + List<String> cmds = List.of("vmstructsdump"); + + Map<String, List<String>> expStrMap = new HashMap<>(); + expStrMap.put("vmstructsdump", List.of( + "field ConstantPool _pool_holder InstanceKlass*", + "field InstanceKlass _methods Array<Method*>*", + "field InstanceKlass _constants ConstantPool*", + "field Klass _name Symbol*", + "type ClassLoaderData* null", + "type DictionaryEntry KlassHashtableEntry", + "field JavaThread _next JavaThread*", + "field JavaThread _osthread OSThread*", + "type TenuredGeneration CardGeneration", + "field JVMState _bci", + "type Universe null", + "type ConstantPoolCache MetaspaceObj")); + test.run(theApp.getPid(), cmds, expStrMap, null); + } catch (Exception ex) { + throw new RuntimeException("Test ERROR " + ex, ex); + } finally { + LingeredApp.stopApp(theApp); + } + System.out.println("Test PASSED"); + } +} From d24ef2f18fa4046efd4aea01573b4b6656f3d4d4 Mon Sep 17 00:00:00 2001 From: Dmitry Chuyko <dchuyko@openjdk.org> Date: Fri, 1 Dec 2017 18:20:00 +0300 Subject: [PATCH 108/165] 8191129: AARCH64: Invalid value passed to critical JNI function Reviewed-by: vlivanov --- src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp | 2 +- src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 2 ++ .../criticalnatives/argumentcorruption/CheckLongArgs.java | 3 ++- .../jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 8cc86c221fc..0a5b696b575 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -1664,7 +1664,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // critical natives they are offset down. GrowableArray<int> arg_order(2 * total_in_args); VMRegPair tmp_vmreg; - tmp_vmreg.set1(r19->as_VMReg()); + tmp_vmreg.set2(r19->as_VMReg()); if (!is_critical_native) { for (int i = total_in_args - 1, c_arg = total_c_args - 1; i >= 0; i--, c_arg--) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 8480e4e8e71..c16783d9713 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -384,4 +384,6 @@ void VM_Version::initialize() { g.generate_getPsrInfo()); get_processor_features(); + + UNSUPPORTED_OPTION(CriticalJNINatives); } diff --git a/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java b/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java index cbe0235c4e0..59acd39043e 100644 --- a/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java +++ b/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java @@ -24,7 +24,8 @@ /* @test * @bug 8167409 - * @run main/othervm/native -Xcomp compiler.runtime.criticalnatives.argumentcorruption.CheckLongArgs + * @requires os.arch != "aarch64" + * @run main/othervm/native -Xcomp -XX:+CriticalJNINatives compiler.runtime.criticalnatives.argumentcorruption.CheckLongArgs */ package compiler.runtime.criticalnatives.argumentcorruption; public class CheckLongArgs { diff --git a/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java b/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java index 17c9a0f9b90..20b7af1726c 100644 --- a/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java +++ b/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java @@ -24,7 +24,8 @@ /* @test * @bug 8167408 - * @run main/othervm/native -Xcomp compiler.runtime.criticalnatives.lookup.LookUp + * @requires os.arch != "aarch64" + * @run main/othervm/native -Xcomp -XX:+CriticalJNINatives compiler.runtime.criticalnatives.lookup.LookUp */ package compiler.runtime.criticalnatives.lookup; public class LookUp { From f4b397b4ba37a6b39044448ba2d45d8e404e270f Mon Sep 17 00:00:00 2001 From: Boris Ulasevich <boris.ulasevich@bell-sw.com> Date: Fri, 1 Dec 2017 18:24:52 +0300 Subject: [PATCH 109/165] 8189439: Parameters type profiling is not performed from aarch64 interpreter Reviewed-by: vlivanov --- .../cpu/aarch64/interp_masm_aarch64.cpp | 6 +- .../cpu/aarch64/macroAssembler_aarch64.hpp | 8 +- .../templateInterpreterGenerator_aarch64.cpp | 8 + .../compiler/profiling/TestTypeProfiling.java | 146 ++++++++++++++++++ 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 39c41c3e21d..550bf100764 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1606,6 +1606,7 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result, } void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { + assert_different_registers(obj, rscratch1); Label update, next, none; verify_oop(obj); @@ -1766,6 +1767,7 @@ void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, } void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register tmp1, Register tmp2) { + assert_different_registers(rscratch1, rscratch2, mdp, tmp1, tmp2); if (ProfileInterpreter && MethodData::profile_parameters()) { Label profile_continue, done; @@ -1773,8 +1775,8 @@ void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register t // Load the offset of the area within the MDO used for // parameters. If it's negative we're not profiling any parameters - ldr(tmp1, Address(mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset()))); - tbnz(tmp1, 63, profile_continue); // i.e. sign bit set + ldrw(tmp1, Address(mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset()))); + tbnz(tmp1, 31, profile_continue); // i.e. sign bit set // Compute a pointer to the area for parameters from the offset // and move the pointer to the slot for the last diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 3db1711eea3..94c8c037164 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -998,12 +998,12 @@ public: void atomic_xchgalw(Register prev, Register newv, Register addr); void orptr(Address adr, RegisterOrConstant src) { - ldr(rscratch2, adr); + ldr(rscratch1, adr); if (src.is_register()) - orr(rscratch2, rscratch2, src.as_register()); + orr(rscratch1, rscratch1, src.as_register()); else - orr(rscratch2, rscratch2, src.as_constant()); - str(rscratch2, adr); + orr(rscratch1, rscratch1, src.as_constant()); + str(rscratch1, adr); } // A generic CAS; success or failure is in the EQ flag. diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index b8ac5ecbcd7..f33c6e513a8 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1662,6 +1662,14 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { __ mov(rscratch2, true); __ strb(rscratch2, do_not_unlock_if_synchronized); + Label no_mdp; + Register mdp = r3; + __ ldr(mdp, Address(rmethod, Method::method_data_offset())); + __ cbz(mdp, no_mdp); + __ add(mdp, mdp, in_bytes(MethodData::data_offset())); + __ profile_parameters_type(mdp, r1, r2); + __ bind(no_mdp); + // increment invocation count & check for overflow Label invocation_counter_overflow; Label profile_method; diff --git a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java new file mode 100644 index 00000000000..5d41c83d0fa --- /dev/null +++ b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018, 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 8189439 + * @summary Parameters type profiling is not performed from aarch64 interpreter + * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.emulatedClient & !vm.graal.enabled + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement + * -server -XX:-TieredCompilation -XX:TypeProfileLevel=020 + * compiler.profiling.TestTypeProfiling + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement + * -server -XX:-TieredCompilation -XX:TypeProfileLevel=200 + * compiler.profiling.TestTypeProfiling + */ + +package compiler.profiling; + +import jdk.test.lib.Platform; +import sun.hotspot.WhiteBox; +import compiler.whitebox.CompilerWhiteBoxTest; +import java.lang.reflect.Method; + +public class TestTypeProfiling { + + public static int[] mParamTypeCheck(Object in) { + try { + return (int[]) in; + } catch (ClassCastException cce) { + return null; + } + } + + static Object x2(Object src) { + return src; + } + + public static int[] mRetTypeCheck(Object in) { + try { + Object out = x2(in); + return (int[]) out; + } catch (ClassCastException cce) { + return null; + } + } + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final int TIERED_STOP_AT_LEVEL = WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue(); + + static boolean deoptimize(Method method, Object src_obj) throws Exception { + for (int i = 0; i < 10; i++) { + method.invoke(null, src_obj); + if (!WHITE_BOX.isMethodCompiled(method)) { + return true; + } + } + return false; + } + + static public void main(String[] args) throws Exception { + if (!Platform.isServer() || Platform.isEmulatedClient()) { + throw new Error("TESTBUG: Not server mode"); + } + // Only execute if C2 is available + if (TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { + throw new RuntimeException("please enable C2"); + } + + Method method; + if (WHITE_BOX.getUintxVMFlag("TypeProfileLevel") == 20) { + method = TestTypeProfiling.class.getMethod("mRetTypeCheck", Object.class); + } else + if (WHITE_BOX.getUintxVMFlag("TypeProfileLevel") == 200) { + method = TestTypeProfiling.class.getMethod("mParamTypeCheck", Object.class); + } else { + throw new RuntimeException("please setup method return/params type profilation: -XX:TypeProfileLevel=020/200"); + } + + int[] src = new int[10]; + Object src_obj = new Object(); + + // Warm up & make sure we collect type profiling + for (int i = 0; i < 20000; i++) { + mParamTypeCheck(src); + mRetTypeCheck(src); + } + + // And make sure the method is compiled by C2 + WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new RuntimeException(method.getName() + " is not compiled"); + } + + // should deoptimize for speculative type check + if (!deoptimize(method, src_obj)) { + throw new RuntimeException(method.getName() + " is not deoptimized"); + } + + // compile again + WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new RuntimeException(method.getName() + " is not recompiled"); + } + + // should deoptimize for actual type check + if (!deoptimize(method, src_obj)) { + throw new RuntimeException(method.getName() + " is not deoptimized (should deoptimize for actual type check)"); + } + + // compile once again + WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new RuntimeException(method.getName() + " is not recompiled"); + } + + // this time new parameter type should not force deoptimization + if (deoptimize(method, src_obj)) { + throw new RuntimeException(method.getName() + " is deoptimized again"); + } + } +} From 7c89ccfeb81e768c10bcdb265bc727934fc7f66c Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" <dcubed@openjdk.org> Date: Fri, 1 Dec 2017 11:00:46 -0500 Subject: [PATCH 110/165] 8192810: EnableThreadSMRStatistics should be default off in release builds Reviewed-by: gtriantafill, coleenp, eosterlund --- src/hotspot/share/runtime/globals.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 10183f60dcf..ad4b27a3b0b 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2488,7 +2488,7 @@ public: diagnostic(bool, EnableThreadSMRExtraValidityChecks, true, \ "Enable Thread SMR extra validity checks") \ \ - diagnostic(bool, EnableThreadSMRStatistics, true, \ + diagnostic(bool, EnableThreadSMRStatistics, trueInDebug, \ "Enable Thread SMR Statistics") \ \ product(bool, Inline, true, \ From 8fd0feffb9244f8ebcbbc62cc3a9b3d1f5efb7df Mon Sep 17 00:00:00 2001 From: Erik Gahlin <egahlin@openjdk.org> Date: Fri, 1 Dec 2017 17:03:07 +0100 Subject: [PATCH 111/165] 8179083: Uninitialized notifier in Java Monitor Wait tracing event Reviewed-by: mgronlun --- src/hotspot/share/runtime/objectMonitor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index ca5a22129f3..7f659bfecb1 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -2152,6 +2152,7 @@ ObjectWaiter::ObjectWaiter(Thread* thread) { _next = NULL; _prev = NULL; _notified = 0; + _notifier_tid = 0; TState = TS_RUN; _thread = thread; _event = thread->_ParkEvent; From d8284abf493acee375a0ef29c35cd70ae5182b49 Mon Sep 17 00:00:00 2001 From: Lutz Schmidt <lucy@openjdk.org> Date: Fri, 1 Dec 2017 17:09:43 +0100 Subject: [PATCH 112/165] 8192818: [s390]: restoring register contents calculates wrong value Reviewed-by: goetz, mdoerr --- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 069e54816ea..afc7a7667e2 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -5041,6 +5041,7 @@ unsigned int MacroAssembler::string_compress(Register result, Register src, Regi z_bru(VectorDone); bind(VectorBreak); + add2reg(Rsrc, -min_vcnt*2); // Fix Rsrc. Rsrc was already updated, but Rdst and Rix are not. z_sll(Rix, log_min_vcnt); // # chars processed so far in VectorLoop, excl. current iteration. z_sr(Z_R0, Rix); // correct # chars processed in total. From 1fd81dac32bdfa09c47f36c6cc66a0cf75e39521 Mon Sep 17 00:00:00 2001 From: Martin Doerr <mdoerr@openjdk.org> Date: Fri, 1 Dec 2017 17:10:33 +0100 Subject: [PATCH 113/165] 8192825: PPC64: Missing null check in C1 inline cache check Reviewed-by: goetz --- src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 631b20b81a5..875c5dfdfdf 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -41,20 +41,25 @@ void C1_MacroAssembler::inline_cache_check(Register receiver, Register iCache) { const Register temp_reg = R12_scratch2; + Label Lmiss; + verify_oop(receiver); + MacroAssembler::null_check(receiver, oopDesc::klass_offset_in_bytes(), &Lmiss); load_klass(temp_reg, receiver); - if (TrapBasedICMissChecks) { + + if (TrapBasedICMissChecks && TrapBasedNullChecks) { trap_ic_miss_check(temp_reg, iCache); } else { - Label L; + Label Lok; cmpd(CCR0, temp_reg, iCache); - beq(CCR0, L); + beq(CCR0, Lok); + bind(Lmiss); //load_const_optimized(temp_reg, SharedRuntime::get_ic_miss_stub(), R0); calculate_address_from_global_toc(temp_reg, SharedRuntime::get_ic_miss_stub(), true, true, false); mtctr(temp_reg); bctr(); align(32, 12); - bind(L); + bind(Lok); } } From f8f83d77a8038500e7edce416c36471fb9f04906 Mon Sep 17 00:00:00 2001 From: Martin Doerr <mdoerr@openjdk.org> Date: Fri, 1 Dec 2017 11:26:22 -0500 Subject: [PATCH 114/165] 8192898: AIX build broken after JDK-8190308 Reviewed-by: coleenp --- src/hotspot/os/posix/os_posix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 089ae4bc602..8c7cf351ef0 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -200,7 +200,7 @@ int os::create_file_for_heap(const char* dir) { static char* reserve_mmapped_memory(size_t bytes, char* requested_addr) { char * addr; - int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS; + int flags = MAP_PRIVATE NOT_AIX( | MAP_NORESERVE ) | MAP_ANONYMOUS; if (requested_addr != NULL) { assert((uintptr_t)requested_addr % os::vm_page_size() == 0, "Requested address should be aligned to OS page size"); flags |= MAP_FIXED; From d17b9f871dc46550381c3e6f13e508c442bf8b0c Mon Sep 17 00:00:00 2001 From: Dean Long <dlong@openjdk.org> Date: Fri, 1 Dec 2017 11:17:45 -0800 Subject: [PATCH 115/165] 8192814: Update Graal Reviewed-by: kvn --- .../compiler/asm/amd64/AMD64Assembler.java | 109 +- .../aarch64/AArch64AddressLoweringByUse.java | 10 +- .../core/aarch64/AArch64AddressNode.java | 3 +- .../core/aarch64/AArch64NodeMatchRules.java | 3 +- .../core/amd64/test/AMD64AllocatorTest.java | 2 +- .../core/amd64/AMD64AddressLowering.java | 74 +- .../compiler/core/amd64/AMD64AddressNode.java | 3 +- .../amd64/AMD64ArithmeticLIRGenerator.java | 22 +- .../compiler/core/amd64/AMD64MoveFactory.java | 3 +- .../core/amd64/AMD64NodeMatchRules.java | 13 +- .../core/common/type/StampFactory.java | 54 +- .../core/sparc/SPARCAddressLowering.java | 5 - .../core/sparc/SPARCImmediateAddressNode.java | 3 +- .../core/sparc/SPARCIndexedAddressNode.java | 3 +- ...RCIntegerCompareCanonicalizationPhase.java | 3 +- .../compiler/core/test/CountedLoopTest.java | 3 +- .../core/test/NodePropertiesTest.java | 3 +- .../test/UncheckedInterfaceProviderTest.java | 146 +++ .../core/test/deopt/CompiledMethodTest.java | 35 +- .../test/ea/PartialEscapeUnsafeStoreTest.java | 1001 +++++++++++++++++ .../inlining/PolymorphicInliningTest.java | 358 ++++++ .../compiler/core/gen/NodeLIRBuilder.java | 25 +- .../compiler/debug/test/VersionsTest.java | 199 ++++ .../graalvm/compiler/debug/DebugContext.java | 14 + .../org/graalvm/compiler/debug/Versions.java | 93 ++ .../aarch64/AArch64HotSpotNodeLIRBuilder.java | 5 +- .../amd64/test/DataPatchInConstantsTest.java | 7 +- .../amd64/AMD64HotSpotAddressLowering.java | 12 +- .../amd64/AMD64HotSpotNodeLIRBuilder.java | 5 +- .../sparc/SPARCHotSpotNodeLIRBuilder.java | 3 +- .../test/AheadOfTimeCompilationTest.java | 32 +- .../hotspot/test/CheckGraalIntrinsics.java | 567 ++++++---- .../hotspot/test/CompilationWrapperTest.java | 7 +- .../test/HotSpotStackIntrospectionTest.java | 104 ++ .../test/HotSpotUnsafeSubstitutionTest.java | 3 +- .../meta/DefaultHotSpotLoweringProvider.java | 24 +- .../meta/HotSpotGraphBuilderPlugins.java | 3 +- .../meta/HotSpotInvokeDynamicPlugin.java | 3 +- .../HotSpotSnippetReflectionProvider.java | 2 +- .../meta/HotSpotWordOperationPlugin.java | 11 +- .../hotspot/nodes/HotSpotCompressionNode.java | 3 +- .../nodes/aot/InitializeKlassNode.java | 3 +- .../nodes/aot/InitializeKlassStubCall.java | 3 +- .../aot/LoadConstantIndirectlyFixedNode.java | 5 +- .../nodes/aot/LoadConstantIndirectlyNode.java | 5 +- .../nodes/aot/ResolveConstantNode.java | 5 +- .../nodes/aot/ResolveConstantStubCall.java | 5 +- .../nodes/aot/ResolveDynamicStubCall.java | 3 +- .../phases/OnStackReplacementPhase.java | 5 +- .../profiling/FinalizeProfileNodesPhase.java | 3 +- .../hotspot/replacements/ClassGetHubNode.java | 3 +- .../replacements/HotSpotReplacementsUtil.java | 14 +- .../replacements/IdentityHashCodeNode.java | 3 +- .../replacements/InstanceOfSnippets.java | 3 +- .../replacements/KlassLayoutHelperNode.java | 7 +- .../hotspot/replacements/MonitorSnippets.java | 5 +- .../hotspot/replacements/ObjectCloneNode.java | 17 +- .../replacements/WriteBarrierSnippets.java | 7 +- .../arraycopy/ArrayCopyCallNode.java | 11 +- .../arraycopy/ArrayCopySnippets.java | 69 +- .../arraycopy/CheckcastArrayCopyCallNode.java | 10 +- .../arraycopy/GenericArrayCopyCallNode.java | 5 +- .../compiler/hotspot/stubs/SnippetStub.java | 3 +- .../hotspot/word/PointerCastNode.java | 3 +- .../graalvm/compiler/java/BytecodeParser.java | 123 +- .../compiler/java/FrameStateBuilder.java | 5 +- .../jdk/Unsafe_compareAndSwapNullCheck.java | 3 - .../graalvm/compiler/lir/amd64/AMD64Move.java | 11 +- .../compiler/lir/LIRIntrospection.java | 2 +- .../lsra/LinearScanLifetimeAnalysisPhase.java | 160 +-- .../compiler/lir/constopt/ConstantTree.java | 2 +- .../compiler/loop/BasicInductionVariable.java | 19 +- .../compiler/loop/CountedLoopInfo.java | 9 +- .../DerivedConvertedInductionVariable.java | 7 +- .../loop/DerivedOffsetInductionVariable.java | 5 +- .../loop/DerivedScaledInductionVariable.java | 7 +- .../compiler/loop/InductionVariable.java | 3 +- .../src/org/graalvm/compiler/loop/LoopEx.java | 15 +- .../graalvm/compiler/loop/LoopFragment.java | 3 +- .../compiler/loop/LoopFragmentInside.java | 7 +- .../org/graalvm/compiler/loop/MathUtil.java | 18 +- .../processor/GraphNodeProcessor.java | 1 - .../compiler/nodes/test/IntegerStampTest.java | 53 +- .../test/NegateNodeCanonicalizationTest.java | 15 +- .../ReinterpretStampDoubleToLongTest.java | 6 +- .../test/ReinterpretStampFloatToIntTest.java | 6 +- .../test/ReinterpretStampIntToFloatTest.java | 6 +- .../ReinterpretStampLongToDoubleTest.java | 6 +- .../compiler/nodes/CompressionNode.java | 9 +- .../graalvm/compiler/nodes/ConstantNode.java | 4 +- .../compiler/nodes/EntryProxyNode.java | 2 +- .../graalvm/compiler/nodes/GraphDecoder.java | 8 +- .../compiler/nodes/GuardedValueNode.java | 8 +- .../org/graalvm/compiler/nodes/IfNode.java | 63 +- .../graalvm/compiler/nodes/LoopBeginNode.java | 2 +- .../org/graalvm/compiler/nodes/NodeView.java | 71 ++ .../org/graalvm/compiler/nodes/PhiNode.java | 2 +- .../org/graalvm/compiler/nodes/PiNode.java | 28 +- .../compiler/nodes/StructuredGraph.java | 4 +- .../org/graalvm/compiler/nodes/ValueNode.java | 12 +- .../graalvm/compiler/nodes/ValuePhiNode.java | 8 +- .../compiler/nodes/ValueProxyNode.java | 4 +- .../graalvm/compiler/nodes/calc/AbsNode.java | 18 + .../graalvm/compiler/nodes/calc/AddNode.java | 24 +- .../graalvm/compiler/nodes/calc/AndNode.java | 22 +- .../nodes/calc/BinaryArithmeticNode.java | 55 +- .../compiler/nodes/calc/BinaryNode.java | 3 +- .../compiler/nodes/calc/CompareNode.java | 80 +- .../compiler/nodes/calc/ConditionalNode.java | 58 +- .../compiler/nodes/calc/FloatConvertNode.java | 7 +- .../compiler/nodes/calc/FloatDivNode.java | 9 +- .../compiler/nodes/calc/FloatEqualsNode.java | 36 +- .../nodes/calc/FloatLessThanNode.java | 28 +- .../compiler/nodes/calc/IntegerBelowNode.java | 23 +- .../nodes/calc/IntegerConvertNode.java | 35 +- .../nodes/calc/IntegerDivRemNode.java | 3 +- .../nodes/calc/IntegerEqualsNode.java | 66 +- .../nodes/calc/IntegerLessThanNode.java | 54 +- .../nodes/calc/IntegerLowerThanNode.java | 55 +- .../compiler/nodes/calc/IntegerTestNode.java | 8 +- .../compiler/nodes/calc/IsNullNode.java | 7 +- .../compiler/nodes/calc/LeftShiftNode.java | 11 +- .../graalvm/compiler/nodes/calc/MulNode.java | 105 +- .../compiler/nodes/calc/NarrowNode.java | 26 +- .../compiler/nodes/calc/NegateNode.java | 13 +- .../nodes/calc/NormalizeCompareNode.java | 6 +- .../graalvm/compiler/nodes/calc/NotNode.java | 19 +- .../compiler/nodes/calc/ObjectEqualsNode.java | 30 +- .../graalvm/compiler/nodes/calc/OrNode.java | 20 +- .../nodes/calc/PointerEqualsNode.java | 28 +- .../compiler/nodes/calc/ReinterpretNode.java | 36 +- .../graalvm/compiler/nodes/calc/RemNode.java | 16 +- .../compiler/nodes/calc/RightShiftNode.java | 24 +- .../compiler/nodes/calc/ShiftNode.java | 17 +- .../compiler/nodes/calc/SignExtendNode.java | 32 +- .../compiler/nodes/calc/SignedDivNode.java | 55 +- .../compiler/nodes/calc/SignedRemNode.java | 43 +- .../graalvm/compiler/nodes/calc/SqrtNode.java | 12 +- .../graalvm/compiler/nodes/calc/SubNode.java | 39 +- .../nodes/calc/UnaryArithmeticNode.java | 9 +- .../compiler/nodes/calc/UnaryNode.java | 5 +- .../nodes/calc/UnpackEndianHalfNode.java | 6 +- .../compiler/nodes/calc/UnsignedDivNode.java | 24 +- .../compiler/nodes/calc/UnsignedRemNode.java | 28 +- .../nodes/calc/UnsignedRightShiftNode.java | 35 +- .../graalvm/compiler/nodes/calc/XorNode.java | 22 +- .../compiler/nodes/calc/ZeroExtendNode.java | 30 +- .../compiler/nodes/debug/BlackholeNode.java | 4 + .../compiler/nodes/debug/OpaqueNode.java | 3 +- .../compiler/nodes/extended/BoxNode.java | 3 +- .../nodes/extended/BranchProbabilityNode.java | 3 +- .../nodes/extended/FixedValueAnchorNode.java | 5 +- .../compiler/nodes/extended/GetClassNode.java | 14 +- .../nodes/extended/IntegerSwitchNode.java | 6 +- .../compiler/nodes/extended/LoadHubNode.java | 10 +- .../nodes/extended/LoadMethodNode.java | 8 +- .../compiler/nodes/extended/RawLoadNode.java | 14 +- .../compiler/nodes/extended/SwitchNode.java | 4 +- .../graphbuilderconf/GraphBuilderContext.java | 5 +- .../java/AbstractCompareAndSwapNode.java | 3 +- .../nodes/java/DynamicNewInstanceNode.java | 3 +- .../nodes/java/ExceptionObjectNode.java | 5 +- .../compiler/nodes/java/InstanceOfNode.java | 12 +- .../compiler/nodes/java/LoadFieldNode.java | 8 +- .../compiler/nodes/java/LoadIndexedNode.java | 7 +- .../nodes/java/LogicCompareAndSwapNode.java | 5 +- .../java/LoweredAtomicReadAndWriteNode.java | 5 +- .../nodes/java/MethodCallTargetNode.java | 3 +- .../compiler/nodes/java/NewArrayNode.java | 6 +- .../nodes/java/RawMonitorEnterNode.java | 3 +- .../nodes/java/RegisterFinalizerNode.java | 6 +- .../compiler/nodes/java/TypeSwitchNode.java | 8 +- .../nodes/java/UnsafeCompareAndSwapNode.java | 3 +- .../nodes/java/ValueCompareAndSwapNode.java | 5 +- .../graalvm/compiler/nodes/memory/Access.java | 2 + .../nodes/memory/FixedAccessNode.java | 1 + .../nodes/memory/FloatingAccessNode.java | 6 + .../nodes/memory/FloatingReadNode.java | 11 +- .../compiler/nodes/memory/ReadNode.java | 12 +- .../compiler/nodes/memory/WriteNode.java | 5 +- .../memory/address/OffsetAddressNode.java | 25 +- .../nodes/memory/address/RawAddressNode.java | 67 -- .../compiler/nodes/type/StampTool.java | 17 +- .../compiler/nodes/util/GraphUtil.java | 3 +- .../nodes/virtual/CommitAllocationNode.java | 3 +- .../nodes/virtual/EnsureVirtualizedNode.java | 3 +- .../common/AddressLoweringByUsePhase.java | 15 +- .../phases/common/AddressLoweringPhase.java | 9 +- .../phases/common/CanonicalizerPhase.java | 19 +- .../common/ConditionalEliminationPhase.java | 33 +- .../phases/common/ExpandLogicPhase.java | 13 +- .../compiler/phases/common/FixReadsPhase.java | 11 +- .../phases/common/NonNullParametersPhase.java | 5 +- .../common/ProfileCompiledMethodsPhase.java | 4 +- .../phases/common/inlining/InliningUtil.java | 3 +- .../info/MultiTypeGuardInlineInfo.java | 3 +- .../inlining/info/TypeGuardInlineInfo.java | 5 +- .../inlining/info/elem/InlineableGraph.java | 7 +- .../common/inlining/walker/InliningData.java | 5 +- .../compiler/phases/graph/InferStamps.java | 12 +- .../compiler/phases/util/ValueMergeUtil.java | 3 +- .../phases/verify/VerifyDebugUsage.java | 3 +- .../phases/verify/VerifyUsageWithEquals.java | 3 +- .../verify/VerifyVirtualizableUsage.java | 3 +- .../compiler/printer/BinaryGraphPrinter.java | 2 +- .../aarch64/AArch64CountLeadingZerosNode.java | 5 +- .../AArch64CountTrailingZerosNode.java | 5 +- .../AArch64FloatArithmeticSnippets.java | 3 +- .../AArch64IntegerArithmeticSnippets.java | 3 +- .../replacements/aarch64/AArch64ReadNode.java | 5 +- .../amd64/AMD64CountLeadingZerosNode.java | 5 +- .../amd64/AMD64CountTrailingZerosNode.java | 5 +- .../replacements/amd64/AMD64RoundNode.java | 5 +- .../replacements/test/BitOpNodesTest.java | 21 +- .../test/IntegerExactFoldTest.java | 5 +- .../test/MethodSubstitutionTest.java | 22 +- .../replacements/test/MonitorTest.java | 3 +- .../replacements/test/ObjectAccessTest.java | 3 +- .../replacements/test/PointerTest.java | 7 +- .../test/SystemArrayCopyTest.java | 126 +++ .../DefaultJavaLoweringProvider.java | 28 +- .../compiler/replacements/GraphKit.java | 5 +- .../InstanceOfSnippetsTemplates.java | 3 +- .../replacements/MethodHandlePlugin.java | 4 + .../compiler/replacements/PEGraphDecoder.java | 5 +- .../replacements/SnippetTemplate.java | 10 +- .../StandardGraphBuilderPlugins.java | 22 +- .../replacements/nodes/ArrayEqualsNode.java | 3 +- .../nodes/BasicArrayCopyNode.java | 7 +- .../nodes/BasicObjectCloneNode.java | 5 +- .../nodes/BinaryMathIntrinsicNode.java | 12 +- .../replacements/nodes/BitCountNode.java | 5 +- .../nodes/BitScanForwardNode.java | 5 +- .../nodes/BitScanReverseNode.java | 5 +- .../replacements/nodes/MethodHandleNode.java | 5 +- .../replacements/nodes/ReadRegisterNode.java | 3 +- .../replacements/nodes/ReverseBytesNode.java | 5 +- .../nodes/UnaryMathIntrinsicNode.java | 5 +- .../nodes/arithmetic/IntegerAddExactNode.java | 9 +- .../arithmetic/IntegerAddExactSplitNode.java | 4 +- .../nodes/arithmetic/IntegerMulExactNode.java | 11 +- .../arithmetic/IntegerMulExactSplitNode.java | 4 +- .../nodes/arithmetic/IntegerMulHighNode.java | 3 +- .../nodes/arithmetic/IntegerSubExactNode.java | 11 +- .../arithmetic/IntegerSubExactSplitNode.java | 4 +- .../nodes/arithmetic/UnsignedMulHighNode.java | 3 +- .../virtual/phases/ea/GraphEffectList.java | 8 +- .../ea/PEReadEliminationBlockState.java | 3 +- .../phases/ea/PEReadEliminationClosure.java | 13 +- .../phases/ea/PartialEscapeClosure.java | 7 +- .../phases/ea/ReadEliminationClosure.java | 15 +- .../phases/ea/VirtualizerToolImpl.java | 5 +- .../graalvm/compiler/word/WordCastNode.java | 15 +- 253 files changed, 4413 insertions(+), 1710 deletions(-) create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java delete mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java create mode 100644 src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java index 04e0e22182d..089cb3f4b0e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java @@ -352,17 +352,18 @@ public class AMD64Assembler extends Assembler { */ private enum OpAssertion { ByteAssertion(CPU, CPU, BYTE), - IntegerAssertion(CPU, CPU, WORD, DWORD, QWORD), - No16BitAssertion(CPU, CPU, DWORD, QWORD), - No32BitAssertion(CPU, CPU, WORD, QWORD), - QwordOnlyAssertion(CPU, CPU, QWORD), - FloatingAssertion(XMM, XMM, SS, SD, PS, PD), - PackedFloatingAssertion(XMM, XMM, PS, PD), + ByteOrLargerAssertion(CPU, CPU, BYTE, WORD, DWORD, QWORD), + WordOrLargerAssertion(CPU, CPU, WORD, DWORD, QWORD), + DwordOrLargerAssertion(CPU, CPU, DWORD, QWORD), + WordOrDwordAssertion(CPU, CPU, WORD, QWORD), + QwordAssertion(CPU, CPU, QWORD), + FloatAssertion(XMM, XMM, SS, SD, PS, PD), + PackedFloatAssertion(XMM, XMM, PS, PD), SingleAssertion(XMM, XMM, SS), DoubleAssertion(XMM, XMM, SD), PackedDoubleAssertion(XMM, XMM, PD), - IntToFloatingAssertion(XMM, CPU, DWORD, QWORD), - FloatingToIntAssertion(CPU, XMM, DWORD, QWORD); + IntToFloatAssertion(XMM, CPU, DWORD, QWORD), + FloatToIntAssertion(CPU, XMM, DWORD, QWORD); private final RegisterCategory resultCategory; private final RegisterCategory inputCategory; @@ -772,25 +773,25 @@ public class AMD64Assembler extends Assembler { */ public static class AMD64RMOp extends AMD64RROp { // @formatter:off - public static final AMD64RMOp IMUL = new AMD64RMOp("IMUL", P_0F, 0xAF); + public static final AMD64RMOp IMUL = new AMD64RMOp("IMUL", P_0F, 0xAF, OpAssertion.ByteOrLargerAssertion); public static final AMD64RMOp BSF = new AMD64RMOp("BSF", P_0F, 0xBC); public static final AMD64RMOp BSR = new AMD64RMOp("BSR", P_0F, 0xBD); public static final AMD64RMOp POPCNT = new AMD64RMOp("POPCNT", 0xF3, P_0F, 0xB8, CPUFeature.POPCNT); public static final AMD64RMOp TZCNT = new AMD64RMOp("TZCNT", 0xF3, P_0F, 0xBC, CPUFeature.BMI1); public static final AMD64RMOp LZCNT = new AMD64RMOp("LZCNT", 0xF3, P_0F, 0xBD, CPUFeature.LZCNT); - public static final AMD64RMOp MOVZXB = new AMD64RMOp("MOVZXB", P_0F, 0xB6, false, true, OpAssertion.IntegerAssertion); - public static final AMD64RMOp MOVZX = new AMD64RMOp("MOVZX", P_0F, 0xB7, OpAssertion.No16BitAssertion); - public static final AMD64RMOp MOVSXB = new AMD64RMOp("MOVSXB", P_0F, 0xBE, false, true, OpAssertion.IntegerAssertion); - public static final AMD64RMOp MOVSX = new AMD64RMOp("MOVSX", P_0F, 0xBF, OpAssertion.No16BitAssertion); - public static final AMD64RMOp MOVSXD = new AMD64RMOp("MOVSXD", 0x63, OpAssertion.QwordOnlyAssertion); + public static final AMD64RMOp MOVZXB = new AMD64RMOp("MOVZXB", P_0F, 0xB6, false, true, OpAssertion.WordOrLargerAssertion); + public static final AMD64RMOp MOVZX = new AMD64RMOp("MOVZX", P_0F, 0xB7, OpAssertion.DwordOrLargerAssertion); + public static final AMD64RMOp MOVSXB = new AMD64RMOp("MOVSXB", P_0F, 0xBE, false, true, OpAssertion.WordOrLargerAssertion); + public static final AMD64RMOp MOVSX = new AMD64RMOp("MOVSX", P_0F, 0xBF, OpAssertion.DwordOrLargerAssertion); + public static final AMD64RMOp MOVSXD = new AMD64RMOp("MOVSXD", 0x63, OpAssertion.QwordAssertion); public static final AMD64RMOp MOVB = new AMD64RMOp("MOVB", 0x8A, OpAssertion.ByteAssertion); public static final AMD64RMOp MOV = new AMD64RMOp("MOV", 0x8B); // MOVD/MOVQ and MOVSS/MOVSD are the same opcode, just with different operand size prefix - public static final AMD64RMOp MOVD = new AMD64RMOp("MOVD", 0x66, P_0F, 0x6E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2); - public static final AMD64RMOp MOVQ = new AMD64RMOp("MOVQ", 0x66, P_0F, 0x6E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2); - public static final AMD64RMOp MOVSS = new AMD64RMOp("MOVSS", P_0F, 0x10, OpAssertion.FloatingAssertion, CPUFeature.SSE); - public static final AMD64RMOp MOVSD = new AMD64RMOp("MOVSD", P_0F, 0x10, OpAssertion.FloatingAssertion, CPUFeature.SSE); + public static final AMD64RMOp MOVD = new AMD64RMOp("MOVD", 0x66, P_0F, 0x6E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2); + public static final AMD64RMOp MOVQ = new AMD64RMOp("MOVQ", 0x66, P_0F, 0x6E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2); + public static final AMD64RMOp MOVSS = new AMD64RMOp("MOVSS", P_0F, 0x10, OpAssertion.FloatAssertion, CPUFeature.SSE); + public static final AMD64RMOp MOVSD = new AMD64RMOp("MOVSD", P_0F, 0x10, OpAssertion.FloatAssertion, CPUFeature.SSE); // TEST is documented as MR operation, but it's symmetric, and using it as RM operation is more convenient. public static final AMD64RMOp TESTB = new AMD64RMOp("TEST", 0x84, OpAssertion.ByteAssertion); @@ -822,7 +823,7 @@ public class AMD64Assembler extends Assembler { } protected AMD64RMOp(String opcode, int prefix1, int prefix2, int op, CPUFeature feature) { - this(opcode, prefix1, prefix2, op, OpAssertion.IntegerAssertion, feature); + this(opcode, prefix1, prefix2, op, OpAssertion.WordOrLargerAssertion, feature); } protected AMD64RMOp(String opcode, int prefix1, int prefix2, int op, OpAssertion assertion, CPUFeature feature) { @@ -1014,7 +1015,7 @@ public class AMD64Assembler extends Assembler { } protected AMD64RRMOp(String opcode, int prefix1, int prefix2, int op, CPUFeature feature) { - this(opcode, prefix1, prefix2, op, OpAssertion.IntegerAssertion, feature); + this(opcode, prefix1, prefix2, op, OpAssertion.WordOrLargerAssertion, feature); } protected AMD64RRMOp(String opcode, int prefix1, int prefix2, int op, OpAssertion assertion, CPUFeature feature) { @@ -1114,12 +1115,12 @@ public class AMD64Assembler extends Assembler { // MOVD and MOVQ are the same opcode, just with different operand size prefix // Note that as MR opcodes, they have reverse operand order, so the IntToFloatingAssertion must be used. - public static final AMD64MROp MOVD = new AMD64MROp("MOVD", 0x66, P_0F, 0x7E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2); - public static final AMD64MROp MOVQ = new AMD64MROp("MOVQ", 0x66, P_0F, 0x7E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2); + public static final AMD64MROp MOVD = new AMD64MROp("MOVD", 0x66, P_0F, 0x7E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2); + public static final AMD64MROp MOVQ = new AMD64MROp("MOVQ", 0x66, P_0F, 0x7E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2); // MOVSS and MOVSD are the same opcode, just with different operand size prefix - public static final AMD64MROp MOVSS = new AMD64MROp("MOVSS", P_0F, 0x11, OpAssertion.FloatingAssertion, CPUFeature.SSE); - public static final AMD64MROp MOVSD = new AMD64MROp("MOVSD", P_0F, 0x11, OpAssertion.FloatingAssertion, CPUFeature.SSE); + public static final AMD64MROp MOVSS = new AMD64MROp("MOVSS", P_0F, 0x11, OpAssertion.FloatAssertion, CPUFeature.SSE); + public static final AMD64MROp MOVSD = new AMD64MROp("MOVSD", P_0F, 0x11, OpAssertion.FloatAssertion, CPUFeature.SSE); // @formatter:on protected AMD64MROp(String opcode, int op) { @@ -1131,7 +1132,7 @@ public class AMD64Assembler extends Assembler { } protected AMD64MROp(String opcode, int prefix, int op) { - this(opcode, prefix, op, OpAssertion.IntegerAssertion); + this(opcode, prefix, op, OpAssertion.WordOrLargerAssertion); } protected AMD64MROp(String opcode, int prefix, int op, OpAssertion assertion) { @@ -1279,7 +1280,7 @@ public class AMD64Assembler extends Assembler { public static final AMD64MOp INC = new AMD64MOp("INC", 0xFF, 0); public static final AMD64MOp DEC = new AMD64MOp("DEC", 0xFF, 1); public static final AMD64MOp PUSH = new AMD64MOp("PUSH", 0xFF, 6); - public static final AMD64MOp POP = new AMD64MOp("POP", 0x8F, 0, OpAssertion.No32BitAssertion); + public static final AMD64MOp POP = new AMD64MOp("POP", 0x8F, 0, OpAssertion.WordOrDwordAssertion); // @formatter:on private final int ext; @@ -1289,7 +1290,7 @@ public class AMD64Assembler extends Assembler { } protected AMD64MOp(String opcode, int prefix, int op, int ext) { - this(opcode, prefix, op, ext, OpAssertion.IntegerAssertion); + this(opcode, prefix, op, ext, OpAssertion.WordOrLargerAssertion); } protected AMD64MOp(String opcode, int op, int ext, OpAssertion assertion) { @@ -1327,7 +1328,7 @@ public class AMD64Assembler extends Assembler { private final int ext; protected AMD64MIOp(String opcode, boolean immIsByte, int op, int ext) { - this(opcode, immIsByte, op, ext, OpAssertion.IntegerAssertion); + this(opcode, immIsByte, op, ext, OpAssertion.WordOrLargerAssertion); } protected AMD64MIOp(String opcode, boolean immIsByte, int op, int ext, OpAssertion assertion) { @@ -1369,7 +1370,7 @@ public class AMD64Assembler extends Assembler { // @formatter:on protected AMD64RMIOp(String opcode, boolean immIsByte, int op) { - this(opcode, immIsByte, 0, op, OpAssertion.IntegerAssertion); + this(opcode, immIsByte, 0, op, OpAssertion.WordOrLargerAssertion); } protected AMD64RMIOp(String opcode, boolean immIsByte, int prefix, int op, OpAssertion assertion) { @@ -1504,16 +1505,16 @@ public class AMD64Assembler extends Assembler { public static class SSEOp extends AMD64RMOp { // @formatter:off - public static final SSEOp CVTSI2SS = new SSEOp("CVTSI2SS", 0xF3, P_0F, 0x2A, OpAssertion.IntToFloatingAssertion); - public static final SSEOp CVTSI2SD = new SSEOp("CVTSI2SS", 0xF2, P_0F, 0x2A, OpAssertion.IntToFloatingAssertion); - public static final SSEOp CVTTSS2SI = new SSEOp("CVTTSS2SI", 0xF3, P_0F, 0x2C, OpAssertion.FloatingToIntAssertion); - public static final SSEOp CVTTSD2SI = new SSEOp("CVTTSD2SI", 0xF2, P_0F, 0x2C, OpAssertion.FloatingToIntAssertion); - public static final SSEOp UCOMIS = new SSEOp("UCOMIS", P_0F, 0x2E, OpAssertion.PackedFloatingAssertion); + public static final SSEOp CVTSI2SS = new SSEOp("CVTSI2SS", 0xF3, P_0F, 0x2A, OpAssertion.IntToFloatAssertion); + public static final SSEOp CVTSI2SD = new SSEOp("CVTSI2SS", 0xF2, P_0F, 0x2A, OpAssertion.IntToFloatAssertion); + public static final SSEOp CVTTSS2SI = new SSEOp("CVTTSS2SI", 0xF3, P_0F, 0x2C, OpAssertion.FloatToIntAssertion); + public static final SSEOp CVTTSD2SI = new SSEOp("CVTTSD2SI", 0xF2, P_0F, 0x2C, OpAssertion.FloatToIntAssertion); + public static final SSEOp UCOMIS = new SSEOp("UCOMIS", P_0F, 0x2E, OpAssertion.PackedFloatAssertion); public static final SSEOp SQRT = new SSEOp("SQRT", P_0F, 0x51); - public static final SSEOp AND = new SSEOp("AND", P_0F, 0x54, OpAssertion.PackedFloatingAssertion); - public static final SSEOp ANDN = new SSEOp("ANDN", P_0F, 0x55, OpAssertion.PackedFloatingAssertion); - public static final SSEOp OR = new SSEOp("OR", P_0F, 0x56, OpAssertion.PackedFloatingAssertion); - public static final SSEOp XOR = new SSEOp("XOR", P_0F, 0x57, OpAssertion.PackedFloatingAssertion); + public static final SSEOp AND = new SSEOp("AND", P_0F, 0x54, OpAssertion.PackedFloatAssertion); + public static final SSEOp ANDN = new SSEOp("ANDN", P_0F, 0x55, OpAssertion.PackedFloatAssertion); + public static final SSEOp OR = new SSEOp("OR", P_0F, 0x56, OpAssertion.PackedFloatAssertion); + public static final SSEOp XOR = new SSEOp("XOR", P_0F, 0x57, OpAssertion.PackedFloatAssertion); public static final SSEOp ADD = new SSEOp("ADD", P_0F, 0x58); public static final SSEOp MUL = new SSEOp("MUL", P_0F, 0x59); public static final SSEOp CVTSS2SD = new SSEOp("CVTSS2SD", P_0F, 0x5A, OpAssertion.SingleAssertion); @@ -1525,7 +1526,7 @@ public class AMD64Assembler extends Assembler { // @formatter:on protected SSEOp(String opcode, int prefix, int op) { - this(opcode, prefix, op, OpAssertion.FloatingAssertion); + this(opcode, prefix, op, OpAssertion.FloatAssertion); } protected SSEOp(String opcode, int prefix, int op, OpAssertion assertion) { @@ -1539,10 +1540,10 @@ public class AMD64Assembler extends Assembler { public static class AVXOp extends AMD64RRMOp { // @formatter:off - public static final AVXOp AND = new AVXOp("AND", P_0F, 0x54, OpAssertion.PackedFloatingAssertion); - public static final AVXOp ANDN = new AVXOp("ANDN", P_0F, 0x55, OpAssertion.PackedFloatingAssertion); - public static final AVXOp OR = new AVXOp("OR", P_0F, 0x56, OpAssertion.PackedFloatingAssertion); - public static final AVXOp XOR = new AVXOp("XOR", P_0F, 0x57, OpAssertion.PackedFloatingAssertion); + public static final AVXOp AND = new AVXOp("AND", P_0F, 0x54, OpAssertion.PackedFloatAssertion); + public static final AVXOp ANDN = new AVXOp("ANDN", P_0F, 0x55, OpAssertion.PackedFloatAssertion); + public static final AVXOp OR = new AVXOp("OR", P_0F, 0x56, OpAssertion.PackedFloatAssertion); + public static final AVXOp XOR = new AVXOp("XOR", P_0F, 0x57, OpAssertion.PackedFloatAssertion); public static final AVXOp ADD = new AVXOp("ADD", P_0F, 0x58); public static final AVXOp MUL = new AVXOp("MUL", P_0F, 0x59); public static final AVXOp SUB = new AVXOp("SUB", P_0F, 0x5C); @@ -1552,7 +1553,7 @@ public class AMD64Assembler extends Assembler { // @formatter:on protected AVXOp(String opcode, int prefix, int op) { - this(opcode, prefix, op, OpAssertion.FloatingAssertion); + this(opcode, prefix, op, OpAssertion.FloatAssertion); } protected AVXOp(String opcode, int prefix, int op, OpAssertion assertion) { @@ -1595,10 +1596,10 @@ public class AMD64Assembler extends Assembler { byteMrOp = new AMD64MROp(opcode, 0, baseOp, OpAssertion.ByteAssertion); byteRmOp = new AMD64RMOp(opcode, 0, baseOp | 0x02, OpAssertion.ByteAssertion); - immOp = new AMD64MIOp(opcode, false, 0, 0x81, code, OpAssertion.IntegerAssertion); - immSxOp = new AMD64MIOp(opcode, true, 0, 0x83, code, OpAssertion.IntegerAssertion); - mrOp = new AMD64MROp(opcode, 0, baseOp | 0x01, OpAssertion.IntegerAssertion); - rmOp = new AMD64RMOp(opcode, 0, baseOp | 0x03, OpAssertion.IntegerAssertion); + immOp = new AMD64MIOp(opcode, false, 0, 0x81, code, OpAssertion.WordOrLargerAssertion); + immSxOp = new AMD64MIOp(opcode, true, 0, 0x83, code, OpAssertion.WordOrLargerAssertion); + mrOp = new AMD64MROp(opcode, 0, baseOp | 0x01, OpAssertion.WordOrLargerAssertion); + rmOp = new AMD64RMOp(opcode, 0, baseOp | 0x03, OpAssertion.WordOrLargerAssertion); } public AMD64MIOp getMIOpcode(OperandSize size, boolean sx) { @@ -1647,9 +1648,9 @@ public class AMD64Assembler extends Assembler { public final AMD64MIOp miOp; private AMD64Shift(String opcode, int code) { - m1Op = new AMD64MOp(opcode, 0, 0xD1, code, OpAssertion.IntegerAssertion); - mcOp = new AMD64MOp(opcode, 0, 0xD3, code, OpAssertion.IntegerAssertion); - miOp = new AMD64MIOp(opcode, true, 0, 0xC1, code, OpAssertion.IntegerAssertion); + m1Op = new AMD64MOp(opcode, 0, 0xD1, code, OpAssertion.WordOrLargerAssertion); + mcOp = new AMD64MOp(opcode, 0, 0xD3, code, OpAssertion.WordOrLargerAssertion); + miOp = new AMD64MIOp(opcode, true, 0, 0xC1, code, OpAssertion.WordOrLargerAssertion); } } @@ -1967,6 +1968,12 @@ public class AMD64Assembler extends Assembler { } } + public final void lead(Register dst, AMD64Address src) { + prefix(src, dst); + emitByte(0x8D); + emitOperandHelper(dst, src, 0); + } + public final void leaq(Register dst, AMD64Address src) { prefixq(src, dst); emitByte(0x8D); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java index f584d35d65a..95abe6f0e70 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java @@ -24,8 +24,6 @@ package org.graalvm.compiler.core.aarch64; -import jdk.vm.ci.aarch64.AArch64Kind; -import jdk.vm.ci.meta.JavaConstant; import org.graalvm.compiler.asm.aarch64.AArch64Address; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.NumUtil; @@ -34,9 +32,11 @@ import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; -import org.graalvm.compiler.nodes.memory.address.RawAddressNode; import org.graalvm.compiler.phases.common.AddressLoweringByUsePhase; +import jdk.vm.ci.aarch64.AArch64Kind; +import jdk.vm.ci.meta.JavaConstant; + public class AArch64AddressLoweringByUse extends AddressLoweringByUsePhase.AddressLoweringByUse { private AArch64LIRKindTool kindtool; @@ -46,9 +46,7 @@ public class AArch64AddressLoweringByUse extends AddressLoweringByUsePhase.Addre @Override public AddressNode lower(ValueNode use, Stamp stamp, AddressNode address) { - if (address instanceof RawAddressNode) { - return doLower(stamp, address.getBase(), null); - } else if (address instanceof OffsetAddressNode) { + if (address instanceof OffsetAddressNode) { OffsetAddressNode offsetAddress = (OffsetAddressNode) address; return doLower(stamp, offsetAddress.getBase(), offsetAddress.getOffset()); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java index 3fddb853424..2737f18b574 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.aarch64.AArch64AddressValue; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -88,7 +89,7 @@ public class AArch64AddressNode extends AddressNode implements LIRLowerable { } } - LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference); + LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference); gen.setResult(this, new AArch64AddressValue(kind, baseValue, indexValue, (int) displacement, scaleFactor, addressingMode)); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java index b99cd62bdad..3e98252c3e1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java @@ -27,6 +27,7 @@ import org.graalvm.compiler.core.gen.NodeMatchRules; import org.graalvm.compiler.lir.LIRFrameState; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodes.DeoptimizingNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.memory.Access; import jdk.vm.ci.aarch64.AArch64Kind; @@ -45,7 +46,7 @@ public class AArch64NodeMatchRules extends NodeMatchRules { } protected AArch64Kind getMemoryKind(Access access) { - return (AArch64Kind) gen.getLIRKind(access.asNode().stamp()).getPlatformKind(); + return (AArch64Kind) gen.getLIRKind(access.asNode().stamp(NodeView.DEFAULT)).getPlatformKind(); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java index c95707da994..e3fb54825b0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java @@ -44,7 +44,7 @@ public class AMD64AllocatorTest extends AllocatorTest { @Test public void test1() { - testAllocation("test1snippet", 3, 1, 0); + testAllocation("test1snippet", 3, 0, 0); } public static long test1snippet(long x) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java index 63e535bc853..595e7174ed2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java @@ -25,24 +25,23 @@ package org.graalvm.compiler.core.amd64; import org.graalvm.compiler.asm.amd64.AMD64Address.Scale; import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.compiler.core.common.type.AbstractPointerStamp; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.PrimitiveStamp; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.LeftShiftNode; import org.graalvm.compiler.nodes.calc.NegateNode; -import org.graalvm.compiler.nodes.calc.ZeroExtendNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.phases.common.AddressLoweringPhase.AddressLowering; import jdk.vm.ci.meta.JavaConstant; public class AMD64AddressLowering extends AddressLowering { - @Override - public AddressNode lower(ValueNode address) { - return lower(address, null); - } + private static final int ADDRESS_BITS = 64; @Override public AddressNode lower(ValueNode base, ValueNode offset) { @@ -54,9 +53,15 @@ public class AMD64AddressLowering extends AddressLowering { changed = improve(graph, base.getDebug(), ret, false, false); } while (changed); + assert checkAddressBitWidth(ret.getBase()); + assert checkAddressBitWidth(ret.getIndex()); return graph.unique(ret); } + private static boolean checkAddressBitWidth(ValueNode value) { + return value == null || value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(value.stamp(NodeView.DEFAULT)) == ADDRESS_BITS; + } + /** * Tries to optimize addresses so that they match the AMD64-specific addressing mode better * (base + index * scale + displacement). @@ -148,7 +153,7 @@ public class AMD64AddressLowering extends AddressLowering { if (base == ret.getBase()) { ret.setBase(originalBase); } else if (ret.getBase() != null) { - ret.setBase(graph.maybeAddOrUnique(NegateNode.create(ret.getBase()))); + ret.setBase(graph.maybeAddOrUnique(NegateNode.create(ret.getBase(), NodeView.DEFAULT))); } } @@ -156,7 +161,7 @@ public class AMD64AddressLowering extends AddressLowering { if (index == ret.getIndex()) { ret.setIndex(originalIndex); } else if (ret.getIndex() != null) { - ret.setIndex(graph.maybeAddOrUnique(NegateNode.create(ret.getIndex()))); + ret.setIndex(graph.maybeAddOrUnique(NegateNode.create(ret.getIndex(), NodeView.DEFAULT))); } } return improved; @@ -168,12 +173,12 @@ public class AMD64AddressLowering extends AddressLowering { private static ValueNode considerNegation(StructuredGraph graph, ValueNode value, boolean negate) { if (negate && value != null) { - return graph.maybeAddOrUnique(NegateNode.create(value)); + return graph.maybeAddOrUnique(NegateNode.create(value, NodeView.DEFAULT)); } return value; } - private ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift, boolean negateExtractedDisplacement) { + private static ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift, boolean negateExtractedDisplacement) { if (node == null) { return null; } @@ -182,30 +187,26 @@ public class AMD64AddressLowering extends AddressLowering { if (c != null) { return improveConstDisp(address, node, c, null, shift, negateExtractedDisplacement); } else { - if (node.stamp() instanceof IntegerStamp) { - if (node instanceof ZeroExtendNode && (((ZeroExtendNode) node).getInputBits() == 32)) { - /* - * we can't just swallow all zero-extends as we might encounter something like - * the following: ZeroExtend(Add(negativeValue, positiveValue)). - * - * if we swallow the zero-extend in this case and subsequently optimize the add, - * we might end up with a negative value that has less than 64 bits in base or - * index. such a value would require sign extension instead of zero-extension - * but the backend can only do zero-extension. if we ever want to optimize that - * further, we would also need to be careful about over-/underflows. - * - * furthermore, we also can't swallow zero-extends with less than 32 bits as - * most of these values are immediately sign-extended to 32 bit by the backend - * (therefore, the subsequent implicit zero-extension to 64 bit won't do what we - * expect). - */ - ValueNode value = ((ZeroExtendNode) node).getValue(); - if (!mightBeOptimized(value)) { - // if the value is not optimized further by the address lowering, then we - // can safely rely on the backend doing the implicitly zero-extension. - return value; - } - } + if (node.stamp(NodeView.DEFAULT) instanceof IntegerStamp) { + assert PrimitiveStamp.getBits(node.stamp(NodeView.DEFAULT)) == ADDRESS_BITS; + + /* + * we can't swallow zero-extends because of multiple reasons: + * + * a) we might encounter something like the following: ZeroExtend(Add(negativeValue, + * positiveValue)). if we swallow the zero-extend in this case and subsequently + * optimize the add, we might end up with a negative value that has less than 64 + * bits in base or index. such a value would require sign extension instead of + * zero-extension but the backend can only do (implicit) zero-extension by using a + * larger register (e.g., rax instead of eax). + * + * b) our backend does not guarantee that the upper half of a 64-bit register equals + * 0 if a 32-bit value is stored in there. + * + * c) we also can't swallow zero-extends with less than 32 bits as most of these + * values are immediately sign-extended to 32 bit by the backend (therefore, the + * subsequent implicit zero-extension to 64 bit won't do what we expect). + */ if (node instanceof AddNode) { AddNode add = (AddNode) node; @@ -221,13 +222,6 @@ public class AMD64AddressLowering extends AddressLowering { return node; } - /** - * This method returns true for all nodes that might be optimized by the address lowering. - */ - protected boolean mightBeOptimized(ValueNode value) { - return value instanceof AddNode || value instanceof LeftShiftNode || value instanceof NegateNode || value instanceof ZeroExtendNode; - } - private static ValueNode improveConstDisp(AMD64AddressNode address, ValueNode original, JavaConstant c, ValueNode other, int shift, boolean negateExtractedDisplacement) { if (c.getJavaKind().isNumericInteger()) { long delta = c.asLong() << shift; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java index 90a8258d41c..bd27ef756d3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; @@ -113,7 +114,7 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo } } - LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference); + LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference); gen.setResult(this, new AMD64AddressValue(kind, baseValue, indexValue, scale, displacement)); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java index 11e770b6e50..5b442b1e3bf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java @@ -99,8 +99,9 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.amd64.AMD64Binary; import org.graalvm.compiler.lir.amd64.AMD64BinaryConsumer; import org.graalvm.compiler.lir.amd64.AMD64ClearRegisterOp; -import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicUnaryOp; import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicBinaryOp; +import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicUnaryOp; +import org.graalvm.compiler.lir.amd64.AMD64Move; import org.graalvm.compiler.lir.amd64.AMD64MulDivOp; import org.graalvm.compiler.lir.amd64.AMD64ShiftOp; import org.graalvm.compiler.lir.amd64.AMD64SignExtendOp; @@ -287,14 +288,33 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen return ((AMD64Kind) kind).isInteger(); } + private Variable emitBaseOffsetLea(LIRKind resultKind, Value base, int offset, OperandSize size) { + Variable result = getLIRGen().newVariable(resultKind); + AMD64AddressValue address = new AMD64AddressValue(resultKind, getLIRGen().asAllocatable(base), offset); + getLIRGen().append(new AMD64Move.LeaOp(result, address, size)); + return result; + } + @Override public Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) { TargetDescription target = getLIRGen().target(); boolean isAvx = ((AMD64) target.arch).getFeatures().contains(CPUFeature.AVX); switch ((AMD64Kind) a.getPlatformKind()) { case DWORD: + if (isJavaConstant(b) && !setFlags) { + long displacement = asJavaConstant(b).asLong(); + if (NumUtil.isInt(displacement) && displacement != 1 && displacement != -1) { + return emitBaseOffsetLea(resultKind, a, (int) displacement, OperandSize.DWORD); + } + } return emitBinary(resultKind, ADD, DWORD, true, a, b, setFlags); case QWORD: + if (isJavaConstant(b) && !setFlags) { + long displacement = asJavaConstant(b).asLong(); + if (NumUtil.isInt(displacement) && displacement != 1 && displacement != -1) { + return emitBaseOffsetLea(resultKind, a, (int) displacement, OperandSize.QWORD); + } + } return emitBinary(resultKind, ADD, QWORD, true, a, b, setFlags); case SINGLE: if (isAvx) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java index 2e07cd3a23c..c72bf39b999 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java @@ -28,6 +28,7 @@ import static org.graalvm.compiler.lir.LIRValueUtil.asConstant; import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue; import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue; +import org.graalvm.compiler.asm.amd64.AMD64Assembler; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.type.DataPointerConstant; import org.graalvm.compiler.debug.GraalError; @@ -85,7 +86,7 @@ public abstract class AMD64MoveFactory extends AMD64MoveFactoryBase { @Override public AMD64LIRInstruction createMove(AllocatableValue dst, Value src) { if (src instanceof AMD64AddressValue) { - return new LeaOp(dst, (AMD64AddressValue) src); + return new LeaOp(dst, (AMD64AddressValue) src, AMD64Assembler.OperandSize.QWORD); } else if (isConstantValue(src)) { return createLoad(dst, asConstant(src)); } else if (isRegister(src) || isStackSlotValue(dst)) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java index 760f599d08c..7d6a4c7840f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java @@ -60,6 +60,7 @@ import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizingNode; import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; import org.graalvm.compiler.nodes.calc.FloatConvertNode; @@ -478,7 +479,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules { @MatchRule("(Write object Narrow=narrow)") public ComplexMatchResult writeNarrow(WriteNode root, NarrowNode narrow) { return builder -> { - LIRKind writeKind = getLIRGeneratorTool().getLIRKind(root.value().stamp()); + LIRKind writeKind = getLIRGeneratorTool().getLIRKind(root.value().stamp(NodeView.DEFAULT)); getArithmeticLIRGenerator().emitStore(writeKind, operand(root.getAddress()), operand(narrow.getValue()), state(root)); return null; }; @@ -504,10 +505,10 @@ public class AMD64NodeMatchRules extends NodeMatchRules { @Override public Value evaluate(NodeLIRBuilder builder) { AMD64AddressValue address = (AMD64AddressValue) operand(access.getAddress()); - LIRKind addressKind = LIRKind.combineDerived(getLIRGeneratorTool().getLIRKind(root.asNode().stamp()), + LIRKind addressKind = LIRKind.combineDerived(getLIRGeneratorTool().getLIRKind(root.asNode().stamp(NodeView.DEFAULT)), address.getBase(), address.getIndex()); AMD64AddressValue newAddress = address.withKind(addressKind); - LIRKind readKind = getLIRGeneratorTool().getLIRKind(root.stamp()); + LIRKind readKind = getLIRGeneratorTool().getLIRKind(root.stamp(NodeView.DEFAULT)); return getArithmeticLIRGenerator().emitZeroExtendMemory((AMD64Kind) readKind.getPlatformKind(), root.getResultBits(), newAddress, getState(access)); } @@ -517,7 +518,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules { @MatchRule("(SignExtend (Narrow=narrow Read=access))") @MatchRule("(SignExtend (Narrow=narrow FloatingRead=access))") public ComplexMatchResult signExtendNarrowRead(SignExtendNode root, NarrowNode narrow, LIRLowerableAccess access) { - LIRKind kind = getLIRGeneratorTool().getLIRKind(narrow.stamp()); + LIRKind kind = getLIRGeneratorTool().getLIRKind(narrow.stamp(NodeView.DEFAULT)); return emitSignExtendMemory(access, narrow.getResultBits(), root.getResultBits(), kind); } @@ -554,7 +555,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules { @MatchRule("(Reinterpret FloatingRead=access)") public ComplexMatchResult reinterpret(ReinterpretNode root, LIRLowerableAccess access) { return builder -> { - LIRKind kind = getLIRGeneratorTool().getLIRKind(root.stamp()); + LIRKind kind = getLIRGeneratorTool().getLIRKind(root.stamp(NodeView.DEFAULT)); return emitReinterpretMemory(kind, access); }; @@ -563,7 +564,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules { @MatchRule("(Write object Reinterpret=reinterpret)") public ComplexMatchResult writeReinterpret(WriteNode root, ReinterpretNode reinterpret) { return builder -> { - LIRKind kind = getLIRGeneratorTool().getLIRKind(reinterpret.getValue().stamp()); + LIRKind kind = getLIRGeneratorTool().getLIRKind(reinterpret.getValue().stamp(NodeView.DEFAULT)); AllocatableValue value = getLIRGeneratorTool().asAllocatable(operand(reinterpret.getValue())); AMD64AddressValue address = (AMD64AddressValue) operand(root.getAddress()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java index 2d94012cf75..d726673a6cd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java @@ -251,22 +251,34 @@ public class StampFactory { } public static Stamp[] createParameterStamps(Assumptions assumptions, ResolvedJavaMethod method) { - Signature sig = method.getSignature(); - Stamp[] result = new Stamp[sig.getParameterCount(!method.isStatic())]; - int index = 0; + return createParameterStamps(assumptions, method, false); + } - if (!method.isStatic()) { - result[index++] = StampFactory.objectNonNull(TypeReference.create(assumptions, method.getDeclaringClass())); + public static Stamp[] createParameterStamps(Assumptions assumptions, ResolvedJavaMethod method, boolean trustInterfaceTypes) { + Signature signature = method.getSignature(); + Stamp[] result = new Stamp[signature.getParameterCount(method.hasReceiver())]; + + int index = 0; + ResolvedJavaType accessingClass = method.getDeclaringClass(); + if (method.hasReceiver()) { + if (trustInterfaceTypes) { + result[index++] = StampFactory.objectNonNull(TypeReference.createTrusted(assumptions, accessingClass)); + } else { + result[index++] = StampFactory.objectNonNull(TypeReference.create(assumptions, accessingClass)); + } } - int max = sig.getParameterCount(false); - ResolvedJavaType accessingClass = method.getDeclaringClass(); - for (int i = 0; i < max; i++) { - JavaType type = sig.getParameterType(i, accessingClass); + for (int i = 0; i < signature.getParameterCount(false); i++) { + JavaType type = signature.getParameterType(i, accessingClass); JavaKind kind = type.getJavaKind(); + Stamp stamp; if (kind == JavaKind.Object && type instanceof ResolvedJavaType) { - stamp = StampFactory.object(TypeReference.create(assumptions, (ResolvedJavaType) type)); + if (trustInterfaceTypes) { + stamp = StampFactory.object(TypeReference.createTrusted(assumptions, (ResolvedJavaType) type)); + } else { + stamp = StampFactory.object(TypeReference.create(assumptions, (ResolvedJavaType) type)); + } } else { stamp = StampFactory.forKind(kind); } @@ -284,16 +296,28 @@ public class StampFactory { if (returnType.getJavaKind() == JavaKind.Object && returnType instanceof ResolvedJavaType) { ResolvedJavaType resolvedJavaType = (ResolvedJavaType) returnType; TypeReference reference = TypeReference.create(assumptions, resolvedJavaType); - if (resolvedJavaType.isInterface()) { - ResolvedJavaType implementor = resolvedJavaType.getSingleImplementor(); - if (implementor != null && !resolvedJavaType.equals(implementor)) { - TypeReference uncheckedType = TypeReference.createTrusted(assumptions, implementor); - return StampPair.create(StampFactory.object(reference, nonNull), StampFactory.object(uncheckedType, nonNull)); + ResolvedJavaType elementalType = resolvedJavaType.getElementalType(); + if (elementalType.isInterface()) { + assert reference == null || !reference.getType().equals(resolvedJavaType); + TypeReference uncheckedType; + ResolvedJavaType elementalImplementor = elementalType.getSingleImplementor(); + if (elementalImplementor != null && !elementalType.equals(elementalImplementor)) { + ResolvedJavaType implementor = elementalImplementor; + ResolvedJavaType t = resolvedJavaType; + while (t.isArray()) { + implementor = implementor.getArrayClass(); + t = t.getComponentType(); + } + uncheckedType = TypeReference.createTrusted(assumptions, implementor); + } else { + uncheckedType = TypeReference.createTrusted(assumptions, resolvedJavaType); } + return StampPair.create(StampFactory.object(reference, nonNull), StampFactory.object(uncheckedType, nonNull)); } return StampPair.createSingle(StampFactory.object(reference, nonNull)); } else { return StampPair.createSingle(StampFactory.forKind(returnType.getJavaKind())); } } + } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java index 14725bf4982..13edb612d34 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java @@ -33,11 +33,6 @@ import jdk.vm.ci.meta.JavaConstant; public class SPARCAddressLowering extends AddressLowering { - @Override - public AddressNode lower(ValueNode address) { - return lower(address, 0); - } - @Override public AddressNode lower(ValueNode base, ValueNode offset) { JavaConstant immBase = asImmediate(base); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java index abdef18c51e..1dee79bd5a4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.sparc.SPARCImmediateAddressValue; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -59,7 +60,7 @@ public class SPARCImmediateAddressNode extends AddressNode implements LIRLowerab AllocatableValue baseValue = tool.asAllocatable(gen.operand(base)); - LIRKind kind = tool.getLIRKind(stamp()); + LIRKind kind = tool.getLIRKind(stamp(NodeView.DEFAULT)); AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue); if (baseReference != null) { kind = kind.makeDerivedReference(baseReference); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java index 3fee3e82ea1..456f17aa215 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java @@ -27,6 +27,7 @@ import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.sparc.SPARCIndexedAddressValue; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -61,7 +62,7 @@ public class SPARCIndexedAddressNode extends AddressNode implements LIRLowerable AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue); AllocatableValue indexReference = LIRKind.derivedBaseFromValue(indexValue); - LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference); + LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference); gen.setResult(this, new SPARCIndexedAddressValue(kind, baseValue, indexValue)); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java index 9f0cd9f3cdc..7c69a738408 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java @@ -27,6 +27,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; @@ -59,7 +60,7 @@ public class SPARCIntegerCompareCanonicalizationPhase extends Phase { } private static void min32(CompareNode enode, ValueNode v) { - Stamp s = v.stamp(); + Stamp s = v.stamp(NodeView.DEFAULT); if (s instanceof IntegerStamp) { int bits = ((IntegerStamp) s).getBits(); if (bits != 32 && bits != 64) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java index 8c1e5733a9d..877e98aae84 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java @@ -25,6 +25,7 @@ package org.graalvm.compiler.core.test; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; +import org.graalvm.compiler.nodes.NodeView; import org.junit.Test; import org.graalvm.compiler.api.directives.GraalDirectives; @@ -223,7 +224,7 @@ public class CountedLoopTest extends GraalCompilerTest { @Input private ValueNode iv; protected IVPropertyNode(IVProperty property, ValueNode iv) { - super(TYPE, iv.stamp().unrestricted()); + super(TYPE, iv.stamp(NodeView.DEFAULT).unrestricted()); this.property = property; this.iv = iv; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java index 51fdf9426d3..a364aae2f8f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java @@ -166,7 +166,8 @@ public class NodePropertiesTest extends GraalCompilerTest { ImprovementSavingCanonicalizer c2 = new ImprovementSavingCanonicalizer(); StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet")); new CanonicalizerPhase(c2).apply(g2, htc); - Assert.assertTrue(c1.savedCycles > c2.savedCycles); + Assert.assertEquals(0, c1.savedCycles); + Assert.assertEquals(0, c2.savedCycles); } private static void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java new file mode 100644 index 00000000000..bcb0fddc0ed --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017, 2017, 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. + */ +package org.graalvm.compiler.core.test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; + +import jdk.vm.ci.meta.ResolvedJavaType; +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.nodeinfo.Verbosity; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.debug.BlackholeNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; +import org.graalvm.compiler.nodes.spi.UncheckedInterfaceProvider; +import org.graalvm.compiler.nodes.type.StampTool; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public class UncheckedInterfaceProviderTest extends GraalCompilerTest { + private Runnable interfaceField; + private Runnable[] interfaceArrayField; + + public void snippet(Runnable interfaceParameter, Runnable[] interfaceArrayParameter) { + GraalDirectives.blackhole(interfaceParameter); + GraalDirectives.blackhole(interfaceArrayParameter); + GraalDirectives.blackhole(interfaceField); + GraalDirectives.blackhole(interfaceArrayField); + GraalDirectives.blackhole(interfaceReturn()); + GraalDirectives.blackhole(interfaceArrayReturn()); + GraalDirectives.blackhole(interfaceReturnException()); + GraalDirectives.blackhole(interfaceArrayReturnException()); + } + + public static Runnable interfaceReturn() { + return new A(); + } + + public static Runnable[] interfaceArrayReturn() { + return new Runnable[]{new A(), new B(), new C(), new D()}; + } + + public static Runnable interfaceReturnException() { + return new A(); + } + + public static Runnable[] interfaceArrayReturnException() { + return new Runnable[]{new A(), new B(), new C(), new D()}; + } + + @Override + protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { + if (method.getName().startsWith("interfaceReturn") || method.getName().startsWith("interfaceArrayReturn")) { + if (method.getName().equals("Exception")) { + return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; + } + return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; + } + return super.bytecodeParserShouldInlineInvoke(b, method, args); + } + + @BeforeClass + public static void setup() { + interfaceArrayReturn(); + } + + @Test + public void test() { + StructuredGraph graph = parseEager("snippet", StructuredGraph.AllowAssumptions.YES); + for (BlackholeNode b : graph.getNodes().filter(BlackholeNode.class)) { + Assert.assertThat(b.getValue(), is(instanceOf(UncheckedInterfaceProvider.class))); + Stamp uncheckedStamp = ((UncheckedInterfaceProvider) b.getValue()).uncheckedStamp(); + String context = b.getValue().toString(Verbosity.Debugger); + Assert.assertNotNull(context, uncheckedStamp); + ResolvedJavaType uncheckedType = StampTool.typeOrNull(uncheckedStamp); + ResolvedJavaType type = StampTool.typeOrNull(b.getValue()); + Assert.assertEquals(context, arrayDepth(type), arrayDepth(uncheckedType)); + Assert.assertTrue(context + ": " + type, type == null || type.getElementalType().isJavaLangObject()); + Assert.assertNotNull(context, uncheckedType); + Assert.assertTrue(context, uncheckedType.getElementalType().isInterface()); + } + } + + private static int arrayDepth(ResolvedJavaType type) { + int depth = 0; + ResolvedJavaType t = type; + while (t != null && t.isArray()) { + depth += 1; + t = t.getComponentType(); + } + return depth; + } + + public static class A implements Runnable { + @Override + public void run() { + // nop + } + } + + public static class B implements Runnable { + @Override + public void run() { + // nop + } + } + + public static class C implements Runnable { + @Override + public void run() { + // nop + } + } + + public static class D implements Runnable { + @Override + public void run() { + // nop + } + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java index f7e0c928304..26a0f899620 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java @@ -22,14 +22,7 @@ */ package org.graalvm.compiler.core.test.deopt; -import jdk.vm.ci.code.InstalledCode; -import jdk.vm.ci.code.InvalidInstalledCodeException; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; - -import org.junit.Assert; -import org.junit.Test; - +import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -37,16 +30,19 @@ import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; import org.graalvm.compiler.phases.tiers.PhaseContext; +import org.junit.Assert; +import org.junit.Test; + +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.code.InvalidInstalledCodeException; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; -/** - * In the following tests, the usages of local variable "a" are replaced with the integer constant - * 0. Then canonicalization is applied and it is verified that the resulting graph is equal to the - * graph of the method that just has a "return 1" statement in it. - */ public class CompiledMethodTest extends GraalCompilerTest { public static Object testMethod(Object arg1, Object arg2, Object arg3) { - return arg1 + " " + arg2 + " " + arg3; + String res = arg1 + " " + arg2 + " " + arg3; + return GraalDirectives.inCompiledCode() ? res : "interpreter"; } Object f1; @@ -55,6 +51,12 @@ public class CompiledMethodTest extends GraalCompilerTest { return f1 + " " + arg1 + " " + arg2 + " " + arg3; } + /** + * Usages of the constant {@code " "} are replaced with the constant {@code "-"} and it is + * verified that executing the compiled code produces a result that the preserves the node + * replacement unless deoptimization occurs (e.g., due to -Xcomp causing profiles to be + * missing). + */ @Test public void test1() { final ResolvedJavaMethod javaMethod = getResolvedJavaMethod("testMethod"); @@ -71,7 +73,10 @@ public class CompiledMethodTest extends GraalCompilerTest { InstalledCode compiledMethod = getCode(javaMethod, graph); try { Object result = compiledMethod.executeVarargs("1", "2", "3"); - Assert.assertEquals("1-2-3", result); + if (!"1-2-3".equals(result)) { + // Deoptimization probably occurred + Assert.assertEquals("interpreter", result); + } } catch (InvalidInstalledCodeException t) { Assert.fail("method invalidated"); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java new file mode 100644 index 00000000000..0792e3ca08d --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java @@ -0,0 +1,1001 @@ +/* + * Copyright (c) 2016, 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. + */ +package org.graalvm.compiler.core.test.ea; + +import java.lang.reflect.Field; + +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.junit.Test; + +import sun.misc.Unsafe; + +/** + * Exercise a mix of unsafe and normal reads ands writes in situations where EA might attempt to + * fold the operations. + */ +public class PartialEscapeUnsafeStoreTest extends GraalCompilerTest { + + private static final Unsafe unsafe = initUnsafe(); + + private static Unsafe initUnsafe() { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return (Unsafe) theUnsafe.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe", e); + } + } + + private static final long byteArrayBaseOffset = unsafe.arrayBaseOffset(byte[].class); + private static byte byteValue = 0x61; + + public static byte[] testByteArrayWithCharStoreSnippet(char v) { + byte[] b = new byte[8]; + unsafe.putChar(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithCharStore() { + test("testByteArrayWithCharStoreSnippet", charValue); + } + + public static byte[] testByteArrayWithShortStoreSnippet(short v) { + byte[] b = new byte[8]; + unsafe.putShort(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithShortStore() { + test("testByteArrayWithShortStoreSnippet", shortValue); + } + + public static byte[] testByteArrayWithIntStoreSnippet(int v) { + byte[] b = new byte[8]; + unsafe.putInt(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithIntStore() { + test("testByteArrayWithIntStoreSnippet", intValue); + } + + public static byte[] testByteArrayWithLongStoreSnippet(long v) { + byte[] b = new byte[8]; + unsafe.putLong(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithLongStore() { + test("testByteArrayWithLongStoreSnippet", longValue); + } + + public static byte[] testByteArrayWithFloatStoreSnippet(float v) { + byte[] b = new byte[8]; + unsafe.putFloat(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithFloatStore() { + test("testByteArrayWithFloatStoreSnippet", floatValue); + } + + public static byte[] testByteArrayWithDoubleStoreSnippet(double v) { + byte[] b = new byte[8]; + unsafe.putDouble(b, byteArrayBaseOffset, v); + return b; + } + + @Test + public void testByteArrayWithDoubleStore() { + test("testByteArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long charArrayBaseOffset = unsafe.arrayBaseOffset(char[].class); + private static char charValue = 0x4142; + + public static char[] testCharArrayWithByteStoreSnippet(byte v) { + char[] b = new char[4]; + unsafe.putByte(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithByteStore() { + test("testCharArrayWithByteStoreSnippet", byteValue); + } + + public static char[] testCharArrayWithShortStoreSnippet(short v) { + char[] b = new char[4]; + unsafe.putShort(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithShortStore() { + test("testCharArrayWithShortStoreSnippet", shortValue); + } + + public static char[] testCharArrayWithIntStoreSnippet(int v) { + char[] b = new char[4]; + unsafe.putInt(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithIntStore() { + test("testCharArrayWithIntStoreSnippet", intValue); + } + + public static char[] testCharArrayWithLongStoreSnippet(long v) { + char[] b = new char[4]; + unsafe.putLong(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithLongStore() { + test("testCharArrayWithLongStoreSnippet", longValue); + } + + public static char[] testCharArrayWithFloatStoreSnippet(float v) { + char[] b = new char[4]; + unsafe.putFloat(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithFloatStore() { + test("testCharArrayWithFloatStoreSnippet", floatValue); + } + + public static char[] testCharArrayWithDoubleStoreSnippet(double v) { + char[] b = new char[4]; + unsafe.putDouble(b, charArrayBaseOffset, v); + return b; + } + + @Test + public void testCharArrayWithDoubleStore() { + test("testCharArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long shortArrayBaseOffset = unsafe.arrayBaseOffset(short[].class); + private static short shortValue = 0x1112; + + public static short[] testShortArrayWithByteStoreSnippet(byte v) { + short[] b = new short[4]; + unsafe.putByte(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithByteStore() { + test("testShortArrayWithByteStoreSnippet", byteValue); + } + + public static short[] testShortArrayWithCharStoreSnippet(char v) { + short[] b = new short[4]; + unsafe.putChar(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithCharStore() { + test("testShortArrayWithCharStoreSnippet", charValue); + } + + public static short[] testShortArrayWithIntStoreSnippet(int v) { + short[] b = new short[4]; + unsafe.putInt(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithIntStore() { + test("testShortArrayWithIntStoreSnippet", intValue); + } + + public static short[] testShortArrayWithLongStoreSnippet(long v) { + short[] b = new short[4]; + unsafe.putLong(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithLongStore() { + test("testShortArrayWithLongStoreSnippet", longValue); + } + + public static short[] testShortArrayWithFloatStoreSnippet(float v) { + short[] b = new short[4]; + unsafe.putFloat(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithFloatStore() { + test("testShortArrayWithFloatStoreSnippet", floatValue); + } + + public static short[] testShortArrayWithDoubleStoreSnippet(double v) { + short[] b = new short[4]; + unsafe.putDouble(b, shortArrayBaseOffset, v); + return b; + } + + @Test + public void testShortArrayWithDoubleStore() { + test("testShortArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long intArrayBaseOffset = unsafe.arrayBaseOffset(int[].class); + private static int intValue = 0x01020304; + + public static int[] testIntArrayWithByteStoreSnippet(byte v) { + int[] b = new int[4]; + unsafe.putByte(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithByteStore() { + test("testIntArrayWithByteStoreSnippet", byteValue); + } + + public static int[] testIntArrayWithCharStoreSnippet(char v) { + int[] b = new int[4]; + unsafe.putChar(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithCharStore() { + test("testIntArrayWithCharStoreSnippet", charValue); + } + + public static int[] testIntArrayWithShortStoreSnippet(short v) { + int[] b = new int[4]; + unsafe.putShort(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithShortStore() { + test("testIntArrayWithShortStoreSnippet", shortValue); + } + + public static int[] testIntArrayWithLongStoreSnippet(long v) { + int[] b = new int[4]; + unsafe.putLong(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithLongStore() { + test("testIntArrayWithLongStoreSnippet", longValue); + } + + public static int[] testIntArrayWithFloatStoreSnippet(float v) { + int[] b = new int[4]; + unsafe.putFloat(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithFloatStore() { + test("testIntArrayWithFloatStoreSnippet", floatValue); + } + + public static int[] testIntArrayWithDoubleStoreSnippet(double v) { + int[] b = new int[4]; + unsafe.putDouble(b, intArrayBaseOffset, v); + return b; + } + + @Test + public void testIntArrayWithDoubleStore() { + test("testIntArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long longArrayBaseOffset = unsafe.arrayBaseOffset(long[].class); + private static long longValue = 0x31323334353637L; + + public static long[] testLongArrayWithByteStoreSnippet(byte v) { + long[] b = new long[4]; + unsafe.putByte(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithByteStore() { + test("testLongArrayWithByteStoreSnippet", byteValue); + } + + public static long[] testLongArrayWithCharStoreSnippet(char v) { + long[] b = new long[4]; + unsafe.putChar(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithCharStore() { + test("testLongArrayWithCharStoreSnippet", charValue); + } + + public static long[] testLongArrayWithShortStoreSnippet(short v) { + long[] b = new long[4]; + unsafe.putShort(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithShortStore() { + test("testLongArrayWithShortStoreSnippet", shortValue); + } + + public static long[] testLongArrayWithIntStoreSnippet(int v) { + long[] b = new long[4]; + unsafe.putInt(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithIntStore() { + test("testLongArrayWithIntStoreSnippet", intValue); + } + + public static long[] testLongArrayWithFloatStoreSnippet(float v) { + long[] b = new long[4]; + unsafe.putFloat(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithFloatStore() { + test("testLongArrayWithFloatStoreSnippet", floatValue); + } + + public static long[] testLongArrayWithDoubleStoreSnippet(double v) { + long[] b = new long[4]; + unsafe.putDouble(b, longArrayBaseOffset, v); + return b; + } + + @Test + public void testLongArrayWithDoubleStore() { + test("testLongArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long floatArrayBaseOffset = unsafe.arrayBaseOffset(float[].class); + private static float floatValue = Float.NaN; + + public static float[] testFloatArrayWithByteStoreSnippet(byte v) { + float[] b = new float[4]; + unsafe.putByte(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithByteStore() { + test("testFloatArrayWithByteStoreSnippet", byteValue); + } + + public static float[] testFloatArrayWithCharStoreSnippet(char v) { + float[] b = new float[4]; + unsafe.putChar(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithCharStore() { + test("testFloatArrayWithCharStoreSnippet", charValue); + } + + public static float[] testFloatArrayWithShortStoreSnippet(short v) { + float[] b = new float[4]; + unsafe.putShort(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithShortStore() { + test("testFloatArrayWithShortStoreSnippet", shortValue); + } + + public static float[] testFloatArrayWithIntStoreSnippet(int v) { + float[] b = new float[4]; + unsafe.putInt(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithIntStore() { + test("testFloatArrayWithIntStoreSnippet", intValue); + } + + public static float[] testFloatArrayWithLongStoreSnippet(long v) { + float[] b = new float[4]; + unsafe.putLong(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithLongStore() { + test("testFloatArrayWithLongStoreSnippet", longValue); + } + + public static float[] testFloatArrayWithDoubleStoreSnippet(double v) { + float[] b = new float[4]; + unsafe.putDouble(b, floatArrayBaseOffset, v); + return b; + } + + @Test + public void testFloatArrayWithDoubleStore() { + test("testFloatArrayWithDoubleStoreSnippet", doubleValue); + } + + private static final long doubleArrayBaseOffset = unsafe.arrayBaseOffset(double[].class); + private static double doubleValue = Double.NaN; + private static final int byteSize = 1; + private static final int charSize = 2; + private static final int shortSize = 2; + private static final int intSize = 4; + private static final int floatSize = 4; + private static final int longSize = 8; + private static final int doubleSize = 8; + + public static double[] testDoubleArrayWithByteStoreSnippet(byte v) { + double[] b = new double[4]; + unsafe.putByte(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithByteStore() { + test("testDoubleArrayWithByteStoreSnippet", byteValue); + } + + public static double[] testDoubleArrayWithCharStoreSnippet(char v) { + double[] b = new double[4]; + unsafe.putChar(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithCharStore() { + test("testDoubleArrayWithCharStoreSnippet", charValue); + } + + public static double[] testDoubleArrayWithShortStoreSnippet(short v) { + double[] b = new double[4]; + unsafe.putShort(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithShortStore() { + test("testDoubleArrayWithShortStoreSnippet", shortValue); + } + + public static double[] testDoubleArrayWithIntStoreSnippet(int v) { + double[] b = new double[4]; + unsafe.putInt(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithIntStore() { + test("testDoubleArrayWithIntStoreSnippet", intValue); + } + + public static double[] testDoubleArrayWithLongStoreSnippet(long v) { + double[] b = new double[4]; + unsafe.putLong(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithLongStore() { + test("testDoubleArrayWithLongStoreSnippet", longValue); + } + + public static double[] testDoubleArrayWithFloatStoreSnippet(float v) { + double[] b = new double[4]; + unsafe.putFloat(b, doubleArrayBaseOffset, v); + return b; + } + + @Test + public void testDoubleArrayWithFloatStore() { + test("testDoubleArrayWithFloatStoreSnippet", floatValue); + } + + public static byte testByteArrayWithCharStoreAndReadSnippet(char v) { + byte[] b = new byte[4]; + unsafe.putChar(b, byteArrayBaseOffset, v); + return b[(byteSize / charSize) + 1]; + } + + @Test + public void testByteArrayWithCharStoreAndRead() { + test("testByteArrayWithCharStoreAndReadSnippet", charValue); + } + + public static byte testByteArrayWithShortStoreAndReadSnippet(short v) { + byte[] b = new byte[4]; + unsafe.putShort(b, byteArrayBaseOffset, v); + return b[(byteSize / shortSize) + 1]; + } + + @Test + public void testByteArrayWithShortStoreAndRead() { + test("testByteArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static byte testByteArrayWithIntStoreAndReadSnippet(int v) { + byte[] b = new byte[4]; + unsafe.putInt(b, byteArrayBaseOffset, v); + return b[(byteSize / intSize) + 1]; + } + + @Test + public void testByteArrayWithIntStoreAndRead() { + test("testByteArrayWithIntStoreAndReadSnippet", intValue); + } + + public static byte testByteArrayWithLongStoreAndReadSnippet(long v) { + byte[] b = new byte[4]; + unsafe.putLong(b, byteArrayBaseOffset, v); + return b[(byteSize / longSize) + 1]; + } + + @Test + public void testByteArrayWithLongStoreAndRead() { + test("testByteArrayWithLongStoreAndReadSnippet", longValue); + } + + public static byte testByteArrayWithFloatStoreAndReadSnippet(float v) { + byte[] b = new byte[4]; + unsafe.putFloat(b, byteArrayBaseOffset, v); + return b[(byteSize / floatSize) + 1]; + } + + @Test + public void testByteArrayWithFloatStoreAndRead() { + test("testByteArrayWithFloatStoreAndReadSnippet", floatValue); + } + + public static byte testByteArrayWithDoubleStoreAndReadSnippet(double v) { + byte[] b = new byte[4]; + unsafe.putDouble(b, byteArrayBaseOffset, v); + return b[(byteSize / doubleSize) + 1]; + } + + @Test + public void testByteArrayWithDoubleStoreAndRead() { + test("testByteArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static char testCharArrayWithByteStoreAndReadSnippet(byte v) { + char[] b = new char[4]; + unsafe.putByte(b, charArrayBaseOffset, v); + return b[(charSize / byteSize) + 1]; + } + + @Test + public void testCharArrayWithByteStoreAndRead() { + test("testCharArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static char testCharArrayWithShortStoreAndReadSnippet(short v) { + char[] b = new char[4]; + unsafe.putShort(b, charArrayBaseOffset, v); + return b[(charSize / shortSize) + 1]; + } + + @Test + public void testCharArrayWithShortStoreAndRead() { + test("testCharArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static char testCharArrayWithIntStoreAndReadSnippet(int v) { + char[] b = new char[4]; + unsafe.putInt(b, charArrayBaseOffset, v); + return b[(charSize / intSize) + 1]; + } + + @Test + public void testCharArrayWithIntStoreAndRead() { + test("testCharArrayWithIntStoreAndReadSnippet", intValue); + } + + public static char testCharArrayWithLongStoreAndReadSnippet(long v) { + char[] b = new char[4]; + unsafe.putLong(b, charArrayBaseOffset, v); + return b[(charSize / longSize) + 1]; + } + + @Test + public void testCharArrayWithLongStoreAndRead() { + test("testCharArrayWithLongStoreAndReadSnippet", longValue); + } + + public static char testCharArrayWithFloatStoreAndReadSnippet(float v) { + char[] b = new char[4]; + unsafe.putFloat(b, charArrayBaseOffset, v); + return b[(charSize / floatSize) + 1]; + } + + @Test + public void testCharArrayWithFloatStoreAndRead() { + test("testCharArrayWithFloatStoreAndReadSnippet", floatValue); + } + + public static char testCharArrayWithDoubleStoreAndReadSnippet(double v) { + char[] b = new char[4]; + unsafe.putDouble(b, charArrayBaseOffset, v); + return b[(charSize / doubleSize) + 1]; + } + + @Test + public void testCharArrayWithDoubleStoreAndRead() { + test("testCharArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static short testShortArrayWithByteStoreAndReadSnippet(byte v) { + short[] b = new short[4]; + unsafe.putByte(b, shortArrayBaseOffset, v); + return b[(shortSize / byteSize) + 1]; + } + + @Test + public void testShortArrayWithByteStoreAndRead() { + test("testShortArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static short testShortArrayWithCharStoreAndReadSnippet(char v) { + short[] b = new short[4]; + unsafe.putChar(b, shortArrayBaseOffset, v); + return b[(shortSize / charSize) + 1]; + } + + @Test + public void testShortArrayWithCharStoreAndRead() { + test("testShortArrayWithCharStoreAndReadSnippet", charValue); + } + + public static short testShortArrayWithIntStoreAndReadSnippet(int v) { + short[] b = new short[4]; + unsafe.putInt(b, shortArrayBaseOffset, v); + return b[(shortSize / intSize) + 1]; + } + + @Test + public void testShortArrayWithIntStoreAndRead() { + test("testShortArrayWithIntStoreAndReadSnippet", intValue); + } + + public static short testShortArrayWithLongStoreAndReadSnippet(long v) { + short[] b = new short[4]; + unsafe.putLong(b, shortArrayBaseOffset, v); + return b[(shortSize / longSize) + 1]; + } + + @Test + public void testShortArrayWithLongStoreAndRead() { + test("testShortArrayWithLongStoreAndReadSnippet", longValue); + } + + public static short testShortArrayWithFloatStoreAndReadSnippet(float v) { + short[] b = new short[4]; + unsafe.putFloat(b, shortArrayBaseOffset, v); + return b[(shortSize / floatSize) + 1]; + } + + @Test + public void testShortArrayWithFloatStoreAndRead() { + test("testShortArrayWithFloatStoreAndReadSnippet", floatValue); + } + + public static short testShortArrayWithDoubleStoreAndReadSnippet(double v) { + short[] b = new short[4]; + unsafe.putDouble(b, shortArrayBaseOffset, v); + return b[(shortSize / doubleSize) + 1]; + } + + @Test + public void testShortArrayWithDoubleStoreAndRead() { + test("testShortArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static int testIntArrayWithByteStoreAndReadSnippet(byte v) { + int[] b = new int[4]; + unsafe.putByte(b, intArrayBaseOffset, v); + return b[(intSize / byteSize) + 1]; + } + + @Test + public void testIntArrayWithByteStoreAndRead() { + test("testIntArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static int testIntArrayWithCharStoreAndReadSnippet(char v) { + int[] b = new int[4]; + unsafe.putChar(b, intArrayBaseOffset, v); + return b[(intSize / charSize) + 1]; + } + + @Test + public void testIntArrayWithCharStoreAndRead() { + test("testIntArrayWithCharStoreAndReadSnippet", charValue); + } + + public static int testIntArrayWithShortStoreAndReadSnippet(short v) { + int[] b = new int[4]; + unsafe.putShort(b, intArrayBaseOffset, v); + return b[(intSize / shortSize) + 1]; + } + + @Test + public void testIntArrayWithShortStoreAndRead() { + test("testIntArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static int testIntArrayWithLongStoreAndReadSnippet(long v) { + int[] b = new int[4]; + unsafe.putLong(b, intArrayBaseOffset, v); + return b[(intSize / longSize) + 1]; + } + + @Test + public void testIntArrayWithLongStoreAndRead() { + test("testIntArrayWithLongStoreAndReadSnippet", longValue); + } + + public static int testIntArrayWithFloatStoreAndReadSnippet(float v) { + int[] b = new int[4]; + unsafe.putFloat(b, intArrayBaseOffset, v); + return b[(intSize / floatSize) + 1]; + } + + @Test + public void testIntArrayWithFloatStoreAndRead() { + test("testIntArrayWithFloatStoreAndReadSnippet", floatValue); + } + + public static int testIntArrayWithDoubleStoreAndReadSnippet(double v) { + int[] b = new int[4]; + unsafe.putDouble(b, intArrayBaseOffset, v); + return b[(intSize / doubleSize) + 1]; + } + + @Test + public void testIntArrayWithDoubleStoreAndRead() { + test("testIntArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static long testLongArrayWithByteStoreAndReadSnippet(byte v) { + long[] b = new long[4]; + unsafe.putByte(b, longArrayBaseOffset, v); + return b[(longSize / byteSize) + 1]; + } + + @Test + public void testLongArrayWithByteStoreAndRead() { + test("testLongArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static long testLongArrayWithCharStoreAndReadSnippet(char v) { + long[] b = new long[4]; + unsafe.putChar(b, longArrayBaseOffset, v); + return b[(longSize / charSize) + 1]; + } + + @Test + public void testLongArrayWithCharStoreAndRead() { + test("testLongArrayWithCharStoreAndReadSnippet", charValue); + } + + public static long testLongArrayWithShortStoreAndReadSnippet(short v) { + long[] b = new long[4]; + unsafe.putShort(b, longArrayBaseOffset, v); + return b[(longSize / shortSize) + 1]; + } + + @Test + public void testLongArrayWithShortStoreAndRead() { + test("testLongArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static long testLongArrayWithIntStoreAndReadSnippet(int v) { + long[] b = new long[4]; + unsafe.putInt(b, longArrayBaseOffset, v); + return b[(longSize / intSize) + 1]; + } + + @Test + public void testLongArrayWithIntStoreAndRead() { + test("testLongArrayWithIntStoreAndReadSnippet", intValue); + } + + public static long testLongArrayWithFloatStoreAndReadSnippet(float v) { + long[] b = new long[4]; + unsafe.putFloat(b, longArrayBaseOffset, v); + return b[(longSize / floatSize) + 1]; + } + + @Test + public void testLongArrayWithFloatStoreAndRead() { + test("testLongArrayWithFloatStoreAndReadSnippet", floatValue); + } + + public static long testLongArrayWithDoubleStoreAndReadSnippet(double v) { + long[] b = new long[4]; + unsafe.putDouble(b, longArrayBaseOffset, v); + return b[(longSize / doubleSize) + 1]; + } + + @Test + public void testLongArrayWithDoubleStoreAndRead() { + test("testLongArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static float testFloatArrayWithByteStoreAndReadSnippet(byte v) { + float[] b = new float[4]; + unsafe.putByte(b, floatArrayBaseOffset, v); + return b[(floatSize / byteSize) + 1]; + } + + @Test + public void testFloatArrayWithByteStoreAndRead() { + test("testFloatArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static float testFloatArrayWithCharStoreAndReadSnippet(char v) { + float[] b = new float[4]; + unsafe.putChar(b, floatArrayBaseOffset, v); + return b[(floatSize / charSize) + 1]; + } + + @Test + public void testFloatArrayWithCharStoreAndRead() { + test("testFloatArrayWithCharStoreAndReadSnippet", charValue); + } + + public static float testFloatArrayWithShortStoreAndReadSnippet(short v) { + float[] b = new float[4]; + unsafe.putShort(b, floatArrayBaseOffset, v); + return b[(floatSize / shortSize) + 1]; + } + + @Test + public void testFloatArrayWithShortStoreAndRead() { + test("testFloatArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static float testFloatArrayWithIntStoreAndReadSnippet(int v) { + float[] b = new float[4]; + unsafe.putInt(b, floatArrayBaseOffset, v); + return b[(floatSize / intSize) + 1]; + } + + @Test + public void testFloatArrayWithIntStoreAndRead() { + test("testFloatArrayWithIntStoreAndReadSnippet", intValue); + } + + public static float testFloatArrayWithLongStoreAndReadSnippet(long v) { + float[] b = new float[4]; + unsafe.putLong(b, floatArrayBaseOffset, v); + return b[(floatSize / longSize) + 1]; + } + + @Test + public void testFloatArrayWithLongStoreAndRead() { + test("testFloatArrayWithLongStoreAndReadSnippet", longValue); + } + + public static float testFloatArrayWithDoubleStoreAndReadSnippet(double v) { + float[] b = new float[4]; + unsafe.putDouble(b, floatArrayBaseOffset, v); + return b[(floatSize / doubleSize) + 1]; + } + + @Test + public void testFloatArrayWithDoubleStoreAndRead() { + test("testFloatArrayWithDoubleStoreAndReadSnippet", doubleValue); + } + + public static double testDoubleArrayWithByteStoreAndReadSnippet(byte v) { + double[] b = new double[4]; + unsafe.putByte(b, doubleArrayBaseOffset, v); + return b[(doubleSize / byteSize) + 1]; + } + + @Test + public void testDoubleArrayWithByteStoreAndRead() { + test("testDoubleArrayWithByteStoreAndReadSnippet", byteValue); + } + + public static double testDoubleArrayWithCharStoreAndReadSnippet(char v) { + double[] b = new double[4]; + unsafe.putChar(b, doubleArrayBaseOffset, v); + return b[(doubleSize / charSize) + 1]; + } + + @Test + public void testDoubleArrayWithCharStoreAndRead() { + test("testDoubleArrayWithCharStoreAndReadSnippet", charValue); + } + + public static double testDoubleArrayWithShortStoreAndReadSnippet(short v) { + double[] b = new double[4]; + unsafe.putShort(b, doubleArrayBaseOffset, v); + return b[(doubleSize / shortSize) + 1]; + } + + @Test + public void testDoubleArrayWithShortStoreAndRead() { + test("testDoubleArrayWithShortStoreAndReadSnippet", shortValue); + } + + public static double testDoubleArrayWithIntStoreAndReadSnippet(int v) { + double[] b = new double[4]; + unsafe.putInt(b, doubleArrayBaseOffset, v); + return b[(doubleSize / intSize) + 1]; + } + + @Test + public void testDoubleArrayWithIntStoreAndRead() { + test("testDoubleArrayWithIntStoreAndReadSnippet", intValue); + } + + public static double testDoubleArrayWithLongStoreAndReadSnippet(long v) { + double[] b = new double[4]; + unsafe.putLong(b, doubleArrayBaseOffset, v); + return b[(doubleSize / longSize) + 1]; + } + + @Test + public void testDoubleArrayWithLongStoreAndRead() { + test("testDoubleArrayWithLongStoreAndReadSnippet", longValue); + } + + public static double testDoubleArrayWithFloatStoreAndReadSnippet(float v) { + double[] b = new double[4]; + unsafe.putFloat(b, doubleArrayBaseOffset, v); + return b[(doubleSize / floatSize) + 1]; + } + + @Test + public void testDoubleArrayWithFloatStoreAndRead() { + test("testDoubleArrayWithFloatStoreAndReadSnippet", floatValue); + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java new file mode 100644 index 00000000000..015add57c38 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2012, 2016, 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. + */ +package org.graalvm.compiler.core.test.inlining; + +import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine; +import static org.graalvm.compiler.test.SubprocessUtil.java; +import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments; + +import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugDumpScope; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.InvokeNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; +import org.graalvm.compiler.nodes.StructuredGraph.Builder; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import org.graalvm.compiler.nodes.java.TypeSwitchNode; +import org.graalvm.compiler.phases.OptimisticOptimizations; +import org.graalvm.compiler.phases.PhaseSuite; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; +import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; +import org.graalvm.compiler.phases.common.inlining.InliningPhase; +import org.graalvm.compiler.phases.tiers.HighTierContext; +import org.graalvm.compiler.test.SubprocessUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; + +public class PolymorphicInliningTest extends GraalCompilerTest { + + @Test + public void testInSubprocess() throws InterruptedException, IOException { + String recursionPropName = getClass().getName() + ".recursion"; + if (Boolean.getBoolean(recursionPropName)) { + testPolymorphicInlining(); + testPolymorphicNotInlining(); + testMegamorphicInlining(); + testMegamorphicNotInlining(); + } else { + List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine()); + NotInlinableSubClass.class.getCanonicalName(); + vmArgs.add("-XX:CompileCommand=dontinline,org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest$NotInlinableSubClass.publicOverriddenMethod"); + vmArgs.add("-D" + recursionPropName + "=true"); + SubprocessUtil.Subprocess proc = java(vmArgs, "com.oracle.mxtool.junit.MxJUnitWrapper", getClass().getName()); + if (proc.exitCode != 0) { + Assert.fail(String.format("non-zero exit code %d for command:%n%s", proc.exitCode, proc)); + } + } + } + + public int polymorphicCallsite(SuperClass receiver) { + return receiver.publicOverriddenMethod(); + } + + public void testPolymorphicInlining() { + for (int i = 0; i < 10000; i++) { + if (i % 2 == 0) { + polymorphicCallsite(Receivers.subClassA); + } else { + polymorphicCallsite(Receivers.subClassB); + } + } + StructuredGraph graph = getGraph("polymorphicCallsite", false); + // This callsite should be inlined with a TypeCheckedInliningViolated deoptimization. + assertTrue(getNodeCount(graph, InvokeNode.class) == 0); + assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1); + assertTrue(getNodeCount(graph, DeoptimizeNode.class) >= 1); + } + + /** + * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding + * interference of the receiver type profile from different unit tests. + */ + public int polymorphicCallsite1(SuperClass receiver) { + return receiver.publicOverriddenMethod(); + } + + public void testPolymorphicNotInlining() { + for (int i = 0; i < 10000; i++) { + if (i % 2 == 0) { + polymorphicCallsite1(Receivers.subClassA); + } else { + polymorphicCallsite1(Receivers.notInlinableSubClass); + } + } + StructuredGraph graph = getGraph("polymorphicCallsite1", false); + // This callsite should not be inlined due to one of the potential callee method is not + // inlinable. + assertTrue(getNodeCount(graph, InvokeNode.class) == 1); + assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0); + } + + /** + * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding + * interference of the receiver type profile from different unit tests. + */ + public int polymorphicCallsite2(SuperClass receiver) { + return receiver.publicOverriddenMethod(); + } + + public void testMegamorphicInlining() { + // Construct a receiver type profile that exceeds the max type width (by default 8 in JVMCI, + // specified by -XX:TypeProfileWidth). + for (int i = 0; i < 2000; i++) { + // Ensure the following receiver type is within the type profile. + polymorphicCallsite2(Receivers.subClassA); + } + for (int i = 0; i < 10000; i++) { + switch (i % 20) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // Probability: 40% + // Ensure the probability is greater than + // GraalOptions.MegamorphicInliningMinMethodProbability (by default 0.33D); + polymorphicCallsite2(Receivers.subClassA); + break; + case 8: + polymorphicCallsite2(Receivers.subClassB); + break; + case 9: + polymorphicCallsite2(Receivers.subClassC); + break; + case 10: + polymorphicCallsite2(Receivers.subClassD); + break; + case 11: + polymorphicCallsite2(Receivers.subClassE); + break; + case 12: + polymorphicCallsite2(Receivers.subClassF); + break; + case 13: + polymorphicCallsite2(Receivers.subClassG); + break; + case 14: + polymorphicCallsite2(Receivers.subClassH); + break; + default: + // Probability: 25% + polymorphicCallsite2(Receivers.notInlinableSubClass); + break; + } + } + StructuredGraph graph = getGraph("polymorphicCallsite2", false); + // This callsite should be inlined with a fallback invocation. + assertTrue(getNodeCount(graph, InvokeNode.class) == 1); + assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1); + } + + /** + * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding + * interference of the receiver type profile from different unit tests. + */ + public int polymorphicCallsite3(SuperClass receiver) { + return receiver.publicOverriddenMethod(); + } + + public void testMegamorphicNotInlining() { + for (int i = 0; i < 10000; i++) { + switch (i % 10) { + case 0: + case 1: + polymorphicCallsite3(Receivers.subClassA); + break; + case 2: + polymorphicCallsite3(Receivers.subClassB); + break; + case 3: + polymorphicCallsite3(Receivers.subClassC); + break; + case 4: + polymorphicCallsite3(Receivers.subClassD); + break; + case 5: + polymorphicCallsite3(Receivers.subClassE); + break; + case 6: + polymorphicCallsite3(Receivers.subClassF); + break; + case 7: + polymorphicCallsite3(Receivers.subClassG); + break; + case 8: + polymorphicCallsite3(Receivers.subClassH); + break; + default: + polymorphicCallsite3(Receivers.notInlinableSubClass); + break; + } + } + StructuredGraph graph = getGraph("polymorphicCallsite3", false); + // This callsite should not be inlined due to non of the potential callee method exceeds the + // probability specified by GraalOptions.MegamorphicInliningMinMethodProbability. + assertTrue(getNodeCount(graph, InvokeNode.class) == 1); + assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0); + } + + @SuppressWarnings("try") + private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) { + DebugContext debug = getDebugContext(); + try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) { + ResolvedJavaMethod method = getResolvedJavaMethod(snippet); + Builder builder = builder(method, AllowAssumptions.YES, debug); + StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite()); + try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) { + PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode + ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true)) + : getDefaultGraphBuilderSuite(); + HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL); + debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph"); + new CanonicalizerPhase().apply(graph, context); + new InliningPhase(new CanonicalizerPhase()).apply(graph, context); + debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph"); + new CanonicalizerPhase().apply(graph, context); + new DeadCodeEliminationPhase().apply(graph); + return graph; + } + } catch (Throwable e) { + throw debug.handle(e); + } + } + + private static int getNodeCount(StructuredGraph graph, Class<? extends Node> nodeClass) { + return graph.getNodes().filter(nodeClass).count(); + } + + private static final class Receivers { + static final SubClassA subClassA = new SubClassA(); + static final SubClassB subClassB = new SubClassB(); + static final SubClassC subClassC = new SubClassC(); + static final SubClassD subClassD = new SubClassD(); + static final SubClassE subClassE = new SubClassE(); + static final SubClassF subClassF = new SubClassF(); + static final SubClassG subClassG = new SubClassG(); + static final SubClassH subClassH = new SubClassH(); + + static final NotInlinableSubClass notInlinableSubClass = new NotInlinableSubClass(); + } + + private abstract static class SuperClass { + + public abstract int publicOverriddenMethod(); + + } + + private static class SubClassA extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'A'; + } + + } + + private static class SubClassB extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'B'; + } + + } + + private static class SubClassC extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'C'; + } + + } + + private static class SubClassD extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'D'; + } + + } + + private static class SubClassE extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'E'; + } + + } + + private static class SubClassF extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'F'; + } + + } + + private static class SubClassG extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'G'; + } + + } + + private static class SubClassH extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'H'; + } + + } + + private static final class NotInlinableSubClass extends SuperClass { + + @Override + public int publicOverriddenMethod() { + return 'X'; + } + + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java index fffe121e077..a151bc46c28 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java @@ -81,6 +81,7 @@ import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoopEndNode; import org.graalvm.compiler.nodes.LoweredCallTargetNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -243,7 +244,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio } protected LIRKind getExactPhiKind(PhiNode phi) { - LIRKind derivedKind = gen.toRegisterKind(gen.getLIRKind(phi.stamp())); + LIRKind derivedKind = gen.toRegisterKind(gen.getLIRKind(phi.stamp(NodeView.DEFAULT))); /* Collect reference information. */ for (int i = 0; i < phi.valueCount() && !derivedKind.isUnknownReference(); i++) { ValueNode node = phi.valueAt(i); @@ -255,7 +256,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio valueKind = value.getValueKind(LIRKind.class); } else { assert isPhiInputFromBackedge(phi, i) : String.format("Input %s to phi node %s is not yet available although it is not coming from a loop back edge", node, phi); - LIRKind kind = gen.getLIRKind(node.stamp()); + LIRKind kind = gen.getLIRKind(node.stamp(NodeView.DEFAULT)); valueKind = gen.toRegisterKind(kind); } /* Merge the reference information of the derived kind and the input. */ @@ -448,7 +449,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio } protected void emitNode(ValueNode node) { - if (node.getDebug().isLogEnabled() && node.stamp().isEmpty()) { + if (node.getDebug().isLogEnabled() && node.stamp(NodeView.DEFAULT).isEmpty()) { node.getDebug().log("This node has an empty stamp, we are emitting dead code(?): %s", node); } setSourcePosition(node.getNodeSourcePosition()); @@ -477,7 +478,8 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { Value paramValue = params[param.index()]; - assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue + " " + getLIRGeneratorTool().getLIRKind(param.stamp()); + assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue + " " + + getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT)); setResult(param, gen.emitMove(paramValue)); } } @@ -506,7 +508,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio } protected LIRKind getPhiKind(PhiNode phi) { - return gen.getLIRKind(phi.stamp()); + return gen.getLIRKind(phi.stamp(NodeView.DEFAULT)); } @Override @@ -529,13 +531,13 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio } private void emitNullCheckBranch(IsNullNode node, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { - LIRKind kind = gen.getLIRKind(node.getValue().stamp()); + LIRKind kind = gen.getLIRKind(node.getValue().stamp(NodeView.DEFAULT)); Value nullValue = gen.emitConstant(kind, JavaConstant.NULL_POINTER); gen.emitCompareBranch(kind.getPlatformKind(), operand(node.getValue()), nullValue, Condition.EQ, false, trueSuccessor, falseSuccessor, trueSuccessorProbability); } public void emitCompareBranch(CompareNode compare, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { - PlatformKind kind = gen.getLIRKind(compare.getX().stamp()).getPlatformKind(); + PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind(); gen.emitCompareBranch(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, trueSuccessorProbability); } @@ -558,12 +560,12 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio public Variable emitConditional(LogicNode node, Value trueValue, Value falseValue) { if (node instanceof IsNullNode) { IsNullNode isNullNode = (IsNullNode) node; - LIRKind kind = gen.getLIRKind(isNullNode.getValue().stamp()); + LIRKind kind = gen.getLIRKind(isNullNode.getValue().stamp(NodeView.DEFAULT)); Value nullValue = gen.emitConstant(kind, JavaConstant.NULL_POINTER); return gen.emitConditionalMove(kind.getPlatformKind(), operand(isNullNode.getValue()), nullValue, Condition.EQ, false, trueValue, falseValue); } else if (node instanceof CompareNode) { CompareNode compare = (CompareNode) node; - PlatformKind kind = gen.getLIRKind(compare.getX().stamp()).getPlatformKind(); + PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind(); return gen.emitConditionalMove(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueValue, falseValue); } else if (node instanceof LogicConstantNode) { return gen.emitMove(((LogicConstantNode) node).getValue() ? trueValue : falseValue); @@ -579,7 +581,8 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio public void emitInvoke(Invoke x) { LoweredCallTargetNode callTarget = (LoweredCallTargetNode) x.callTarget(); FrameMapBuilder frameMapBuilder = gen.getResult().getFrameMapBuilder(); - CallingConvention invokeCc = frameMapBuilder.getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp().javaType(gen.getMetaAccess()), callTarget.signature(), gen); + CallingConvention invokeCc = frameMapBuilder.getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess()), + callTarget.signature(), gen); frameMapBuilder.callsMethod(invokeCc); Value[] parameters = visitInvokeArguments(invokeCc, callTarget.arguments()); @@ -650,7 +653,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio if (keyCount == 1) { assert defaultTarget != null; double probability = x.probability(x.keySuccessor(0)); - LIRKind kind = gen.getLIRKind(x.value().stamp()); + LIRKind kind = gen.getLIRKind(x.value().stamp(NodeView.DEFAULT)); Value key = gen.emitConstant(kind, x.keyAt(0)); gen.emitCompareBranch(kind.getPlatformKind(), gen.load(operand(x.value())), key, Condition.EQ, false, getLIRBlock(x.keySuccessor(0)), defaultTarget, probability); } else if (x instanceof IntegerSwitchNode && x.isSorted()) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java new file mode 100644 index 00000000000..f9b562f0e21 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2013, 2017, 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. + */ +package org.graalvm.compiler.debug.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; +import org.graalvm.compiler.debug.Versions; +import org.junit.After; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; +import org.junit.Test; + +public class VersionsTest { + private File temporaryDirectory; + + @After + public void cleanUp() throws IOException { + if (temporaryDirectory != null) { + Files.walkFileTree(temporaryDirectory.toPath(), new FileVisitor<Path>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + temporaryDirectory = null; + } + + @Test + public void emptyProperties() { + Path root = Paths.get("file:/"); + Versions v = new Versions(root); + assertEmpty(v.withVersions(null)); + } + + @Test + public void emptyWithNullProperties() { + Path root = Paths.get("file:/"); + Versions v = new Versions(root); + assertEmpty(v.withVersions(null)); + } + + @Test + public void readFromSameDirNullProps() throws IOException { + File dir = prepareReleaseFile(); + + Versions v = new Versions(dir.toPath()); + Map<Object, Object> map = v.withVersions(null); + assertNonModifiable(map); + + assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk")); + assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby")); + } + + @Test + public void readFromSameDir() throws IOException { + File dir = prepareReleaseFile(); + + Versions v = new Versions(dir.toPath()); + + Map<Object, Object> prepared = new HashMap<>(); + prepared.put("test", "best"); + + Map<Object, Object> map = v.withVersions(prepared); + assertSame(prepared, map); + + assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk")); + assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby")); + assertEquals("best", map.get("test")); + } + + @Test + public void readFromSubDirNullProps() throws IOException { + File dir = prepareSubReleaseFile(); + + Versions v = new Versions(dir.toPath()); + Map<Object, Object> map = v.withVersions(null); + assertNonModifiable(map); + + assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk")); + assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby")); + } + + @Test + public void readFromSubDir() throws IOException { + File dir = prepareSubReleaseFile(); + + Versions v = new Versions(dir.toPath()); + + Map<Object, Object> prepared = new HashMap<>(); + prepared.put("test", "best"); + + Map<Object, Object> map = v.withVersions(prepared); + assertSame(prepared, map); + + assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk")); + assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby")); + assertEquals("best", map.get("test")); + } + + private File prepareReleaseFile() throws IOException { + if (temporaryDirectory == null) { + temporaryDirectory = File.createTempFile("versions", ".tmp"); + temporaryDirectory.delete(); + assumeTrue(temporaryDirectory.mkdirs()); + try (FileWriter w = new FileWriter(new File(temporaryDirectory, "release"))) { +// @formatter:off + w.write( +"OS_NAME=linux\n" + +"OS_ARCH=amd64\n" + +"SOURCE=\" truffle:16055f1ffaf736b7b86dcfaea53971983cd9ae0a sdk:16055f1ffaf736b7b86dcfaea53971983cd9ae0a " + +"tools-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 graal-js:d374a8fd2733487a9f7518be6a55eb6163a779d1 " + +"graal-nodejs:3fcaf6874c9059d5ca5f0615edaa405d66cc1b02 truffleruby:7930979c3b0af09a910accaaf3e73b2a55d2bade " + +"fastr:079c6513b46f36abc24bce8aa6022c90576b3eaf graalpython:4cbee7853d460930c4d693970a21b73f811a4703 " + +"sulong:2c425f92caa004b12f60428a3e7e6e2715b51f87 substratevm:fcc1292a05e807a63589e24ce6073aafdef45bb9 " + +"compiler:16055f1ffaf736b7b86dcfaea53971983cd9ae0a substratevm-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 " + +"vm-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 graal-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 \"\n" + +"COMMIT_INFO={\"vm-enterprise\":{\"commit.rev\":\"fcc1292a05e807a63589e24ce6073aafdef45bb9\"," + +"\"commit.committer\":\"Vojin Jovanovic <vojin.jovanovic@oracle.com>\",}}\n" + +"GRAALVM_VERSION=\"0.29-dev\"" + ); +// @formatter:on + } + } + return temporaryDirectory; + } + + private File prepareSubReleaseFile() throws IOException { + File subdir = new File(prepareReleaseFile(), "subdir"); + assumeTrue(subdir.mkdirs()); + return subdir; + } + + private static void assertEmpty(Map<?, ?> map) { + assertNotNull(map); + assertTrue(map.isEmpty()); + assertNonModifiable(map); + } + + private static void assertNonModifiable(Map<?, ?> map) { + try { + map.put(null, null); + fail("Map shall not be modifiable: " + map); + } catch (UnsupportedOperationException ex) { + // ok + } + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java index 96cd6cbf524..ae2285f019b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java @@ -129,6 +129,20 @@ public final class DebugContext implements AutoCloseable { } } + /** + * Adds version properties to the provided map. The version properties are read at a start of + * the JVM from a JVM specific location. Each property identifiers a commit of a certain + * component in the system. The properties added to the {@code properties} map are prefixed with + * {@code "version."} prefix. + * + * @param properties map to add the version properties to or {@code null} + * @return {@code properties} with version properties added or an unmodifiable map containing + * the version properties if {@code properties == null} + */ + public static Map<Object, Object> addVersionProperties(Map<Object, Object> properties) { + return Versions.VERSIONS.withVersions(properties); + } + /** * The immutable configuration that can be shared between {@link DebugContext} objects. */ diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java new file mode 100644 index 00000000000..6de0bfe3020 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012, 2017, 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. + */ +package org.graalvm.compiler.debug; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** Avoid using directly. Only public for the needs of unit testing. */ +public final class Versions { + static final Versions VERSIONS; + static { + String home = System.getProperty("java.home"); + VERSIONS = new Versions(home == null ? null : new File(home).toPath()); + } + + private final Map<Object, Object> versions; + + public Versions(Path home) { + Map<Object, Object> map = new HashMap<>(); + ASSIGN: try { + Path info = findReleaseInfo(home); + if (info == null) { + break ASSIGN; + } + for (String line : Files.readAllLines(info)) { + final String prefix = "SOURCE="; + if (line.startsWith(prefix)) { + for (String versionInfo : line.substring(prefix.length()).replace('"', ' ').split(" ")) { + String[] idVersion = versionInfo.split(":"); + if (idVersion != null && idVersion.length == 2) { + map.put("version." + idVersion[0], idVersion[1]); + } + } + break ASSIGN; + } + } + } catch (IOException ex) { + // no versions file found + } + versions = Collections.unmodifiableMap(map); + } + + public Map<Object, Object> withVersions(Map<Object, Object> properties) { + if (properties == null) { + return versions; + } else { + properties.putAll(versions); + return properties; + } + } + + private static Path findReleaseInfo(Path jreDir) { + if (jreDir == null) { + return null; + } + Path releaseInJre = jreDir.resolve("release"); + if (Files.exists(releaseInJre)) { + return releaseInJre; + } + Path jdkDir = jreDir.getParent(); + if (jdkDir == null) { + return null; + } + Path releaseInJdk = jdkDir.resolve("release"); + return Files.exists(releaseInJdk) ? releaseInJdk : null; + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java index 5527d764061..e67915192cb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java @@ -49,6 +49,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.DirectCallTargetNode; import org.graalvm.compiler.nodes.FullInfopointNode; import org.graalvm.compiler.nodes.IndirectCallTargetNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.SafepointNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -110,7 +111,7 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { Value paramValue = params[param.index()]; - assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue.getValueKind() + " != " + param.stamp(); + assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue.getValueKind() + " != " + param.stamp(NodeView.DEFAULT); setResult(param, gen.emitMove(paramValue)); } } @@ -181,7 +182,7 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen public void visitBreakpointNode(BreakpointNode node) { JavaType[] sig = new JavaType[node.arguments().size()]; for (int i = 0; i < sig.length; i++) { - sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); + sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess()); } Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java index 4d1adff918e..6585e2135da 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java @@ -40,6 +40,7 @@ import org.graalvm.compiler.lir.asm.CompilationResultBuilder; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; @@ -170,7 +171,7 @@ public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest { @Input protected ValueNode input; protected LoadThroughPatchNode(ValueNode input) { - super(TYPE, input.stamp()); + super(TYPE, input.stamp(NodeView.DEFAULT)); this.input = input; } @@ -179,9 +180,9 @@ public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest { assert input.isConstant(); LIRGeneratorTool gen = generator.getLIRGeneratorTool(); - Variable ret = gen.newVariable(gen.getLIRKind(stamp())); + Variable ret = gen.newVariable(gen.getLIRKind(stamp(NodeView.DEFAULT))); - gen.append(new LoadThroughPatchOp(input.asConstant(), stamp() instanceof NarrowOopStamp, ret)); + gen.append(new LoadThroughPatchOp(input.asConstant(), stamp(NodeView.DEFAULT) instanceof NarrowOopStamp, ret)); generator.setResult(this, ret); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java index 70bf4dae8c9..e2be3f3bb26 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java @@ -43,6 +43,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CompressionNode; import org.graalvm.compiler.nodes.CompressionNode.CompressionOp; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; @@ -76,7 +77,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering { @Override public void generate(NodeLIRBuilderTool generator) { - LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp()); + LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); generator.setResult(this, heapBaseRegister.asValue(kind)); } } @@ -117,11 +118,6 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering { return false; } - @Override - protected boolean mightBeOptimized(ValueNode value) { - return super.mightBeOptimized(value) || value instanceof CompressionNode; - } - private boolean improveUncompression(AMD64AddressNode addr, CompressionNode compression, ValueNode other, boolean isBaseNegated, boolean isIndexNegated) { if (isBaseNegated || isIndexNegated || compression.getOp() != CompressionOp.Uncompress) { return false; @@ -134,7 +130,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering { } if (heapBaseRegister != null && encoding.getBase() == heapBase) { - if ((!generatePIC || compression.stamp() instanceof ObjectStamp) && other == null) { + if ((!generatePIC || compression.stamp(NodeView.DEFAULT) instanceof ObjectStamp) && other == null) { // With PIC it is only legal to do for oops since the base value may be // different at runtime. ValueNode base = compression.graph().unique(new HeapBaseNode(heapBaseRegister)); @@ -142,7 +138,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering { } else { return false; } - } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp() instanceof KlassPointerStamp)) { + } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp(NodeView.DEFAULT) instanceof KlassPointerStamp)) { if (generatePIC) { if (other == null) { ValueNode base = compression.graph().unique(new GraalHotSpotVMConfigNode(config, config.MARKID_NARROW_KLASS_BASE_ADDRESS, JavaKind.Long)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java index 4ce46a5f5fc..7f71cac66ff 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java @@ -46,6 +46,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.DirectCallTargetNode; import org.graalvm.compiler.nodes.FullInfopointNode; import org.graalvm.compiler.nodes.IndirectCallTargetNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.SafepointNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -113,7 +114,7 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { Value paramValue = params[param.index()]; - assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue.getValueKind() + " != " + param.stamp(); + assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue.getValueKind() + " != " + param.stamp(NodeView.DEFAULT); setResult(param, gen.emitMove(paramValue)); } } @@ -187,7 +188,7 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H public void visitBreakpointNode(BreakpointNode node) { JavaType[] sig = new JavaType[node.arguments().size()]; for (int i = 0; i < sig.length; i++) { - sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); + sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess()); } Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java index 2e94bbadafc..afe79ed1132 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java @@ -46,6 +46,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.DirectCallTargetNode; import org.graalvm.compiler.nodes.FullInfopointNode; import org.graalvm.compiler.nodes.IndirectCallTargetNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.SafepointNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -158,7 +159,7 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H public void visitBreakpointNode(BreakpointNode node) { JavaType[] sig = new JavaType[node.arguments().size()]; for (int i = 0; i < sig.length; i++) { - sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess()); + sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess()); } Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java index a08edd079c0..bd0c8693723 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java @@ -22,14 +22,15 @@ */ package org.graalvm.compiler.hotspot.test; -import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; -import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; - +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; import org.graalvm.compiler.core.common.type.Stamp; -import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; @@ -41,10 +42,8 @@ import org.junit.Assume; import org.junit.Before; import org.junit.Test; -import jdk.vm.ci.aarch64.AArch64; -import jdk.vm.ci.hotspot.HotSpotResolvedObjectType; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; +import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode; +import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes; /** * use @@ -55,7 +54,7 @@ import jdk.vm.ci.meta.JavaKind; * * to print disassembly. */ -public class AheadOfTimeCompilationTest extends GraalCompilerTest { +public class AheadOfTimeCompilationTest extends HotSpotGraalCompilerTest { public static final Object STATICFINALOBJECT = new Object(); public static final String STATICFINALSTRING = "test string"; @@ -74,9 +73,10 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest { public void testStaticFinalObjectAOT() { StructuredGraph result = compile("getStaticFinalObject", true); assertDeepEquals(1, getConstantNodes(result).count()); - Stamp constantStamp = getConstantNodes(result).first().stamp(); + Stamp constantStamp = getConstantNodes(result).first().stamp(NodeView.DEFAULT); Assert.assertTrue(constantStamp.toString(), constantStamp instanceof KlassPointerStamp); - assertDeepEquals(2, result.getNodes().filter(ReadNode.class).count()); + int expected = runtime().getVMConfig().classMirrorIsHandle ? 3 : 2; + assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count()); } @Test @@ -99,8 +99,8 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest { assertDeepEquals(1, filter.count()); HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(AheadOfTimeCompilationTest.class); assertDeepEquals(type.klass(), filter.first().asConstant()); - - assertDeepEquals(1, result.getNodes().filter(ReadNode.class).count()); + int expected = runtime().getVMConfig().classMirrorIsHandle ? 2 : 1; + assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count()); } @Test @@ -124,10 +124,10 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest { StructuredGraph result = compile("getPrimitiveClassObject", true); NodeIterable<ConstantNode> filter = getConstantNodes(result); assertDeepEquals(1, filter.count()); - Stamp constantStamp = filter.first().stamp(); + Stamp constantStamp = filter.first().stamp(NodeView.DEFAULT); Assert.assertTrue(constantStamp instanceof KlassPointerStamp); - - assertDeepEquals(2, result.getNodes().filter(ReadNode.class).count()); + int expected = runtime().getVMConfig().classMirrorIsHandle ? 3 : 2; + assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count()); } @Test diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java index 8363e827907..8802c18dc3f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java @@ -143,50 +143,49 @@ public class CheckGraalIntrinsics extends GraalTest { } static { + // These are dead add(IGNORE, - // dead "java/lang/Math.atan2(DD)D", - // Used during stack walking - "java/lang/Throwable.fillInStackTrace()Ljava/lang/Throwable;", - // Marker intrinsic id - "java/lang/invoke/MethodHandle.<compiledLambdaForm>*", - // Marker intrinsic id - "java/lang/invoke/MethodHandle.invoke*", - // Implemented through lowering - "java/lang/ref/Reference.get()Ljava/lang/Object;", - // Used during stack walk - "java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", - // Only used by C1 - "java/nio/Buffer.checkIndex(I)I", - // dead + "jdk/internal/misc/Unsafe.park(ZJ)V", + "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V", "sun/misc/Unsafe.park(ZJ)V", - // dead "sun/misc/Unsafe.prefetchRead(Ljava/lang/Object;J)V", - // dead "sun/misc/Unsafe.prefetchReadStatic(Ljava/lang/Object;J)V", - // dead "sun/misc/Unsafe.prefetchWrite(Ljava/lang/Object;J)V", - // dead "sun/misc/Unsafe.prefetchWriteStatic(Ljava/lang/Object;J)V", - // dead "sun/misc/Unsafe.unpark(Ljava/lang/Object;)V"); - add(TO_BE_INVESTIGATED, - // JDK 8 - "java/lang/Double.doubleToLongBits(D)J", - "java/lang/Float.floatToIntBits(F)I", - "java/lang/Integer.toString(I)Ljava/lang/String;", - "java/lang/Math.decrementExact(I)I", - "java/lang/Math.decrementExact(J)J", - "java/lang/Math.incrementExact(I)I", - "java/lang/Math.incrementExact(J)J", + // These only exist to assist escape analysis in C2 + add(IGNORE, + "java/lang/Throwable.fillInStackTrace()Ljava/lang/Throwable;"); + + // These are only used for the security handling during stack walking + add(IGNORE, + "java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + + // These are marker intrinsic ids only + add(IGNORE, + "java/lang/invoke/MethodHandle.<compiledLambdaForm>*", + "java/lang/invoke/MethodHandle.invoke*"); + + // These are implemented through lowering + add(IGNORE, + "java/lang/ref/Reference.get()Ljava/lang/Object;"); + + // These are only used by C1 + add(IGNORE, + "java/nio/Buffer.checkIndex(I)I"); + + // These do general compiler optimizations and convert min/max to cmov instructions. We are + // ignoring them as cmovs are not necessarily beneficial. + add(IGNORE, "java/lang/Math.max(II)I", - "java/lang/Math.min(II)I", - "java/lang/Math.negateExact(I)I", - "java/lang/Math.negateExact(J)J", + "java/lang/Math.min(II)I"); + + // These are known to be implemented down stream + add(IGNORE, + "java/lang/Integer.toString(I)Ljava/lang/String;", "java/lang/String.<init>(Ljava/lang/String;)V", - "java/lang/String.compareTo(Ljava/lang/String;)I", - "java/lang/String.indexOf(Ljava/lang/String;)I", "java/lang/StringBuffer.<init>()V", "java/lang/StringBuffer.<init>(I)V", "java/lang/StringBuffer.<init>(Ljava/lang/String;)V", @@ -201,172 +200,11 @@ public class CheckGraalIntrinsics extends GraalTest { "java/lang/StringBuilder.append(I)Ljava/lang/StringBuilder;", "java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;", "java/lang/StringBuilder.toString()Ljava/lang/String;", - "java/lang/reflect/Array.newArray(Ljava/lang/Class;I)Ljava/lang/Object;", "java/util/Arrays.copyOf([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;", - "java/util/Arrays.copyOfRange([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;", - "oracle/jrockit/jfr/Timing.counterTime()J", - "oracle/jrockit/jfr/VMJFR.classID0(Ljava/lang/Class;)J", - "oracle/jrockit/jfr/VMJFR.threadID()I", - "sun/nio/cs/ISO_8859_1$Encoder.encodeISOArray([CI[BII)I", - "sun/security/provider/DigestBase.implCompressMultiBlock([BII)I", - "sun/security/provider/SHA.implCompress([BI)V", - "sun/security/provider/SHA2.implCompress([BI)V", - "sun/security/provider/SHA5.implCompress([BI)V"); + "java/util/Arrays.copyOfRange([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;"); - add(TO_BE_INVESTIGATED, - // JDK 9 - "com/sun/crypto/provider/CounterMode.implCrypt([BII[BI)I", - "com/sun/crypto/provider/GHASH.processBlocks([BII[J[J)V", - "java/lang/Math.fma(DDD)D", - "java/lang/Math.fma(FFF)F", - "java/lang/Object.notify()V", - "java/lang/Object.notifyAll()V", - "java/lang/StringCoding.hasNegatives([BII)Z", - "java/lang/StringCoding.implEncodeISOArray([BI[BII)I", - "java/lang/StringLatin1.compareTo([B[B)I", - "java/lang/StringLatin1.compareToUTF16([B[B)I", - "java/lang/StringLatin1.equals([B[B)Z", - "java/lang/StringLatin1.indexOf([BI[BII)I", - "java/lang/StringLatin1.indexOf([B[B)I", - "java/lang/StringLatin1.inflate([BI[BII)V", - "java/lang/StringLatin1.inflate([BI[CII)V", - "java/lang/StringUTF16.compareTo([B[B)I", - "java/lang/StringUTF16.compareToLatin1([B[B)I", - "java/lang/StringUTF16.compress([BI[BII)I", - "java/lang/StringUTF16.compress([CI[BII)I", - "java/lang/StringUTF16.equals([B[B)Z", - "java/lang/StringUTF16.getChar([BI)C", - "java/lang/StringUTF16.getChars([BII[CI)V", - "java/lang/StringUTF16.indexOf([BI[BII)I", - "java/lang/StringUTF16.indexOf([B[B)I", - "java/lang/StringUTF16.indexOfChar([BIII)I", - "java/lang/StringUTF16.indexOfLatin1([BI[BII)I", - "java/lang/StringUTF16.indexOfLatin1([B[B)I", - "java/lang/StringUTF16.putChar([BII)V", - "java/lang/StringUTF16.toBytes([CII)[B", - "java/lang/Thread.onSpinWait()V", - "java/lang/invoke/MethodHandleImpl.isCompileConstant(Ljava/lang/Object;)Z", - "java/math/BigInteger.implMontgomeryMultiply([I[I[IIJ[I)[I", - "java/math/BigInteger.implMontgomerySquare([I[IIJ[I)[I", - "java/math/BigInteger.implMulAdd([I[IIII)I", - "java/math/BigInteger.implSquareToLen([II[II)[I", - "java/util/ArraysSupport.vectorizedMismatch(Ljava/lang/Object;JLjava/lang/Object;JII)I", - "java/util/stream/Streams$RangeIntSpliterator.forEachRemaining(Ljava/util/function/IntConsumer;)V", - "java/util/zip/Adler32.updateByteBuffer(IJII)I", - "java/util/zip/Adler32.updateBytes(I[BII)I", - "jdk/internal/misc/Unsafe.allocateUninitializedArray0(Ljava/lang/Class;I)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.compareAndExchangeByte(Ljava/lang/Object;JBB)B", - "jdk/internal/misc/Unsafe.compareAndExchangeByteAcquire(Ljava/lang/Object;JBB)B", - "jdk/internal/misc/Unsafe.compareAndExchangeByteRelease(Ljava/lang/Object;JBB)B", - "jdk/internal/misc/Unsafe.compareAndExchangeInt(Ljava/lang/Object;JII)I", - "jdk/internal/misc/Unsafe.compareAndExchangeIntAcquire(Ljava/lang/Object;JII)I", - "jdk/internal/misc/Unsafe.compareAndExchangeIntRelease(Ljava/lang/Object;JII)I", - "jdk/internal/misc/Unsafe.compareAndExchangeLong(Ljava/lang/Object;JJJ)J", - "jdk/internal/misc/Unsafe.compareAndExchangeLongAcquire(Ljava/lang/Object;JJJ)J", - "jdk/internal/misc/Unsafe.compareAndExchangeLongRelease(Ljava/lang/Object;JJJ)J", - "jdk/internal/misc/Unsafe.compareAndExchangeObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.compareAndExchangeObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.compareAndExchangeObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.compareAndExchangeShort(Ljava/lang/Object;JSS)S", - "jdk/internal/misc/Unsafe.compareAndExchangeShortAcquire(Ljava/lang/Object;JSS)S", - "jdk/internal/misc/Unsafe.compareAndExchangeShortRelease(Ljava/lang/Object;JSS)S", - "jdk/internal/misc/Unsafe.compareAndSetByte(Ljava/lang/Object;JBB)Z", - "jdk/internal/misc/Unsafe.compareAndSetShort(Ljava/lang/Object;JSS)Z", - "jdk/internal/misc/Unsafe.getAndAddByte(Ljava/lang/Object;JB)B", - "jdk/internal/misc/Unsafe.getAndAddShort(Ljava/lang/Object;JS)S", - "jdk/internal/misc/Unsafe.getAndSetByte(Ljava/lang/Object;JB)B", - "jdk/internal/misc/Unsafe.getAndSetShort(Ljava/lang/Object;JS)S", - "jdk/internal/misc/Unsafe.getBooleanAcquire(Ljava/lang/Object;J)Z", - "jdk/internal/misc/Unsafe.getBooleanOpaque(Ljava/lang/Object;J)Z", - "jdk/internal/misc/Unsafe.getByteAcquire(Ljava/lang/Object;J)B", - "jdk/internal/misc/Unsafe.getByteOpaque(Ljava/lang/Object;J)B", - "jdk/internal/misc/Unsafe.getCharAcquire(Ljava/lang/Object;J)C", - "jdk/internal/misc/Unsafe.getCharOpaque(Ljava/lang/Object;J)C", - "jdk/internal/misc/Unsafe.getDoubleAcquire(Ljava/lang/Object;J)D", - "jdk/internal/misc/Unsafe.getDoubleOpaque(Ljava/lang/Object;J)D", - "jdk/internal/misc/Unsafe.getFloatAcquire(Ljava/lang/Object;J)F", - "jdk/internal/misc/Unsafe.getFloatOpaque(Ljava/lang/Object;J)F", - "jdk/internal/misc/Unsafe.getIntAcquire(Ljava/lang/Object;J)I", - "jdk/internal/misc/Unsafe.getIntOpaque(Ljava/lang/Object;J)I", - "jdk/internal/misc/Unsafe.getLongAcquire(Ljava/lang/Object;J)J", - "jdk/internal/misc/Unsafe.getLongOpaque(Ljava/lang/Object;J)J", - "jdk/internal/misc/Unsafe.getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.getObjectOpaque(Ljava/lang/Object;J)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.getShortAcquire(Ljava/lang/Object;J)S", - "jdk/internal/misc/Unsafe.getShortOpaque(Ljava/lang/Object;J)S", - "jdk/internal/misc/Unsafe.park(ZJ)V", - "jdk/internal/misc/Unsafe.putBooleanOpaque(Ljava/lang/Object;JZ)V", - "jdk/internal/misc/Unsafe.putByteOpaque(Ljava/lang/Object;JB)V", - "jdk/internal/misc/Unsafe.putCharOpaque(Ljava/lang/Object;JC)V", - "jdk/internal/misc/Unsafe.putDoubleOpaque(Ljava/lang/Object;JD)V", - "jdk/internal/misc/Unsafe.putFloatOpaque(Ljava/lang/Object;JF)V", - "jdk/internal/misc/Unsafe.putIntOpaque(Ljava/lang/Object;JI)V", - "jdk/internal/misc/Unsafe.putLongOpaque(Ljava/lang/Object;JJ)V", - "jdk/internal/misc/Unsafe.putObjectOpaque(Ljava/lang/Object;JLjava/lang/Object;)V", - "jdk/internal/misc/Unsafe.putShortOpaque(Ljava/lang/Object;JS)V", - "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V", - "jdk/internal/misc/Unsafe.weakCompareAndSetByte(Ljava/lang/Object;JBB)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetByteAcquire(Ljava/lang/Object;JBB)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetBytePlain(Ljava/lang/Object;JBB)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetByteRelease(Ljava/lang/Object;JBB)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetInt(Ljava/lang/Object;JII)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetIntAcquire(Ljava/lang/Object;JII)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetIntPlain(Ljava/lang/Object;JII)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetIntRelease(Ljava/lang/Object;JII)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetLong(Ljava/lang/Object;JJJ)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetLongAcquire(Ljava/lang/Object;JJJ)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetLongPlain(Ljava/lang/Object;JJJ)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetLongRelease(Ljava/lang/Object;JJJ)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetObjectPlain(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetShort(Ljava/lang/Object;JSS)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetShortAcquire(Ljava/lang/Object;JSS)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetShortPlain(Ljava/lang/Object;JSS)Z", - "jdk/internal/misc/Unsafe.weakCompareAndSetShortRelease(Ljava/lang/Object;JSS)Z", - "jdk/internal/util/Preconditions.checkIndex(IILjava/util/function/BiFunction;)I", - "jdk/jfr/internal/JVM.counterTime()J", - "jdk/jfr/internal/JVM.getBufferWriter()Ljava/lang/Object;", - "jdk/jfr/internal/JVM.getClassId(Ljava/lang/Class;)J", - "sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray([CI[BII)I", - "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I", - "sun/security/provider/SHA.implCompress0([BI)V", - "sun/security/provider/SHA2.implCompress0([BI)V", - "sun/security/provider/SHA5.implCompress0([BI)V"); - - if (!getHostArchitectureName().equals("amd64")) { - add(TO_BE_INVESTIGATED, - // Can we implement these on non-AMD64 platforms? C2 seems to. - "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I", - "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J", - "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I", - "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J", - "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;"); - // JDK 9 - add(TO_BE_INVESTIGATED, - "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I", - "jdk/internal/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J", - "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I", - "jdk/internal/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J", - "jdk/internal/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;", - "jdk/internal/misc/Unsafe.getCharUnaligned(Ljava/lang/Object;J)C", - "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;J)I", - "jdk/internal/misc/Unsafe.getLongUnaligned(Ljava/lang/Object;J)J", - "jdk/internal/misc/Unsafe.getShortUnaligned(Ljava/lang/Object;J)S", - "jdk/internal/misc/Unsafe.putCharUnaligned(Ljava/lang/Object;JC)V", - "jdk/internal/misc/Unsafe.putIntUnaligned(Ljava/lang/Object;JI)V", - "jdk/internal/misc/Unsafe.putLongUnaligned(Ljava/lang/Object;JJ)V", - "jdk/internal/misc/Unsafe.putShortUnaligned(Ljava/lang/Object;JS)V"); - } - - HotSpotGraalRuntimeProvider rt = (HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class); - GraalHotSpotVMConfig config = rt.getVMConfig(); - - /* - * These are known to be implemented but the platform dependent conditions for when they are - * enabled are complex so just ignore them all the time. - */ + // These are known to be implemented but the platform dependent conditions + // for when they are enabled are complex so just ignore them all the time. add(IGNORE, "java/lang/Integer.bitCount(I)I", "java/lang/Integer.numberOfLeadingZeros(I)I", @@ -375,52 +213,323 @@ public class CheckGraalIntrinsics extends GraalTest { "java/lang/Long.numberOfLeadingZeros(J)I", "java/lang/Long.numberOfTrailingZeros(J)I"); - if (!config.useCRC32Intrinsics) { - // Registration of the CRC32 plugins is guarded by UseCRC32Intrinsics - add(IGNORE, "java/util/zip/CRC32.update(II)I"); - if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) { - add(IGNORE, - "java/util/zip/CRC32.updateByteBuffer(IJII)I", - "java/util/zip/CRC32.updateBytes(I[BII)I"); - } else { - add(IGNORE, - "java/util/zip/CRC32.updateByteBuffer0(IJII)I", - "java/util/zip/CRC32.updateBytes0(I[BII)I", - "java/util/zip/CRC32C.updateBytes(I[BII)I", - "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I"); - } - } else { - if (JDK9Method.JAVA_SPECIFICATION_VERSION >= 9) { + // Relevant for Java flight recorder + add(TO_BE_INVESTIGATED, + "oracle/jrockit/jfr/Timing.counterTime()J", + "oracle/jrockit/jfr/VMJFR.classID0(Ljava/lang/Class;)J", + "oracle/jrockit/jfr/VMJFR.threadID()I"); + + add(TO_BE_INVESTIGATED, + // Should be fairly easy to implement - C2 intrinsifies these to use "v != + // v" to check for NaN instead of looking at the bit pattern. + "java/lang/Double.doubleToLongBits(D)J", + "java/lang/Float.floatToIntBits(F)I", + + // Should be trivial to implement because we already have existing nodes + "java/lang/Math.decrementExact(I)I", + "java/lang/Math.decrementExact(J)J", + "java/lang/Math.incrementExact(I)I", + "java/lang/Math.incrementExact(J)J", + + // Similar to addExact + "java/lang/Math.negateExact(I)I", + // Similar to addExact + "java/lang/Math.negateExact(J)J", + // HotSpot MacroAssembler-based intrinsic + "java/lang/String.compareTo(Ljava/lang/String;)I", + // HotSpot MacroAssembler-based intrinsic + "java/lang/String.indexOf(Ljava/lang/String;)I", + // Can share most implementation parts with with + // Unsafe.allocateUninitializedArray0 + "java/lang/reflect/Array.newArray(Ljava/lang/Class;I)Ljava/lang/Object;", + // HotSpot MacroAssembler-based intrinsic + "sun/nio/cs/ISO_8859_1$Encoder.encodeISOArray([CI[BII)I", + // Stub based intrinsics but implementation seems complex in C2 + "sun/security/provider/DigestBase.implCompressMultiBlock([BII)I"); + + if (isJDK9OrHigher()) { + // Relevant for Java flight recorder + add(TO_BE_INVESTIGATED, + "jdk/jfr/internal/JVM.counterTime()J", + "jdk/jfr/internal/JVM.getBufferWriter()Ljava/lang/Object;", + "jdk/jfr/internal/JVM.getClassId(Ljava/lang/Class;)J"); + + add(TO_BE_INVESTIGATED, + // Some logic and a stub call + "com/sun/crypto/provider/CounterMode.implCrypt([BII[BI)I", + // Stub and very little logic + "com/sun/crypto/provider/GHASH.processBlocks([BII[J[J)V", + // HotSpot MacroAssembler-based intrinsic + "java/lang/Math.fma(DDD)D", + // HotSpot MacroAssembler-based intrinsic + "java/lang/Math.fma(FFF)F", + // Just a runtime call (the called C code has a better fast path) + "java/lang/Object.notify()V", + // Just a runtime call (the called C code has a better fast path) + "java/lang/Object.notifyAll()V", + // Emit pause instruction if os::is_MP() + "java/lang/Thread.onSpinWait()V", + // Just check if the argument is a compile time constant + "java/lang/invoke/MethodHandleImpl.isCompileConstant(Ljava/lang/Object;)Z", + // Some logic and a runtime call + "java/util/ArraysSupport.vectorizedMismatch(Ljava/lang/Object;JLjava/lang/Object;JII)I", + // Only used as a marker for vectorization? + "java/util/stream/Streams$RangeIntSpliterator.forEachRemaining(Ljava/util/function/IntConsumer;)V", + // Only implemented on non-AMD64 platforms (some logic and runtime call) + "java/util/zip/Adler32.updateByteBuffer(IJII)I", + // Only implemented on non-AMD64 platforms (some logic and runtime call) + "java/util/zip/Adler32.updateBytes(I[BII)I", + // similar to CRC32.updateBytes + "java/util/zip/CRC32C.updateBytes(I[BII)I", + // similar to CRC32.updateDirectByteBuffer + "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I", + // Emits a slow and a fast path and some dispatching logic + "jdk/internal/misc/Unsafe.allocateUninitializedArray0(Ljava/lang/Class;I)Ljava/lang/Object;", + + // Should be easy to implement as it seems to match the logic that is + // already implemented in ValueCompareAndSwapNode. On the high-level, we + // would need something similar to UnsafeCompareAndSwapNode but with a + // different result type. + "jdk/internal/misc/Unsafe.compareAndExchangeByte(Ljava/lang/Object;JBB)B", + "jdk/internal/misc/Unsafe.compareAndExchangeInt(Ljava/lang/Object;JII)I", + "jdk/internal/misc/Unsafe.compareAndExchangeLong(Ljava/lang/Object;JJJ)J", + "jdk/internal/misc/Unsafe.compareAndExchangeObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.compareAndExchangeShort(Ljava/lang/Object;JSS)S", + + // Should be easy to implement as we already have an implementation for + // int, long, and Object. + "jdk/internal/misc/Unsafe.compareAndSetByte(Ljava/lang/Object;JBB)Z", + "jdk/internal/misc/Unsafe.compareAndSetShort(Ljava/lang/Object;JSS)Z", + + // Should be easy to implement as we already have an implementation for + // int and long. + "jdk/internal/misc/Unsafe.getAndAddByte(Ljava/lang/Object;JB)B", + "jdk/internal/misc/Unsafe.getAndAddShort(Ljava/lang/Object;JS)S", + + // Should be easy to implement as we already have an implementation for + // int, long, and Object. + "jdk/internal/misc/Unsafe.getAndSetByte(Ljava/lang/Object;JB)B", + "jdk/internal/misc/Unsafe.getAndSetShort(Ljava/lang/Object;JS)S", + + // Control flow, deopts, and a cast + "jdk/internal/util/Preconditions.checkIndex(IILjava/util/function/BiFunction;)I", + // HotSpot MacroAssembler-based intrinsic + "sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray([CI[BII)I", + // Runtime call and some complex compiler logic + "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I"); + /* + * Per default, all these operations are mapped to some generic method for which we + * already have compiler intrinsics. Performance-wise it would be better to support them + * explicitly as the more generic method might be more restrictive and therefore slower + * than necessary. + */ + add(TO_BE_INVESTIGATED, + // Mapped to compareAndExchange* + "jdk/internal/misc/Unsafe.compareAndExchangeByteAcquire(Ljava/lang/Object;JBB)B", + "jdk/internal/misc/Unsafe.compareAndExchangeByteRelease(Ljava/lang/Object;JBB)B", + "jdk/internal/misc/Unsafe.compareAndExchangeIntAcquire(Ljava/lang/Object;JII)I", + "jdk/internal/misc/Unsafe.compareAndExchangeIntRelease(Ljava/lang/Object;JII)I", + "jdk/internal/misc/Unsafe.compareAndExchangeLongAcquire(Ljava/lang/Object;JJJ)J", + "jdk/internal/misc/Unsafe.compareAndExchangeLongRelease(Ljava/lang/Object;JJJ)J", + "jdk/internal/misc/Unsafe.compareAndExchangeObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.compareAndExchangeObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.compareAndExchangeShortAcquire(Ljava/lang/Object;JSS)S", + "jdk/internal/misc/Unsafe.compareAndExchangeShortRelease(Ljava/lang/Object;JSS)S", + + // Mapped to get*Volatile + "jdk/internal/misc/Unsafe.getBooleanAcquire(Ljava/lang/Object;J)Z", + "jdk/internal/misc/Unsafe.getBooleanOpaque(Ljava/lang/Object;J)Z", + "jdk/internal/misc/Unsafe.getByteAcquire(Ljava/lang/Object;J)B", + "jdk/internal/misc/Unsafe.getByteOpaque(Ljava/lang/Object;J)B", + "jdk/internal/misc/Unsafe.getCharAcquire(Ljava/lang/Object;J)C", + "jdk/internal/misc/Unsafe.getCharOpaque(Ljava/lang/Object;J)C", + "jdk/internal/misc/Unsafe.getDoubleAcquire(Ljava/lang/Object;J)D", + "jdk/internal/misc/Unsafe.getDoubleOpaque(Ljava/lang/Object;J)D", + "jdk/internal/misc/Unsafe.getFloatAcquire(Ljava/lang/Object;J)F", + "jdk/internal/misc/Unsafe.getFloatOpaque(Ljava/lang/Object;J)F", + "jdk/internal/misc/Unsafe.getIntAcquire(Ljava/lang/Object;J)I", + "jdk/internal/misc/Unsafe.getIntOpaque(Ljava/lang/Object;J)I", + "jdk/internal/misc/Unsafe.getLongAcquire(Ljava/lang/Object;J)J", + "jdk/internal/misc/Unsafe.getLongOpaque(Ljava/lang/Object;J)J", + "jdk/internal/misc/Unsafe.getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.getObjectOpaque(Ljava/lang/Object;J)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.getShortAcquire(Ljava/lang/Object;J)S", + "jdk/internal/misc/Unsafe.getShortOpaque(Ljava/lang/Object;J)S", + + // Mapped to put*Volatile + "jdk/internal/misc/Unsafe.putBooleanOpaque(Ljava/lang/Object;JZ)V", + "jdk/internal/misc/Unsafe.putByteOpaque(Ljava/lang/Object;JB)V", + "jdk/internal/misc/Unsafe.putCharOpaque(Ljava/lang/Object;JC)V", + "jdk/internal/misc/Unsafe.putDoubleOpaque(Ljava/lang/Object;JD)V", + "jdk/internal/misc/Unsafe.putFloatOpaque(Ljava/lang/Object;JF)V", + "jdk/internal/misc/Unsafe.putIntOpaque(Ljava/lang/Object;JI)V", + "jdk/internal/misc/Unsafe.putLongOpaque(Ljava/lang/Object;JJ)V", + "jdk/internal/misc/Unsafe.putObjectOpaque(Ljava/lang/Object;JLjava/lang/Object;)V", + "jdk/internal/misc/Unsafe.putShortOpaque(Ljava/lang/Object;JS)V", + + // Mapped to compareAndSet* + "jdk/internal/misc/Unsafe.weakCompareAndSetByte(Ljava/lang/Object;JBB)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetByteAcquire(Ljava/lang/Object;JBB)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetBytePlain(Ljava/lang/Object;JBB)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetByteRelease(Ljava/lang/Object;JBB)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetInt(Ljava/lang/Object;JII)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetIntAcquire(Ljava/lang/Object;JII)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetIntPlain(Ljava/lang/Object;JII)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetIntRelease(Ljava/lang/Object;JII)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetLong(Ljava/lang/Object;JJJ)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetLongAcquire(Ljava/lang/Object;JJJ)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetLongPlain(Ljava/lang/Object;JJJ)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetLongRelease(Ljava/lang/Object;JJJ)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetObjectPlain(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetShort(Ljava/lang/Object;JSS)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetShortAcquire(Ljava/lang/Object;JSS)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetShortPlain(Ljava/lang/Object;JSS)Z", + "jdk/internal/misc/Unsafe.weakCompareAndSetShortRelease(Ljava/lang/Object;JSS)Z"); + + // Compact string support - HotSpot MacroAssembler-based intrinsic or complex C2 logic. + add(TO_BE_INVESTIGATED, + "java/lang/StringCoding.hasNegatives([BII)Z", + "java/lang/StringCoding.implEncodeISOArray([BI[BII)I", + "java/lang/StringLatin1.compareTo([B[B)I", + "java/lang/StringLatin1.compareToUTF16([B[B)I", + "java/lang/StringLatin1.equals([B[B)Z", + "java/lang/StringLatin1.indexOf([BI[BII)I", + "java/lang/StringLatin1.indexOf([B[B)I", + "java/lang/StringLatin1.inflate([BI[BII)V", + "java/lang/StringLatin1.inflate([BI[CII)V", + "java/lang/StringUTF16.compareTo([B[B)I", + "java/lang/StringUTF16.compareToLatin1([B[B)I", + "java/lang/StringUTF16.compress([BI[BII)I", + "java/lang/StringUTF16.compress([CI[BII)I", + "java/lang/StringUTF16.equals([B[B)Z", + "java/lang/StringUTF16.getChar([BI)C", + "java/lang/StringUTF16.getChars([BII[CI)V", + "java/lang/StringUTF16.indexOf([BI[BII)I", + "java/lang/StringUTF16.indexOf([B[B)I", + "java/lang/StringUTF16.indexOfChar([BIII)I", + "java/lang/StringUTF16.indexOfLatin1([BI[BII)I", + "java/lang/StringUTF16.indexOfLatin1([B[B)I", + "java/lang/StringUTF16.putChar([BII)V", + "java/lang/StringUTF16.toBytes([CII)[B"); + } + + if (!getHostArchitectureName().equals("amd64")) { + // Can we implement these on non-AMD64 platforms? C2 seems to. + add(TO_BE_INVESTIGATED, + "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I", + "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J", + "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I", + "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J", + "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;"); + + if (isJDK9OrHigher()) { add(TO_BE_INVESTIGATED, - "java/util/zip/CRC32C.updateBytes(I[BII)I", - "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I"); + "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I", + "jdk/internal/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J", + "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I", + "jdk/internal/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J", + "jdk/internal/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;", + "jdk/internal/misc/Unsafe.getCharUnaligned(Ljava/lang/Object;J)C", + "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;J)I", + "jdk/internal/misc/Unsafe.getLongUnaligned(Ljava/lang/Object;J)J", + "jdk/internal/misc/Unsafe.getShortUnaligned(Ljava/lang/Object;J)S", + "jdk/internal/misc/Unsafe.putCharUnaligned(Ljava/lang/Object;JC)V", + "jdk/internal/misc/Unsafe.putIntUnaligned(Ljava/lang/Object;JI)V", + "jdk/internal/misc/Unsafe.putLongUnaligned(Ljava/lang/Object;JJ)V", + "jdk/internal/misc/Unsafe.putShortUnaligned(Ljava/lang/Object;JS)V"); } } - if (!config.useAESIntrinsics) { - // Registration of the AES plugins is guarded by UseAESIntrinsics - if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) { + HotSpotGraalRuntimeProvider rt = (HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class); + GraalHotSpotVMConfig config = rt.getVMConfig(); + + /* + * The intrinsics down here are known to be implemented but they are not always enabled on + * the HotSpot side (e.g., because they require certain CPU features). So, we are ignoring + * them if the HotSpot config tells us that they can't be used. + */ + + // CRC32 intrinsics + if (!config.useCRC32Intrinsics) { + add(IGNORE, "java/util/zip/CRC32.update(II)I"); + if (isJDK9OrHigher()) { add(IGNORE, - "com/sun/crypto/provider/AESCrypt.decryptBlock([BI[BI)V", - "com/sun/crypto/provider/AESCrypt.encryptBlock([BI[BI)V", - "com/sun/crypto/provider/CipherBlockChaining.decrypt([BII[BI)I", - "com/sun/crypto/provider/CipherBlockChaining.encrypt([BII[BI)I"); + "java/util/zip/CRC32.updateByteBuffer0(IJII)I", + "java/util/zip/CRC32.updateBytes0(I[BII)I"); } else { + add(IGNORE, + "java/util/zip/CRC32.updateByteBuffer(IJII)I", + "java/util/zip/CRC32.updateBytes(I[BII)I"); + } + } + + // AES intrinsics + if (!config.useAESIntrinsics) { + if (isJDK9OrHigher()) { add(IGNORE, "com/sun/crypto/provider/AESCrypt.implDecryptBlock([BI[BI)V", "com/sun/crypto/provider/AESCrypt.implEncryptBlock([BI[BI)V", "com/sun/crypto/provider/CipherBlockChaining.implDecrypt([BII[BI)I", "com/sun/crypto/provider/CipherBlockChaining.implEncrypt([BII[BI)I"); - } - } - if (!config.useMultiplyToLenIntrinsic()) { - // Registration of the AES plugins is guarded by UseAESIntrinsics - if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) { - add(IGNORE, "java/math/BigInteger.multiplyToLen([II[II[I)[I"); } else { - add(IGNORE, "java/math/BigInteger.implMultiplyToLen([II[II[I)[I"); + add(IGNORE, + "com/sun/crypto/provider/AESCrypt.decryptBlock([BI[BI)V", + "com/sun/crypto/provider/AESCrypt.encryptBlock([BI[BI)V", + "com/sun/crypto/provider/CipherBlockChaining.decrypt([BII[BI)I", + "com/sun/crypto/provider/CipherBlockChaining.encrypt([BII[BI)I"); } } + + // BigInteger intrinsics + if (!config.useMultiplyToLenIntrinsic()) { + if (isJDK9OrHigher()) { + add(IGNORE, "java/math/BigInteger.implMultiplyToLen([II[II[I)[I"); + } else { + add(IGNORE, "java/math/BigInteger.multiplyToLen([II[II[I)[I"); + } + } + if (!config.useMulAddIntrinsic()) { + add(IGNORE, "java/math/BigInteger.implMulAdd([I[IIII)I"); + } + if (!config.useMontgomeryMultiplyIntrinsic()) { + add(IGNORE, "java/math/BigInteger.implMontgomeryMultiply([I[I[IIJ[I)[I"); + } + if (!config.useMontgomerySquareIntrinsic()) { + add(IGNORE, "java/math/BigInteger.implMontgomerySquare([I[IIJ[I)[I"); + } + if (!config.useSquareToLenIntrinsic()) { + add(IGNORE, "java/math/BigInteger.implSquareToLen([II[II)[I"); + } + + // SHA intrinsics + if (!config.useSHA1Intrinsics()) { + if (isJDK9OrHigher()) { + add(IGNORE, "sun/security/provider/SHA.implCompress0([BI)V"); + } else { + add(IGNORE, "sun/security/provider/SHA.implCompress([BI)V"); + } + } + if (!config.useSHA256Intrinsics()) { + if (isJDK9OrHigher()) { + add(IGNORE, "sun/security/provider/SHA2.implCompress0([BI)V"); + } else { + add(IGNORE, "sun/security/provider/SHA2.implCompress([BI)V"); + } + } + if (!config.useSHA512Intrinsics()) { + if (isJDK9OrHigher()) { + add(IGNORE, "sun/security/provider/SHA5.implCompress0([BI)V"); + } else { + add(IGNORE, "sun/security/provider/SHA5.implCompress([BI)V"); + } + } + } + + private static boolean isJDK9OrHigher() { + return JDK9Method.JAVA_SPECIFICATION_VERSION >= 9; } private static String getHostArchitectureName() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java index 4f53585d4d4..dea1d25a430 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java @@ -106,7 +106,12 @@ public class CompilationWrapperTest extends GraalCompilerTest { final int maxProblems = 4; Probe[] probes = { new Probe("To capture more information for diagnosing or reporting a compilation", maxProblems), - new Probe("Retrying compilation of", maxProblems), + new Probe("Retrying compilation of", maxProblems) { + @Override + String test() { + return actualOccurrences > 0 && actualOccurrences <= maxProblems ? null : String.format("expected occurrences to be in [1 .. %d]", maxProblems); + } + }, new Probe("adjusting CompilationFailureAction from Diagnose to Print", 1), new Probe("adjusting CompilationFailureAction from Print to Silent", 1), }; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java new file mode 100644 index 00000000000..98692350b86 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 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. + */ +package org.graalvm.compiler.hotspot.test; + +import java.util.function.Function; + +import org.graalvm.compiler.test.GraalTest; +import org.junit.Assume; +import org.junit.Test; + +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.code.InvalidInstalledCodeException; +import jdk.vm.ci.code.stack.InspectedFrame; +import jdk.vm.ci.code.stack.StackIntrospection; +import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * Create a single object which is referenced from a local, the expression stack and the lock state + * and then ensure that identity is maintained when the frame is forced to be materialized by + * {@link InspectedFrame#materializeVirtualObjects(boolean)}. + */ +public class HotSpotStackIntrospectionTest extends HotSpotGraalCompilerTest { + + static StackIntrospection stackIntrospection = HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getStackIntrospection(); + static volatile int v; + + public static void testSynchronizedSnippet(Function<Void, Void> f) { + Object a = new Object(); + synchronized (a) { + testOnStack(a, forceFrameState(a, f), a); + // This object should be locked so try to notify on it + a.notify(); + } + } + + public static void testSnippet(Function<Void, Void> f) { + Object a = new Object(); + testOnStack(a, forceFrameState(a, f), a); + } + + private static void testOnStack(Object a, Object a2, Object a3) { + if (a != a2 || a != a3) { + throw new InternalError(); + } + } + + private static Object forceFrameState(Object a, Function<Void, Void> f) { + // Use a volatile store to ensure a FrameState is captured after this point. + v++; + f.apply(null); + return a; + } + + @Test(timeout = 20000) + public void run() throws InvalidInstalledCodeException { + // The JDK9 bits are currently broken + Assume.assumeTrue(GraalTest.Java8OrEarlier); + test("testSnippet"); + } + + @Test(timeout = 20000) + public void runSynchronized() throws InvalidInstalledCodeException { + // The JDK9 bits are currently broken + Assume.assumeTrue(GraalTest.Java8OrEarlier); + test("testSynchronizedSnippet"); + } + + private void test(String name) throws InvalidInstalledCodeException { + ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(name)); + Function<Void, Void> f = o -> { + stackIntrospection.iterateFrames(null, null, 0, frame -> { + if (frame.getMethod().equals(method)) { + frame.materializeVirtualObjects(true); + } + return null; + }); + return null; + }; + InstalledCode code = getCode(method); + code.executeVarargs(f); + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java index 7864f301c10..bbbadb9df58 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.hotspot.test; +import org.graalvm.compiler.hotspot.meta.HotSpotUnsafeSubstitutions; import org.graalvm.compiler.replacements.test.MethodSubstitutionTest; import org.junit.Test; @@ -56,7 +57,7 @@ public class HotSpotUnsafeSubstitutionTest extends MethodSubstitutionTest { @Test public void testUnsafeSubstitutions() throws Exception { - testGraph("unsafeCopyMemory"); + testGraph("unsafeCopyMemory", HotSpotUnsafeSubstitutions.copyMemoryName); } public void unsafeCopyMemory(Object srcBase, long srcOffset, Object dstBase, long dstOffset, long bytes) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java index d3c57cfe2c0..395521ad0fd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java @@ -103,6 +103,7 @@ import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoweredCallTargetNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.SafepointNode; import org.graalvm.compiler.nodes.StartNode; @@ -254,7 +255,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider instanceofSnippets.lower(instanceOfDynamicNode, tool); } else { ValueNode mirror = instanceOfDynamicNode.getMirrorOrHub(); - if (mirror.stamp().getStackKind() == JavaKind.Object) { + if (mirror.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Object) { ClassGetHubNode classGetHub = graph.unique(new ClassGetHubNode(mirror)); instanceOfDynamicNode.setMirror(classGetHub); } @@ -409,7 +410,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider StructuredGraph graph = n.graph(); assert !n.getHub().isConstant(); AddressNode address = createOffsetAddress(graph, n.getHub(), runtime.getVMConfig().klassLayoutHelperOffset); - n.replaceAtUsagesAndDelete(graph.unique(new FloatingReadNode(address, KLASS_LAYOUT_HELPER_LOCATION, null, n.stamp(), null, BarrierType.NONE))); + n.replaceAtUsagesAndDelete(graph.unique(new FloatingReadNode(address, KLASS_LAYOUT_HELPER_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE))); } private void lowerHubGetClassNode(HubGetClassNode n, LoweringTool tool) { @@ -422,11 +423,12 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider StructuredGraph graph = n.graph(); assert !hub.isConstant() || GraalOptions.ImmutableCode.getValue(graph.getOptions()); AddressNode mirrorAddress = createOffsetAddress(graph, hub, vmConfig.classMirrorOffset); - FloatingReadNode read = graph.unique(new FloatingReadNode(mirrorAddress, CLASS_MIRROR_LOCATION, null, vmConfig.classMirrorIsHandle ? StampFactory.forKind(target.wordJavaKind) : n.stamp(), - null, BarrierType.NONE)); + FloatingReadNode read = graph.unique( + new FloatingReadNode(mirrorAddress, CLASS_MIRROR_LOCATION, null, vmConfig.classMirrorIsHandle ? StampFactory.forKind(target.wordJavaKind) : n.stamp(NodeView.DEFAULT), + null, BarrierType.NONE)); if (vmConfig.classMirrorIsHandle) { AddressNode address = createOffsetAddress(graph, read, 0); - read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_HANDLE_LOCATION, null, n.stamp(), null, BarrierType.NONE)); + read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_HANDLE_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE)); } n.replaceAtUsagesAndDelete(read); } @@ -439,7 +441,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider StructuredGraph graph = n.graph(); assert !n.getValue().isConstant(); AddressNode address = createOffsetAddress(graph, n.getValue(), runtime.getVMConfig().klassOffset); - FloatingReadNode read = graph.unique(new FloatingReadNode(address, CLASS_KLASS_LOCATION, null, n.stamp(), null, BarrierType.NONE)); + FloatingReadNode read = graph.unique(new FloatingReadNode(address, CLASS_KLASS_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE)); n.replaceAtUsagesAndDelete(read); } @@ -448,7 +450,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); NodeInputList<ValueNode> parameters = callTarget.arguments(); ValueNode receiver = parameters.size() <= 0 ? null : parameters.get(0); - if (!callTarget.isStatic() && receiver.stamp() instanceof ObjectStamp && !StampTool.isPointerNonNull(receiver)) { + if (!callTarget.isStatic() && receiver.stamp(NodeView.DEFAULT) instanceof ObjectStamp && !StampTool.isPointerNonNull(receiver)) { ValueNode nonNullReceiver = createNullCheckedValue(receiver, invoke.asNode(), tool); parameters.set(0, nonNullReceiver); receiver = nonNullReceiver; @@ -586,7 +588,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider int size = osrLocal.getStackKind().getSlotCount(); int offset = localsOffset - (osrLocal.index() + size - 1) * wordSize; AddressNode address = createOffsetAddress(graph, buffer, offset); - ReadNode load = graph.add(new ReadNode(address, any(), osrLocal.stamp(), BarrierType.NONE)); + ReadNode load = graph.add(new ReadNode(address, any(), osrLocal.stamp(NodeView.DEFAULT), BarrierType.NONE)); osrLocal.replaceAndDelete(load); graph.addBeforeFixed(migrationEnd, load); } @@ -609,7 +611,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider // load the displaced mark from the osr buffer AddressNode addressDisplacedHeader = createOffsetAddress(graph, buffer, offsetDisplacedHeader); - ReadNode loadDisplacedHeader = graph.add(new ReadNode(addressDisplacedHeader, any(), lock.stamp(), BarrierType.NONE)); + ReadNode loadDisplacedHeader = graph.add(new ReadNode(addressDisplacedHeader, any(), lock.stamp(NodeView.DEFAULT), BarrierType.NONE)); graph.addBeforeFixed(migrationEnd, loadDisplacedHeader); // we need to initialize the stack slot for the lock @@ -623,7 +625,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider // load the lock object from the osr buffer AddressNode addressLockObject = createOffsetAddress(graph, buffer, offsetLockObject); - ReadNode loadObject = graph.add(new ReadNode(addressLockObject, any(), lock.stamp(), BarrierType.NONE)); + ReadNode loadObject = graph.add(new ReadNode(addressLockObject, any(), lock.stamp(NodeView.DEFAULT), BarrierType.NONE)); lock.replaceAndDelete(loadObject); graph.addBeforeFixed(migrationEnd, loadObject); } @@ -688,7 +690,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider } StructuredGraph graph = node.graph(); - ForeignCallNode foreignCallNode = graph.add(new ForeignCallNode(foreignCalls, descriptor, node.stamp(), node.getArguments())); + ForeignCallNode foreignCallNode = graph.add(new ForeignCallNode(foreignCalls, descriptor, node.stamp(NodeView.DEFAULT), node.getArguments())); graph.replaceFixedWithFixed(node, foreignCallNode); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index 5e1504ab14c..84dcaad7b8f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -66,6 +66,7 @@ import org.graalvm.compiler.nodes.DynamicPiNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; @@ -318,7 +319,7 @@ public class HotSpotGraphBuilderPlugins { private static boolean readMetaspaceConstantPoolElement(GraphBuilderContext b, ValueNode constantPoolOop, ValueNode index, JavaKind elementKind, WordTypes wordTypes, GraalHotSpotVMConfig config) { ValueNode constants = getMetaspaceConstantPool(b, constantPoolOop, wordTypes, config); int shift = CodeUtil.log2(wordTypes.getWordKind().getByteCount()); - ValueNode scaledIndex = b.add(new LeftShiftNode(IntegerConvertNode.convert(index, StampFactory.forKind(JavaKind.Long)), b.add(ConstantNode.forInt(shift)))); + ValueNode scaledIndex = b.add(new LeftShiftNode(IntegerConvertNode.convert(index, StampFactory.forKind(JavaKind.Long), NodeView.DEFAULT), b.add(ConstantNode.forInt(shift)))); ValueNode offset = b.add(new AddNode(scaledIndex, b.add(ConstantNode.forLong(config.constantPoolSize)))); AddressNode elementAddress = b.add(new OffsetAddressNode(constants, offset)); boolean notCompressible = false; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java index ec2c62b3eb4..e9f01f60583 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.hotspot.nodes.aot.ResolveDynamicConstantNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.InvokeDynamicPlugin; import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; @@ -136,7 +137,7 @@ public class HotSpotInvokeDynamicPlugin implements InvokeDynamicPlugin { ConstantNode appendixNode = ConstantNode.forConstant(appendix, builder.getMetaAccess(), builder.getGraph()); - Stamp appendixStamp = appendixNode.stamp(); + Stamp appendixStamp = appendixNode.stamp(NodeView.DEFAULT); Stamp resolveStamp = treatAppendixAsConstant ? appendixStamp : appendixStamp.unrestricted(); ResolveDynamicConstantNode resolveNode = new ResolveDynamicConstantNode(resolveStamp, appendixNode); ResolveDynamicConstantNode added = builder.append(resolveNode); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java index f1c7359eba3..376325e759b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java @@ -87,7 +87,7 @@ public class HotSpotSnippetReflectionProvider implements SnippetReflectionProvid // Need to test all fields since there no guarantee under the JMM // about the order in which these fields are written. GraalHotSpotVMConfig config = runtime.getVMConfig(); - if (configType == null || wordTypesType == null || configType == null) { + if (configType == null || wordTypesType == null || runtimeType == null) { wordTypesType = wordTypes.getClass(); runtimeType = runtime.getClass(); configType = config.getClass(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java index 354b8e1f8af..246491cef7d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java @@ -40,6 +40,7 @@ import org.graalvm.compiler.hotspot.word.HotSpotOperation; import org.graalvm.compiler.hotspot.word.HotSpotOperation.HotspotOpcode; import org.graalvm.compiler.hotspot.word.PointerCastNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.ConditionalNode; import org.graalvm.compiler.nodes.calc.IsNullNode; @@ -102,23 +103,23 @@ class HotSpotWordOperationPlugin extends WordOperationPlugin { HotspotOpcode opcode = operation.opcode(); ValueNode left = args[0]; ValueNode right = args[1]; - assert left.stamp() instanceof MetaspacePointerStamp : left + " " + left.stamp(); - assert right.stamp() instanceof MetaspacePointerStamp : right + " " + right.stamp(); + assert left.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : left + " " + left.stamp(NodeView.DEFAULT); + assert right.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : right + " " + right.stamp(NodeView.DEFAULT); assert opcode == POINTER_EQ || opcode == POINTER_NE; PointerEqualsNode comparison = b.add(new PointerEqualsNode(left, right)); ValueNode eqValue = b.add(forBoolean(opcode == POINTER_EQ)); ValueNode neValue = b.add(forBoolean(opcode == POINTER_NE)); - b.addPush(returnKind, ConditionalNode.create(comparison, eqValue, neValue)); + b.addPush(returnKind, ConditionalNode.create(comparison, eqValue, neValue, NodeView.DEFAULT)); break; case IS_NULL: assert args.length == 1; ValueNode pointer = args[0]; - assert pointer.stamp() instanceof MetaspacePointerStamp; + assert pointer.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp; LogicNode isNull = b.addWithInputs(IsNullNode.create(pointer)); - b.addPush(returnKind, ConditionalNode.create(isNull, b.add(forBoolean(true)), b.add(forBoolean(false)))); + b.addPush(returnKind, ConditionalNode.create(isNull, b.add(forBoolean(true)), b.add(forBoolean(false)), NodeView.DEFAULT)); break; case FROM_POINTER: diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java index ecd8ba0b811..b164b1226b5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.hotspot.nodes.type.HotSpotNarrowOopStamp; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CompressionNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.hotspot.HotSpotCompressedNullConstant; @@ -45,7 +46,7 @@ public final class HotSpotCompressionNode extends CompressionNode { public static final NodeClass<HotSpotCompressionNode> TYPE = NodeClass.create(HotSpotCompressionNode.class); public HotSpotCompressionNode(CompressionOp op, ValueNode input, CompressEncoding encoding) { - super(TYPE, op, input, HotSpotNarrowOopStamp.mkStamp(op, input.stamp(), encoding), encoding); + super(TYPE, op, input, HotSpotNarrowOopStamp.mkStamp(op, input.stamp(NodeView.DEFAULT), encoding), encoding); } public static HotSpotCompressionNode compress(ValueNode input, CompressEncoding encoding) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java index aff5ce9b872..63674b901da 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java @@ -29,6 +29,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_16; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -42,7 +43,7 @@ public class InitializeKlassNode extends DeoptimizingFixedWithNextNode implement @Input ValueNode value; public InitializeKlassNode(ValueNode value) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java index 2630a60dcd3..b7af9ab772a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizingNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; @@ -61,7 +62,7 @@ public class InitializeKlassStubCall extends AbstractMemoryCheckpoint implements protected Constant constant; protected InitializeKlassStubCall(ValueNode value, ValueNode string) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.string = string; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java index 2603e78cee4..85fac77d08b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.hotspot.word.MethodPointer; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -56,14 +57,14 @@ public class LoadConstantIndirectlyFixedNode extends FixedWithNextNode implement protected HotSpotConstantLoadAction action; public LoadConstantIndirectlyFixedNode(ValueNode value) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.constant = null; this.action = HotSpotConstantLoadAction.RESOLVE; } public LoadConstantIndirectlyFixedNode(ValueNode value, HotSpotConstantLoadAction action) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.constant = null; this.action = action; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java index 8758c02cba3..7cba4506833 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.hotspot.HotSpotLIRGenerator; import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -55,14 +56,14 @@ public class LoadConstantIndirectlyNode extends FloatingNode implements Canonica protected HotSpotConstantLoadAction action; public LoadConstantIndirectlyNode(ValueNode value) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.constant = null; this.action = HotSpotConstantLoadAction.RESOLVE; } public LoadConstantIndirectlyNode(ValueNode value, HotSpotConstantLoadAction action) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.constant = null; this.action = action; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java index 73202626047..c6b131658aa 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -41,13 +42,13 @@ public class ResolveConstantNode extends DeoptimizingFixedWithNextNode implement protected HotSpotConstantLoadAction action; public ResolveConstantNode(ValueNode value, HotSpotConstantLoadAction action) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.action = action; } public ResolveConstantNode(ValueNode value) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.action = HotSpotConstantLoadAction.RESOLVE; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java index 6a6393b1990..912d295f2d4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.hotspot.nodes.DeoptimizingStubCall; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.lir.LIRFrameState; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -59,14 +60,14 @@ public class ResolveConstantStubCall extends DeoptimizingStubCall implements Can protected HotSpotConstantLoadAction action; public ResolveConstantStubCall(ValueNode value, ValueNode string) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.string = string; this.action = HotSpotConstantLoadAction.RESOLVE; } public ResolveConstantStubCall(ValueNode value, ValueNode string, HotSpotConstantLoadAction action) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.string = string; this.action = action; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java index dd3a558d79e..f811e3d923f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizingNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -59,7 +60,7 @@ public class ResolveDynamicStubCall extends AbstractMemoryCheckpoint implements protected Constant constant; public ResolveDynamicStubCall(ValueNode value) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java index 116ef56436b..e790daea4fd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java @@ -50,6 +50,7 @@ import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StartNode; @@ -185,8 +186,8 @@ public class OnStackReplacementPhase extends Phase { * (if a branch was not parsed for example). In cases when this is possible, we * insert a guard and narrow the OSRLocal stamp at its usages. */ - Stamp narrowedStamp = proxy.value().stamp(); - Stamp unrestrictedStamp = proxy.stamp().unrestricted(); + Stamp narrowedStamp = proxy.value().stamp(NodeView.DEFAULT); + Stamp unrestrictedStamp = proxy.stamp(NodeView.DEFAULT).unrestricted(); ValueNode osrLocal; if (i >= localsSize) { osrLocal = graph.addOrUnique(new OSRLockNode(i - localsSize, unrestrictedStamp)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java index 07576103017..b3781b00f0e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.hotspot.nodes.profiling.RandomSeedNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.InvokeNode; import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -130,7 +131,7 @@ public class FinalizeProfileNodesPhase extends BasePhase<PhaseContext> { LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode(); random = loopRandomValueCache.get(loopBegin); if (random == null) { - PhiNode phi = graph.addWithoutUnique(new ValuePhiNode(seed.stamp(), loopBegin)); + PhiNode phi = graph.addWithoutUnique(new ValuePhiNode(seed.stamp(NodeView.DEFAULT), loopBegin)); phi.addInput(seed); // X_{n+1} = a*X_n + c, using glibc-like constants ValueNode a = ConstantNode.forInt(1103515245, graph); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java index a385134ccb4..62bf05e689e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.ConvertNode; @@ -118,7 +119,7 @@ public final class ClassGetHubNode extends FloatingNode implements Lowerable, Ca @Override public Node canonical(CanonicalizerTool tool) { - return canonical(this, tool.getMetaAccess(), tool.getConstantReflection(), tool.allUsagesAvailable(), stamp(), clazz); + return canonical(this, tool.getMetaAccess(), tool.getConstantReflection(), tool.allUsagesAvailable(), stamp(NodeView.DEFAULT), clazz); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java index 29a508882d8..0966cf6f1de 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java @@ -45,6 +45,7 @@ import org.graalvm.compiler.nodes.CanonicalizableLocation; import org.graalvm.compiler.nodes.CompressionNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.ForeignCallNode; import org.graalvm.compiler.nodes.extended.LoadHubNode; @@ -124,7 +125,7 @@ public class HotSpotReplacementsUtil { AddressNode address = access.getAddress(); if (address instanceof OffsetAddressNode) { OffsetAddressNode offset = (OffsetAddressNode) address; - assert offset.getBase().stamp().isCompatible(read.stamp()); + assert offset.getBase().stamp(NodeView.DEFAULT).isCompatible(read.stamp(NodeView.DEFAULT)); return offset.getBase(); } } @@ -370,8 +371,8 @@ public class HotSpotReplacementsUtil { public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) { ValueNode javaObject = findReadHub(object); if (javaObject != null) { - if (javaObject.stamp() instanceof ObjectStamp) { - ObjectStamp stamp = (ObjectStamp) javaObject.stamp(); + if (javaObject.stamp(NodeView.DEFAULT) instanceof ObjectStamp) { + ObjectStamp stamp = (ObjectStamp) javaObject.stamp(NodeView.DEFAULT); HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) stamp.javaType(tool.getMetaAccess()); if (type.isArray() && !type.getComponentType().isPrimitive()) { int layout = type.layoutHelper(); @@ -437,7 +438,7 @@ public class HotSpotReplacementsUtil { public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) { TypeReference constantType = StampTool.typeReferenceOrNull(object); if (constantType != null && constantType.isExact()) { - return ConstantNode.forConstant(read.stamp(), tool.getConstantReflection().asObjectHub(constantType.getType()), tool.getMetaAccess()); + return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), tool.getConstantReflection().asObjectHub(constantType.getType()), tool.getMetaAccess()); } return read; } @@ -448,7 +449,8 @@ public class HotSpotReplacementsUtil { public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) { TypeReference constantType = StampTool.typeReferenceOrNull(object); if (constantType != null && constantType.isExact()) { - return ConstantNode.forConstant(read.stamp(), ((HotSpotMetaspaceConstant) tool.getConstantReflection().asObjectHub(constantType.getType())).compress(), tool.getMetaAccess()); + return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), ((HotSpotMetaspaceConstant) tool.getConstantReflection().asObjectHub(constantType.getType())).compress(), + tool.getMetaAccess()); } return read; } @@ -962,7 +964,7 @@ public class HotSpotReplacementsUtil { AssumptionResult<ResolvedJavaType> leafType = element.findLeafConcreteSubtype(); if (leafType != null && leafType.canRecordTo(assumptions)) { leafType.recordTo(assumptions); - return ConstantNode.forConstant(read.stamp(), tool.getConstantReflection().asObjectHub(leafType.getResult()), tool.getMetaAccess()); + return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), tool.getConstantReflection().asObjectHub(leafType.getResult()), tool.getMetaAccess()); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java index a28f4559f67..66971954ac3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -65,7 +66,7 @@ public class IdentityHashCodeNode extends FixedWithNextNode implements Canonical @Override public Node canonical(CanonicalizerTool tool) { if (object.isConstant()) { - assert object.stamp() instanceof AbstractObjectStamp; + assert object.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp; JavaConstant c = (JavaConstant) object.asConstant(); if (ImmutableCode.getValue(tool.getOptions())) { return this; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java index 29453e464ce..1ca01ebb9b0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java @@ -53,6 +53,7 @@ import org.graalvm.compiler.hotspot.replacements.TypeCheckSnippetUtils.Hints; import org.graalvm.compiler.hotspot.word.KlassPointer; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.SnippetAnchorNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -332,7 +333,7 @@ public class InstanceOfSnippets implements Snippets { } else if (replacer.instanceOf instanceof ClassIsAssignableFromNode) { ClassIsAssignableFromNode isAssignable = (ClassIsAssignableFromNode) replacer.instanceOf; Arguments args = new Arguments(isAssignableFrom, isAssignable.graph().getGuardsStage(), tool.getLoweringStage()); - assert ((ObjectStamp) isAssignable.getThisClass().stamp()).nonNull(); + assert ((ObjectStamp) isAssignable.getThisClass().stamp(NodeView.DEFAULT)).nonNull(); args.add("thisClassNonNull", isAssignable.getThisClass()); args.add("otherClass", isAssignable.getOtherClass()); args.add("trueValue", replacer.trueValue); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java index 7cbcd3887f9..bdde73fd117 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.extended.LoadHubNode; @@ -83,7 +84,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic public boolean inferStamp() { if (klass instanceof LoadHubNode) { LoadHubNode hub = (LoadHubNode) klass; - Stamp hubStamp = hub.getValue().stamp(); + Stamp hubStamp = hub.getValue().stamp(NodeView.DEFAULT); if (hubStamp instanceof ObjectStamp) { ObjectStamp objectStamp = (ObjectStamp) hubStamp; ResolvedJavaType type = objectStamp.type(); @@ -108,7 +109,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic if (tool.allUsagesAvailable() && hasNoUsages()) { return null; } else { - return canonical(this, config, klass, stamp(), tool.getConstantReflection(), tool.getMetaAccess()); + return canonical(this, config, klass, stamp(NodeView.DEFAULT), tool.getConstantReflection(), tool.getMetaAccess()); } } @@ -123,7 +124,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic } if (klass instanceof LoadHubNode) { LoadHubNode hub = (LoadHubNode) klass; - Stamp hubStamp = hub.getValue().stamp(); + Stamp hubStamp = hub.getValue().stamp(NodeView.DEFAULT); if (hubStamp instanceof ObjectStamp) { ObjectStamp ostamp = (ObjectStamp) hubStamp; HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) ostamp.type(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java index 83a6060d95d..40fe62ea766 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java @@ -99,6 +99,7 @@ import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.InvokeNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -736,7 +737,7 @@ public class MonitorSnippets implements Snippets { StructuredGraph graph = monitorenterNode.graph(); checkBalancedMonitors(graph, tool); - assert ((ObjectStamp) monitorenterNode.object().stamp()).nonNull(); + assert ((ObjectStamp) monitorenterNode.object().stamp(NodeView.DEFAULT)).nonNull(); Arguments args; if (useFastLocking) { @@ -781,7 +782,7 @@ public class MonitorSnippets implements Snippets { } public static boolean isTracingEnabledForType(ValueNode object) { - ResolvedJavaType type = StampTool.typeOrNull(object.stamp()); + ResolvedJavaType type = StampTool.typeOrNull(object.stamp(NodeView.DEFAULT)); String filter = TraceMonitorsTypeFilter.getValue(object.getOptions()); if (filter == null) { return false; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java index b12cf66155f..02bf3438173 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -62,14 +63,14 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu @Override protected Stamp computeStamp(ValueNode object) { - if (getConcreteType(object.stamp()) != null) { - return AbstractPointerStamp.pointerNonNull(object.stamp()); + if (getConcreteType(object.stamp(NodeView.DEFAULT)) != null) { + return AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT)); } /* * If this call can't be intrinsified don't report a non-null stamp, otherwise the stamp * would change when this is lowered back to an invoke and we might lose a null check. */ - return AbstractPointerStamp.pointerMaybeNull(object.stamp()); + return AbstractPointerStamp.pointerMaybeNull(object.stamp(NodeView.DEFAULT)); } @Override @@ -91,16 +92,16 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu } assert snippetGraph != null : "ObjectCloneSnippets should be installed"; - assert getConcreteType(stamp()) != null; + assert getConcreteType(stamp(NodeView.DEFAULT)) != null; return lowerReplacement((StructuredGraph) snippetGraph.copy(getDebug()), tool); } assert false : "unhandled array type " + type.getComponentType().getJavaKind(); } else { Assumptions assumptions = graph().getAssumptions(); - type = getConcreteType(getObject().stamp()); + type = getConcreteType(getObject().stamp(NodeView.DEFAULT)); if (type != null) { StructuredGraph newGraph = new StructuredGraph.Builder(graph().getOptions(), graph().getDebug(), AllowAssumptions.ifNonNull(assumptions)).build(); - ParameterNode param = newGraph.addWithoutUnique(new ParameterNode(0, StampPair.createSingle(getObject().stamp()))); + ParameterNode param = newGraph.addWithoutUnique(new ParameterNode(0, StampPair.createSingle(getObject().stamp(NodeView.DEFAULT)))); NewInstanceNode newInstance = newGraph.add(new NewInstanceNode(type, true)); newGraph.addAfterFixed(newGraph.start(), newInstance); ReturnNode returnNode = newGraph.add(new ReturnNode(newInstance)); @@ -111,12 +112,12 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu newGraph.addBeforeFixed(returnNode, load); newGraph.addBeforeFixed(returnNode, newGraph.add(new StoreFieldNode(newInstance, field, load))); } - assert getConcreteType(stamp()) != null; + assert getConcreteType(stamp(NodeView.DEFAULT)) != null; return lowerReplacement(newGraph, tool); } } } - assert getConcreteType(stamp()) == null; + assert getConcreteType(stamp(NodeView.DEFAULT)) == null; return null; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java index b4cdc08ace8..9a3c4011948 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java @@ -66,6 +66,7 @@ import org.graalvm.compiler.hotspot.nodes.SerialArrayRangeWriteBarrier; import org.graalvm.compiler.hotspot.nodes.SerialWriteBarrier; import org.graalvm.compiler.hotspot.nodes.VMErrorNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.FixedValueAnchorNode; @@ -447,7 +448,7 @@ public class WriteBarrierSnippets implements Snippets { } ValueNode expected = writeBarrierPre.getExpectedObject(); - if (expected != null && expected.stamp() instanceof NarrowOopStamp) { + if (expected != null && expected.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) { assert oopEncoding != null; expected = HotSpotCompressionNode.uncompress(expected, oopEncoding); } @@ -472,7 +473,7 @@ public class WriteBarrierSnippets implements Snippets { } ValueNode expected = readBarrier.getExpectedObject(); - if (expected != null && expected.stamp() instanceof NarrowOopStamp) { + if (expected != null && expected.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) { assert oopEncoding != null; expected = HotSpotCompressionNode.uncompress(expected, oopEncoding); } @@ -503,7 +504,7 @@ public class WriteBarrierSnippets implements Snippets { } ValueNode value = writeBarrierPost.getValue(); - if (value.stamp() instanceof NarrowOopStamp) { + if (value.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) { assert oopEncoding != null; value = HotSpotCompressionNode.uncompress(value, oopEncoding); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java index 8f4e370020f..863db348004 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; @@ -143,7 +144,7 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements FixedWithNextNode basePtr = graph().add(new GetObjectAddressNode(base)); graph().addBeforeFixed(this, basePtr); Stamp wordStamp = StampFactory.forKind(runtime.getTarget().wordJavaKind); - ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph()); + ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph(), NodeView.DEFAULT); int shift = CodeUtil.log2(getArrayIndexScale(elementKind)); ValueNode scaledIndex = graph().unique(new LeftShiftNode(wordPos, ConstantNode.forInt(shift, graph()))); ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerStamp(wordStamp, getArrayBaseOffset(elementKind), graph()))); @@ -160,8 +161,8 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements ValueNode srcAddr = computeBase(getSource(), getSourcePosition()); ValueNode destAddr = computeBase(getDestination(), getDestinationPosition()); ValueNode len = getLength(); - if (len.stamp().getStackKind() != JavaKind.Long) { - len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph()); + if (len.stamp(NodeView.DEFAULT).getStackKind() != JavaKind.Long) { + len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph(), NodeView.DEFAULT); } ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len)); call.setStateAfter(stateAfter()); @@ -232,8 +233,8 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements // Can treat as disjoint disjoint = true; } - PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp().asConstant(); - PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp().asConstant(); + PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp(NodeView.DEFAULT).asConstant(); + PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp(NodeView.DEFAULT).asConstant(); if (constantSrc != null && constantDst != null) { if (!aligned) { aligned = isHeapWordAligned(constantSrc, componentKind) && isHeapWordAligned(constantDst, componentKind); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java index a8c59ade39b..216fb279e0f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java @@ -54,6 +54,7 @@ import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -90,11 +91,12 @@ public class ArrayCopySnippets implements Snippets { private enum ArrayCopyTypeCheck { UNDEFINED_ARRAY_TYPE_CHECK, - // we know that both objects are arrays and have the same type + // either we know that both objects are arrays and have the same type, + // or we apply generic array copy snippet, which enforces type check NO_ARRAY_TYPE_CHECK, // can be used when we know that one of the objects is a primitive array HUB_BASED_ARRAY_TYPE_CHECK, - // must be used when we don't have sufficient information to use one of the others + // can be used when we know that one of the objects is an object array LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK } @@ -232,18 +234,18 @@ public class ArrayCopySnippets implements Snippets { @Snippet(allowPartialIntrinsicArgumentMismatch = true) public static void genericArraycopyWithSlowPathWork(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) { - if (probability(FREQUENT_PROBABILITY, length > 0)) { - counters.genericArraycopyDifferentTypeCounter.inc(); - counters.genericArraycopyDifferentTypeCopiedCounter.add(length); - int copiedElements = GenericArrayCopyCallNode.genericArraycopy(src, srcPos, dest, destPos, length); - if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) { - /* - * the stub doesn't throw the ArrayStoreException, but returns the number of copied - * elements (xor'd with -1). - */ - copiedElements ^= -1; - System.arraycopy(src, srcPos + copiedElements, dest, destPos + copiedElements, length - copiedElements); - } + // The length > 0 check should not be placed here because generic array copy stub should + // enforce type check. This is fine performance-wise because this snippet is rarely used. + counters.genericArraycopyDifferentTypeCounter.inc(); + counters.genericArraycopyDifferentTypeCopiedCounter.add(length); + int copiedElements = GenericArrayCopyCallNode.genericArraycopy(src, srcPos, dest, destPos, length); + if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) { + /* + * the stub doesn't throw the ArrayStoreException, but returns the number of copied + * elements (xor'd with -1). + */ + copiedElements ^= -1; + System.arraycopy(src, srcPos + copiedElements, dest, destPos + copiedElements, length - copiedElements); } } @@ -275,21 +277,14 @@ public class ArrayCopySnippets implements Snippets { } else if (arrayTypeCheck == ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK) { KlassPointer srcHub = loadHub(nonNullSrc); KlassPointer destHub = loadHub(nonNullDest); - checkArrayType(srcHub); - checkArrayType(destHub); + if (probability(SLOW_PATH_PROBABILITY, readLayoutHelper(srcHub) != readLayoutHelper(destHub))) { + DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); + } } else { ReplacementsUtil.staticAssert(false, "unknown array type check"); } } - private static int checkArrayType(KlassPointer nonNullHub) { - int layoutHelper = readLayoutHelper(nonNullHub); - if (probability(SLOW_PATH_PROBABILITY, layoutHelper >= 0)) { - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - return layoutHelper; - } - static class Counters { final SnippetCounter checkSuccessCounter; final SnippetCounter checkAIOOBECounter; @@ -381,8 +376,8 @@ public class ArrayCopySnippets implements Snippets { SnippetInfo snippetInfo; ArrayCopyTypeCheck arrayTypeCheck; - ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp()); - ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp()); + ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp(NodeView.DEFAULT)); + ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp(NodeView.DEFAULT)); if (!canBeArray(srcType) || !canBeArray(destType)) { // at least one of the objects is definitely not an array - use the native call // right away as the copying will fail anyways @@ -399,7 +394,8 @@ public class ArrayCopySnippets implements Snippets { } else if (srcComponentType == null && destComponentType == null) { // we don't know anything about the types - use the generic copying snippetInfo = arraycopyGenericSnippet; - arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK; + // no need for additional type check to avoid duplicated work + arrayTypeCheck = ArrayCopyTypeCheck.NO_ARRAY_TYPE_CHECK; } else if (srcComponentType != null && destComponentType != null) { if (!srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) { // it depends on the array content if the copy succeeds - we need @@ -416,14 +412,14 @@ public class ArrayCopySnippets implements Snippets { } else { ResolvedJavaType nonNullComponentType = srcComponentType != null ? srcComponentType : destComponentType; if (nonNullComponentType.isPrimitive()) { - // one involved object is a primitive array - we can safely assume that we - // are copying primitive arrays + // one involved object is a primitive array - it is sufficient to directly + // compare the hub. snippetInfo = arraycopyExactSnippet; arrayTypeCheck = ArrayCopyTypeCheck.HUB_BASED_ARRAY_TYPE_CHECK; elementKind = nonNullComponentType.getJavaKind(); } else { - // one involved object is an object array - we can safely assume that we are - // copying object arrays that might require a store check + // one involved object is an object array - the other array's element type + // may be primitive or object, hence we compare the layout helper. snippetInfo = arraycopyCheckcastSnippet; arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK; } @@ -432,7 +428,12 @@ public class ArrayCopySnippets implements Snippets { // a few special cases that are easier to handle when all other variables already have a // value - if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) { + if (snippetInfo != arraycopyNativeSnippet && snippetInfo != arraycopyGenericSnippet && arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) { + // Copying 0 element between object arrays with conflicting types will not throw an + // exception - once we pass the preliminary element type checks that we are not + // mixing arrays of different basic types, ArrayStoreException is only thrown when + // an *astore would have thrown it. Therefore, copying null between object arrays + // with conflicting types will also succeed (we do not optimize for such case here). snippetInfo = arraycopyZeroLengthSnippet; } else if (snippetInfo == arraycopyExactSnippet && shouldUnroll(arraycopy.getLength())) { snippetInfo = arraycopyUnrolledSnippet; @@ -495,8 +496,8 @@ public class ArrayCopySnippets implements Snippets { } public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy) { - ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp()); - ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp()); + ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp(NodeView.DEFAULT)); + ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp(NodeView.DEFAULT)); if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) { return null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java index e90d486a98d..e6fb867a83e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; @@ -115,9 +116,10 @@ public final class CheckcastArrayCopyCallNode extends AbstractMemoryCheckpoint i graph().addBeforeFixed(this, basePtr); int shift = CodeUtil.log2(getArrayIndexScale(JavaKind.Object)); - ValueNode extendedPos = IntegerConvertNode.convert(pos, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph()); + ValueNode extendedPos = IntegerConvertNode.convert(pos, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT); ValueNode scaledIndex = graph().unique(new LeftShiftNode(extendedPos, ConstantNode.forInt(shift, graph()))); - ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(scaledIndex.stamp()), getArrayBaseOffset(JavaKind.Object), graph()))); + ValueNode offset = graph().unique( + new AddNode(scaledIndex, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(scaledIndex.stamp(NodeView.DEFAULT)), getArrayBaseOffset(JavaKind.Object), graph()))); return graph().unique(new OffsetAddressNode(basePtr, offset)); } @@ -129,8 +131,8 @@ public final class CheckcastArrayCopyCallNode extends AbstractMemoryCheckpoint i ValueNode srcAddr = computeBase(getSource(), getSourcePosition()); ValueNode destAddr = computeBase(getDestination(), getDestinationPosition()); ValueNode len = getLength(); - if (len.stamp().getStackKind() != runtime.getTarget().wordJavaKind) { - len = IntegerConvertNode.convert(len, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph()); + if (len.stamp(NodeView.DEFAULT).getStackKind() != runtime.getTarget().wordJavaKind) { + len = IntegerConvertNode.convert(len, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT); } ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len, superCheckOffset, destElemKlass)); call.setStateAfter(stateAfter()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java index 1017b07c2b2..70373bfe4e1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider; import org.graalvm.compiler.hotspot.nodes.GetObjectAddressNode; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.IntegerConvertNode; @@ -106,8 +107,8 @@ public final class GenericArrayCopyCallNode extends AbstractMemoryCheckpoint imp } private ValueNode wordValue(ValueNode value) { - if (value.stamp().getStackKind() != runtime.getTarget().wordJavaKind) { - return IntegerConvertNode.convert(value, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph()); + if (value.stamp(NodeView.DEFAULT).getStackKind() != runtime.getTarget().wordJavaKind) { + return IntegerConvertNode.convert(value, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT); } return value; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java index 658c3ce30fb..01dc9cdab21 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage; import org.graalvm.compiler.hotspot.meta.HotSpotProviders; import org.graalvm.compiler.java.GraphBuilderPhase; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage; @@ -120,7 +121,7 @@ public abstract class SnippetStub extends Stub implements Snippets { for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { int index = param.index(); if (method.getParameterAnnotation(NonNullParameter.class, index) != null) { - param.setStamp(param.stamp().join(StampFactory.objectNonNull())); + param.setStamp(param.stamp(NodeView.DEFAULT).join(StampFactory.objectNonNull())); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java index 866ba59bd0b..18a27d002e5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.hotspot.word.HotSpotOperation.HotspotOpcode; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -59,7 +60,7 @@ public final class PointerCastNode extends FloatingNode implements LIRLowerable, @Override public void generate(NodeLIRBuilderTool generator) { Value value = generator.operand(input); - assert value.getValueKind().equals(generator.getLIRGeneratorTool().getLIRKind(stamp())) : "PointerCastNode shouldn't change the LIRKind"; + assert value.getValueKind().equals(generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT))) : "PointerCastNode shouldn't change the LIRKind"; generator.setResult(this, value); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java index 8a8299098fe..e39464a0f6a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java @@ -330,6 +330,7 @@ import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopEndNode; import org.graalvm.compiler.nodes.LoopExitNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ReturnNode; @@ -1092,71 +1093,71 @@ public class BytecodeParser implements GraphBuilderContext { } protected ValueNode genIntegerAdd(ValueNode x, ValueNode y) { - return AddNode.create(x, y); + return AddNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genIntegerSub(ValueNode x, ValueNode y) { - return SubNode.create(x, y); + return SubNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genIntegerMul(ValueNode x, ValueNode y) { - return MulNode.create(x, y); + return MulNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genFloatAdd(ValueNode x, ValueNode y) { - return AddNode.create(x, y); + return AddNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genFloatSub(ValueNode x, ValueNode y) { - return SubNode.create(x, y); + return SubNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genFloatMul(ValueNode x, ValueNode y) { - return MulNode.create(x, y); + return MulNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genFloatDiv(ValueNode x, ValueNode y) { - return FloatDivNode.create(x, y); + return FloatDivNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genFloatRem(ValueNode x, ValueNode y) { - return new RemNode(x, y); + return RemNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genIntegerDiv(ValueNode x, ValueNode y) { - return new SignedDivNode(x, y); + return SignedDivNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genIntegerRem(ValueNode x, ValueNode y) { - return new SignedRemNode(x, y); + return SignedRemNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genNegateOp(ValueNode x) { - return NegateNode.create(x); + return NegateNode.create(x, NodeView.DEFAULT); } protected ValueNode genLeftShift(ValueNode x, ValueNode y) { - return LeftShiftNode.create(x, y); + return LeftShiftNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genRightShift(ValueNode x, ValueNode y) { - return RightShiftNode.create(x, y); + return RightShiftNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) { - return new UnsignedRightShiftNode(x, y); + return UnsignedRightShiftNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genAnd(ValueNode x, ValueNode y) { - return AndNode.create(x, y); + return AndNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genOr(ValueNode x, ValueNode y) { - return OrNode.create(x, y); + return OrNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genXor(ValueNode x, ValueNode y) { - return XorNode.create(x, y); + return XorNode.create(x, y, NodeView.DEFAULT); } protected ValueNode genNormalizeCompare(ValueNode x, ValueNode y, boolean isUnorderedLess) { @@ -1164,19 +1165,19 @@ public class BytecodeParser implements GraphBuilderContext { } protected ValueNode genFloatConvert(FloatConvert op, ValueNode input) { - return FloatConvertNode.create(op, input); + return FloatConvertNode.create(op, input, NodeView.DEFAULT); } protected ValueNode genNarrow(ValueNode input, int bitCount) { - return NarrowNode.create(input, bitCount); + return NarrowNode.create(input, bitCount, NodeView.DEFAULT); } protected ValueNode genSignExtend(ValueNode input, int bitCount) { - return SignExtendNode.create(input, bitCount); + return SignExtendNode.create(input, bitCount, NodeView.DEFAULT); } protected ValueNode genZeroExtend(ValueNode input, int bitCount) { - return ZeroExtendNode.create(input, bitCount); + return ZeroExtendNode.create(input, bitCount, NodeView.DEFAULT); } protected void genGoto() { @@ -1191,15 +1192,15 @@ public class BytecodeParser implements GraphBuilderContext { } protected LogicNode genObjectEquals(ValueNode x, ValueNode y) { - return ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y); + return ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y, NodeView.DEFAULT); } protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) { - return IntegerEqualsNode.create(constantReflection, metaAccess, options, null, x, y); + return IntegerEqualsNode.create(constantReflection, metaAccess, options, null, x, y, NodeView.DEFAULT); } protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) { - return IntegerLessThanNode.create(constantReflection, metaAccess, options, null, x, y); + return IntegerLessThanNode.create(constantReflection, metaAccess, options, null, x, y, NodeView.DEFAULT); } protected ValueNode genUnique(ValueNode x) { @@ -1219,7 +1220,7 @@ public class BytecodeParser implements GraphBuilderContext { ValueNode exception = frameState.pop(JavaKind.Object); FixedGuardNode nullCheck = append(new FixedGuardNode(graph.addOrUniqueWithInputs(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true)); - ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp().join(objectNonNull()), nullCheck)); + ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp(NodeView.DEFAULT).join(objectNonNull()), nullCheck)); lastInstr.setNext(handleException(nonNullException, bci(), false)); } @@ -1244,7 +1245,7 @@ public class BytecodeParser implements GraphBuilderContext { } protected ValueNode genConditional(ValueNode x) { - return ConditionalNode.create((LogicNode) x); + return ConditionalNode.create((LogicNode) x, NodeView.DEFAULT); } protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) { @@ -1275,7 +1276,7 @@ public class BytecodeParser implements GraphBuilderContext { } protected ValueNode emitExplicitNullCheck(ValueNode receiver) { - if (StampTool.isPointerNonNull(receiver.stamp())) { + if (StampTool.isPointerNonNull(receiver.stamp(NodeView.DEFAULT))) { return receiver; } BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class)); @@ -1293,7 +1294,7 @@ public class BytecodeParser implements GraphBuilderContext { protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) { AbstractBeginNode trueSucc = graph.add(new BeginNode()); BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index)); - append(new IfNode(genUnique(IntegerBelowNode.create(constantReflection, metaAccess, options, null, index, length)), trueSucc, exception, FAST_PATH_PROBABILITY)); + append(new IfNode(genUnique(IntegerBelowNode.create(constantReflection, metaAccess, options, null, index, length, NodeView.DEFAULT)), trueSucc, exception, FAST_PATH_PROBABILITY)); lastInstr = trueSucc; exception.setStateAfter(createFrameState(bci(), exception)); @@ -1803,7 +1804,7 @@ public class BytecodeParser implements GraphBuilderContext { this.args = args; this.resultType = resultType; this.beforeStackSize = frameState.stackSize(); - this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp()); + this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp(NodeView.DEFAULT)); this.nodeCount = graph.getNodeCount(); this.mark = graph.getMark(); } @@ -1817,7 +1818,8 @@ public class BytecodeParser implements GraphBuilderContext { int expectedStackSize = beforeStackSize + resultType.getSlotCount(); assert expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d", expectedStackSize, frameState.stackSize()); NodeIterable<Node> newNodes = graph.getNewNodes(mark); - assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]); + assert !needsNullCheck || isPointerNonNull(args[0].stamp(NodeView.DEFAULT)) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), + args[0]); for (Node n : newNodes) { if (n instanceof StateSplit) { StateSplit stateSplit = (StateSplit) n; @@ -1891,7 +1893,7 @@ public class BytecodeParser implements GraphBuilderContext { LoadHubNode hub = graph.unique(new LoadHubNode(stampProvider, nonNullReceiver)); LoadMethodNode actual = append(new LoadMethodNode(methodStamp, targetMethod, receiverType, method.getDeclaringClass(), hub)); ConstantNode expected = graph.unique(ConstantNode.forConstant(methodStamp, targetMethod.getEncoding(), getMetaAccess())); - LogicNode compare = graph.addOrUniqueWithInputs(CompareNode.createCompareNode(constantReflection, metaAccess, options, null, Condition.EQ, actual, expected)); + LogicNode compare = graph.addOrUniqueWithInputs(CompareNode.createCompareNode(constantReflection, metaAccess, options, null, Condition.EQ, actual, expected, NodeView.DEFAULT)); JavaTypeProfile profile = null; if (profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) { @@ -2394,7 +2396,7 @@ public class BytecodeParser implements GraphBuilderContext { if (kind != returnKind) { // sub-word integer assert returnKind.isNumericInteger() && returnKind.getStackKind() == JavaKind.Int; - IntegerStamp stamp = (IntegerStamp) value.stamp(); + IntegerStamp stamp = (IntegerStamp) value.stamp(NodeView.DEFAULT); // the bytecode verifier doesn't check that the value is in the correct range if (stamp.lowerBound() < returnKind.getMinValue() || returnKind.getMaxValue() < stamp.upperBound()) { @@ -2480,7 +2482,7 @@ public class BytecodeParser implements GraphBuilderContext { JsrScope scope = currentBlock.getJsrScope(); int retAddress = scope.nextReturnAddress(); ConstantNode returnBciNode = getJsrConstant(retAddress); - LogicNode guard = IntegerEqualsNode.create(constantReflection, metaAccess, options, null, local, returnBciNode); + LogicNode guard = IntegerEqualsNode.create(constantReflection, metaAccess, options, null, local, returnBciNode, NodeView.DEFAULT); guard = graph.addOrUniqueWithInputs(guard); append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile)); if (!successor.getJsrScope().equals(scope.pop())) { @@ -3184,7 +3186,7 @@ public class BytecodeParser implements GraphBuilderContext { private void genConditionalForIf(BciBlock trueBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt, boolean genReturn) { ConstantNode trueValue = graph.unique(ConstantNode.forInt(trueBlockInt)); ConstantNode falseValue = graph.unique(ConstantNode.forInt(falseBlockInt)); - ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue); + ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue, NodeView.DEFAULT); if (conditionalNode.graph() == null) { conditionalNode = graph.addOrUniqueWithInputs(conditionalNode); } @@ -3716,7 +3718,7 @@ public class BytecodeParser implements GraphBuilderContext { } } - boolean nonNull = ((ObjectStamp) object.stamp()).nonNull(); + boolean nonNull = ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull(); if (castNode == null) { LogicNode condition = genUnique(createInstanceOfAllowNull(checkedType, object, null)); if (condition.isTautology()) { @@ -4174,7 +4176,8 @@ public class BytecodeParser implements GraphBuilderContext { ValueNode value = frameState.pop(JavaKind.Int); int nofCases = bs.numberOfCases(); - double[] keyProbabilities = switchProbability(nofCases + 1, bci); + int nofCasesPlusDefault = nofCases + 1; + double[] keyProbabilities = switchProbability(nofCasesPlusDefault, bci); EconomicMap<Integer, SuccessorInfo> bciToBlockSuccessorIndex = EconomicMap.create(Equivalence.DEFAULT); for (int i = 0; i < currentBlock.getSuccessorCount(); i++) { @@ -4184,11 +4187,11 @@ public class BytecodeParser implements GraphBuilderContext { ArrayList<BciBlock> actualSuccessors = new ArrayList<>(); int[] keys = new int[nofCases]; - int[] keySuccessors = new int[nofCases + 1]; + int[] keySuccessors = new int[nofCasesPlusDefault]; int deoptSuccessorIndex = -1; int nextSuccessorIndex = 0; boolean constantValue = value.isConstant(); - for (int i = 0; i < nofCases + 1; i++) { + for (int i = 0; i < nofCasesPlusDefault; i++) { if (i < nofCases) { keys[i] = bs.keyAt(i); } @@ -4200,7 +4203,7 @@ public class BytecodeParser implements GraphBuilderContext { } keySuccessors[i] = deoptSuccessorIndex; } else { - int targetBci = i >= nofCases ? bs.defaultTarget() : bs.targetAt(i); + int targetBci = i < nofCases ? bs.targetAt(i) : bs.defaultTarget(); SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci); if (info.actualIndex < 0) { info.actualIndex = nextSuccessorIndex++; @@ -4209,6 +4212,48 @@ public class BytecodeParser implements GraphBuilderContext { keySuccessors[i] = info.actualIndex; } } + /* + * When the profile indicates a case is never taken, the above code will cause the case to + * deopt should it be subsequently encountered. However, the case may share code with + * another case that is taken according to the profile. + * + * For example: + * // @formatter:off + * switch (opcode) { + * case GOTO: + * case GOTO_W: { + * // emit goto code + * break; + * } + * } + * // @formatter:on + * + * The profile may indicate the GOTO_W case is never taken, and thus a deoptimization stub + * will be emitted. There might be optimization opportunity if additional branching based + * on opcode is within the case block. Specially, if there is only single case that + * reaches a target, we have better chance cutting out unused branches. Otherwise, + * it might be beneficial routing to the same code instead of deopting. + * + * The following code rewires deoptimization stub to existing resolved branch target if + * the target is connected by more than 1 cases. + */ + if (deoptSuccessorIndex >= 0) { + int[] connectedCases = new int[nextSuccessorIndex]; + for (int i = 0; i < nofCasesPlusDefault; i++) { + connectedCases[keySuccessors[i]]++; + } + + for (int i = 0; i < nofCasesPlusDefault; i++) { + if (keySuccessors[i] == deoptSuccessorIndex) { + int targetBci = i < nofCases ? bs.targetAt(i) : bs.defaultTarget(); + SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci); + int rewiredIndex = info.actualIndex; + if (rewiredIndex >= 0 && connectedCases[rewiredIndex] > 1) { + keySuccessors[i] = info.actualIndex; + } + } + } + } genIntegerSwitch(value, actualSuccessors, keys, keyProbabilities, keySuccessors); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java index b924c25f85c..04b2c315ed4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java @@ -55,6 +55,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; @@ -460,7 +461,7 @@ public final class FrameStateBuilder implements SideEffectsState { } private ValuePhiNode createValuePhi(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) { - ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp().unrestricted(), block)); + ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp(NodeView.DEFAULT).unrestricted(), block)); for (int i = 0; i < block.phiPredecessorCount(); i++) { phi.addInput(currentValue); } @@ -558,7 +559,7 @@ public final class FrameStateBuilder implements SideEffectsState { } assert !block.isPhiAtMerge(value) : "phi function for this block already created"; - ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(stampFromValue ? value.stamp() : value.stamp().unrestricted(), block)); + ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(stampFromValue ? value.stamp(NodeView.DEFAULT) : value.stamp(NodeView.DEFAULT).unrestricted(), block)); phi.addInput(value); return phi; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java index a69bf2d8059..25d05b83774 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java @@ -23,7 +23,6 @@ package org.graalvm.compiler.jtt.jdk; import org.graalvm.compiler.jtt.JTTTest; -import org.junit.Assume; import org.junit.Test; public class Unsafe_compareAndSwapNullCheck extends JTTTest { @@ -48,8 +47,6 @@ public class Unsafe_compareAndSwapNullCheck extends JTTTest { @Test public void run0() throws Throwable { - // GR-2921: Unsafe_compareAndSwapNullCheck test crashes on jdk9 - Assume.assumeTrue(Java8OrEarlier); runTest(getInitialOptions(), EMPTY, false, true, "test", null, 1L, 2L); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java index 38fdd263a6c..7b9add5daaf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java @@ -299,16 +299,23 @@ public class AMD64Move { @Def({REG}) protected AllocatableValue result; @Use({COMPOSITE, UNINITIALIZED}) protected AMD64AddressValue address; + private final OperandSize size; - public LeaOp(AllocatableValue result, AMD64AddressValue address) { + public LeaOp(AllocatableValue result, AMD64AddressValue address, OperandSize size) { super(TYPE); this.result = result; this.address = address; + this.size = size; } @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - masm.leaq(asRegister(result, AMD64Kind.QWORD), address.toAddress()); + if (size == OperandSize.QWORD) { + masm.leaq(asRegister(result, AMD64Kind.QWORD), address.toAddress()); + } else { + assert size == OperandSize.DWORD; + masm.lead(asRegister(result, AMD64Kind.DWORD), address.toAddress()); + } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java index b3f3bdd9440..5e4a5c513cd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java @@ -340,7 +340,7 @@ abstract class LIRIntrospection<T> extends FieldIntrospection<T> { private static boolean isPrintableAsciiString(byte[] array) { for (byte b : array) { char c = (char) b; - if (c != 0 && c < 0x20 && c > 0x7F) { + if (c != 0 && (c < 0x20 || c > 0x7F)) { return false; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java index 82665f59712..cb02e6653cb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java @@ -159,97 +159,101 @@ public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase { intervalInLoop = new BitMap2D(allocator.operandSize(), allocator.numLoops()); - // iterate all blocks - for (final AbstractBlockBase<?> block : allocator.sortedBlocks()) { - try (Indent indent = debug.logAndIndent("compute local live sets for block %s", block)) { + try { + // iterate all blocks + for (final AbstractBlockBase<?> block : allocator.sortedBlocks()) { + try (Indent indent = debug.logAndIndent("compute local live sets for block %s", block)) { - final BitSet liveGen = new BitSet(liveSize); - final BitSet liveKill = new BitSet(liveSize); + final BitSet liveGen = new BitSet(liveSize); + final BitSet liveKill = new BitSet(liveSize); - ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); - int numInst = instructions.size(); + ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block); + int numInst = instructions.size(); - ValueConsumer useConsumer = (operand, mode, flags) -> { - if (isVariable(operand)) { - int operandNum = allocator.operandNumber(operand); - if (!liveKill.get(operandNum)) { - liveGen.set(operandNum); - if (debug.isLogEnabled()) { - debug.log("liveGen for operand %d(%s)", operandNum, operand); + ValueConsumer useConsumer = (operand, mode, flags) -> { + if (isVariable(operand)) { + int operandNum = allocator.operandNumber(operand); + if (!liveKill.get(operandNum)) { + liveGen.set(operandNum); + if (debug.isLogEnabled()) { + debug.log("liveGen for operand %d(%s)", operandNum, operand); + } + } + if (block.getLoop() != null) { + intervalInLoop.setBit(operandNum, block.getLoop().getIndex()); } } - if (block.getLoop() != null) { - intervalInLoop.setBit(operandNum, block.getLoop().getIndex()); - } - } - if (allocator.detailedAsserts) { - verifyInput(block, liveKill, operand); - } - }; - ValueConsumer stateConsumer = (operand, mode, flags) -> { - if (LinearScan.isVariableOrRegister(operand)) { - int operandNum = allocator.operandNumber(operand); - if (!liveKill.get(operandNum)) { - liveGen.set(operandNum); - if (debug.isLogEnabled()) { - debug.log("liveGen in state for operand %d(%s)", operandNum, operand); + if (allocator.detailedAsserts) { + verifyInput(block, liveKill, operand); + } + }; + ValueConsumer stateConsumer = (operand, mode, flags) -> { + if (LinearScan.isVariableOrRegister(operand)) { + int operandNum = allocator.operandNumber(operand); + if (!liveKill.get(operandNum)) { + liveGen.set(operandNum); + if (debug.isLogEnabled()) { + debug.log("liveGen in state for operand %d(%s)", operandNum, operand); + } } } - } - }; - ValueConsumer defConsumer = (operand, mode, flags) -> { - if (isVariable(operand)) { - int varNum = allocator.operandNumber(operand); - liveKill.set(varNum); - if (debug.isLogEnabled()) { - debug.log("liveKill for operand %d(%s)", varNum, operand); + }; + ValueConsumer defConsumer = (operand, mode, flags) -> { + if (isVariable(operand)) { + int varNum = allocator.operandNumber(operand); + liveKill.set(varNum); + if (debug.isLogEnabled()) { + debug.log("liveKill for operand %d(%s)", varNum, operand); + } + if (block.getLoop() != null) { + intervalInLoop.setBit(varNum, block.getLoop().getIndex()); + } } - if (block.getLoop() != null) { - intervalInLoop.setBit(varNum, block.getLoop().getIndex()); + + if (allocator.detailedAsserts) { + /* + * Fixed intervals are never live at block boundaries, so they need not + * be processed in live sets. Process them only in debug mode so that + * this can be checked + */ + verifyTemp(liveKill, operand); } + }; + + // iterate all instructions of the block + for (int j = 0; j < numInst; j++) { + final LIRInstruction op = instructions.get(j); + + try (Indent indent2 = debug.logAndIndent("handle op %d: %s", op.id(), op)) { + op.visitEachInput(useConsumer); + op.visitEachAlive(useConsumer); + /* + * Add uses of live locals from interpreter's point of view for proper + * debug information generation. + */ + op.visitEachState(stateConsumer); + op.visitEachTemp(defConsumer); + op.visitEachOutput(defConsumer); + } + } // end of instruction iteration + + BlockData blockSets = allocator.getBlockData(block); + blockSets.liveGen = liveGen; + blockSets.liveKill = liveKill; + blockSets.liveIn = new BitSet(liveSize); + blockSets.liveOut = new BitSet(liveSize); + + if (debug.isLogEnabled()) { + debug.log("liveGen B%d %s", block.getId(), blockSets.liveGen); + debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill); } - if (allocator.detailedAsserts) { - /* - * Fixed intervals are never live at block boundaries, so they need not be - * processed in live sets. Process them only in debug mode so that this can - * be checked - */ - verifyTemp(liveKill, operand); - } - }; - - // iterate all instructions of the block - for (int j = 0; j < numInst; j++) { - final LIRInstruction op = instructions.get(j); - - try (Indent indent2 = debug.logAndIndent("handle op %d: %s", op.id(), op)) { - op.visitEachInput(useConsumer); - op.visitEachAlive(useConsumer); - /* - * Add uses of live locals from interpreter's point of view for proper debug - * information generation. - */ - op.visitEachState(stateConsumer); - op.visitEachTemp(defConsumer); - op.visitEachOutput(defConsumer); - } - } // end of instruction iteration - - BlockData blockSets = allocator.getBlockData(block); - blockSets.liveGen = liveGen; - blockSets.liveKill = liveKill; - blockSets.liveIn = new BitSet(liveSize); - blockSets.liveOut = new BitSet(liveSize); - - if (debug.isLogEnabled()) { - debug.log("liveGen B%d %s", block.getId(), blockSets.liveGen); - debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill); } - - } - } // end of block iteration + } // end of block iteration + } catch (OutOfMemoryError oom) { + throw new PermanentBailoutException(oom, "Out-of-memory during live set allocation of size %d", liveSize); + } } private void verifyTemp(BitSet liveKill, Value operand) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java index 0aac4b7ecf8..d6b6de653a9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java @@ -76,7 +76,7 @@ public class ConstantTree extends PrintableDominatorOptimizationProblem<Constant public List<UseEntry> getUsages() { if (usages == null) { - Collections.emptyList(); + return Collections.emptyList(); } return usages; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java index 6d27b6414dc..5c2683200e9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.ValuePhiNode; @@ -70,7 +71,7 @@ public class BasicInductionVariable extends InductionVariable { @Override public Direction direction() { - Stamp stamp = rawStride.stamp(); + Stamp stamp = rawStride.stamp(NodeView.DEFAULT); if (stamp instanceof IntegerStamp) { IntegerStamp integerStamp = (IntegerStamp) stamp; Direction dir = null; @@ -140,27 +141,27 @@ public class BasicInductionVariable extends InductionVariable { @Override public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) { - Stamp fromStamp = phi.stamp(); + Stamp fromStamp = phi.stamp(NodeView.DEFAULT); StructuredGraph graph = graph(); ValueNode stride = strideNode(); ValueNode initNode = this.initNode(); if (!fromStamp.isCompatible(stamp)) { - stride = IntegerConvertNode.convert(stride, stamp, graph()); - initNode = IntegerConvertNode.convert(initNode, stamp, graph()); + stride = IntegerConvertNode.convert(stride, stamp, graph(), NodeView.DEFAULT); + initNode = IntegerConvertNode.convert(initNode, stamp, graph(), NodeView.DEFAULT); } ValueNode maxTripCount = loop.counted().maxTripCountNode(assumePositiveTripCount); - if (!maxTripCount.stamp().isCompatible(stamp)) { - maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph()); + if (!maxTripCount.stamp(NodeView.DEFAULT).isCompatible(stamp)) { + maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph(), NodeView.DEFAULT); } return add(graph, mul(graph, stride, sub(graph, maxTripCount, ConstantNode.forIntegerStamp(stamp, 1, graph))), initNode); } @Override public ValueNode exitValueNode() { - Stamp stamp = phi.stamp(); + Stamp stamp = phi.stamp(NodeView.DEFAULT); ValueNode maxTripCount = loop.counted().maxTripCountNode(false); - if (!maxTripCount.stamp().isCompatible(stamp)) { - maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph()); + if (!maxTripCount.stamp(NodeView.DEFAULT).isCompatible(stamp)) { + maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph(), NodeView.DEFAULT); } return add(graph(), mul(graph(), strideNode(), maxTripCount), initNode()); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java index ae6bb412278..86a942eeda4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.GuardNode; import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; @@ -69,7 +70,7 @@ public class CountedLoopInfo { public ValueNode maxTripCountNode(boolean assumePositive) { StructuredGraph graph = iv.valueNode().graph(); - Stamp stamp = iv.valueNode().stamp(); + Stamp stamp = iv.valueNode().stamp(NodeView.DEFAULT); ValueNode range = sub(graph, end, iv.initNode()); ValueNode oneDirection; @@ -84,7 +85,7 @@ public class CountedLoopInfo { } // round-away-from-zero divison: (range + stride -/+ 1) / stride ValueNode denominator = range; - if (!oneDirection.stamp().equals(iv.strideNode().stamp())) { + if (!oneDirection.stamp(NodeView.DEFAULT).equals(iv.strideNode().stamp(NodeView.DEFAULT))) { ValueNode subedRanged = sub(graph, range, oneDirection); denominator = add(graph, subedRanged, iv.strideNode()); } @@ -204,7 +205,7 @@ public class CountedLoopInfo { if (overflowGuard != null) { return overflowGuard; } - IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(); + IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); StructuredGraph graph = iv.valueNode().graph(); CompareNode cond; // we use a negated guard with a < condition to achieve a >= ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph); @@ -230,6 +231,6 @@ public class CountedLoopInfo { } public IntegerStamp getStamp() { - return (IntegerStamp) iv.valueNode().stamp(); + return (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java index dc0ef63febd..79a078496a7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java @@ -23,6 +23,7 @@ package org.graalvm.compiler.loop; import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.IntegerConvertNode; @@ -49,12 +50,12 @@ public class DerivedConvertedInductionVariable extends DerivedInductionVariable @Override public ValueNode initNode() { - return IntegerConvertNode.convert(base.initNode(), stamp, graph()); + return IntegerConvertNode.convert(base.initNode(), stamp, graph(), NodeView.DEFAULT); } @Override public ValueNode strideNode() { - return IntegerConvertNode.convert(base.strideNode(), stamp, graph()); + return IntegerConvertNode.convert(base.strideNode(), stamp, graph(), NodeView.DEFAULT); } @Override @@ -84,7 +85,7 @@ public class DerivedConvertedInductionVariable extends DerivedInductionVariable @Override public ValueNode exitValueNode() { - return IntegerConvertNode.convert(base.exitValueNode(), stamp, graph()); + return IntegerConvertNode.convert(base.exitValueNode(), stamp, graph(), NodeView.DEFAULT); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java index 33d0ed1636f..64aecebe331 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java @@ -27,6 +27,7 @@ import static org.graalvm.compiler.loop.MathUtil.sub; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; @@ -90,14 +91,14 @@ public class DerivedOffsetInductionVariable extends DerivedInductionVariable { @Override public ValueNode strideNode() { if (value instanceof SubNode && base.valueNode() == value.getY()) { - return graph().addOrUniqueWithInputs(NegateNode.create(base.strideNode())); + return graph().addOrUniqueWithInputs(NegateNode.create(base.strideNode(), NodeView.DEFAULT)); } return base.strideNode(); } @Override public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) { - return op(base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(offset, stamp, graph())); + return op(base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(offset, stamp, graph(), NodeView.DEFAULT)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java index 7d897981616..b85f0ea5df5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java @@ -27,6 +27,7 @@ import static org.graalvm.compiler.loop.MathUtil.mul; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.IntegerConvertNode; import org.graalvm.compiler.nodes.calc.NegateNode; @@ -45,7 +46,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable { public DerivedScaledInductionVariable(LoopEx loop, InductionVariable base, NegateNode value) { super(loop, base); - this.scale = ConstantNode.forIntegerStamp(value.stamp(), -1, value.graph()); + this.scale = ConstantNode.forIntegerStamp(value.stamp(NodeView.DEFAULT), -1, value.graph()); this.value = value; } @@ -60,7 +61,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable { @Override public Direction direction() { - Stamp stamp = scale.stamp(); + Stamp stamp = scale.stamp(NodeView.DEFAULT); if (stamp instanceof IntegerStamp) { IntegerStamp integerStamp = (IntegerStamp) stamp; if (integerStamp.isStrictlyPositive()) { @@ -104,7 +105,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable { @Override public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) { - return mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph())); + return mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph(), NodeView.DEFAULT)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java index 2384ad832ca..d4edf5b69d3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.loop; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -93,7 +94,7 @@ public abstract class InductionVariable { * {@link CountedLoopInfo#isExactTripCount()} returns false for the containing loop. */ public ValueNode extremumNode() { - return extremumNode(false, valueNode().stamp()); + return extremumNode(false, valueNode().stamp(NodeView.DEFAULT)); } public abstract ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java index 79158e4d672..b0a2931db66 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.nodes.FullInfopointNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoopBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -188,7 +189,7 @@ public class LoopEx { if (!binary.isAssociative()) { continue; } - ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY()); + ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY(), NodeView.DEFAULT); if (result != binary) { if (!result.isAlive()) { assert !result.isDeleted(); @@ -259,8 +260,8 @@ public class LoopEx { if (!iv.isConstantStride() || Math.abs(iv.constantStride()) != 1) { return false; } - IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp(); - IntegerStamp limitStamp = (IntegerStamp) limit.stamp(); + IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp(NodeView.DEFAULT); + IntegerStamp limitStamp = (IntegerStamp) limit.stamp(NodeView.DEFAULT); if (iv.direction() == Direction.Up) { if (initStamp.upperBound() > limitStamp.lowerBound()) { return false; @@ -392,12 +393,12 @@ public class LoopEx { } else { boolean isValidConvert = op instanceof PiNode || op instanceof SignExtendNode; if (!isValidConvert && op instanceof ZeroExtendNode) { - IntegerStamp inputStamp = (IntegerStamp) ((ZeroExtendNode) op).getValue().stamp(); + IntegerStamp inputStamp = (IntegerStamp) ((ZeroExtendNode) op).getValue().stamp(NodeView.DEFAULT); isValidConvert = inputStamp.isPositive(); } if (isValidConvert) { - iv = new DerivedConvertedInductionVariable(loop, baseIv, op.stamp(), op); + iv = new DerivedConvertedInductionVariable(loop, baseIv, op.stamp(NodeView.DEFAULT), op); } } @@ -411,7 +412,7 @@ public class LoopEx { } private static ValueNode addSub(LoopEx loop, ValueNode op, ValueNode base) { - if (op.stamp() instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) { + if (op.stamp(NodeView.DEFAULT) instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) { BinaryArithmeticNode<?> aritOp = (BinaryArithmeticNode<?>) op; if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) { return aritOp.getY(); @@ -434,7 +435,7 @@ public class LoopEx { if (op instanceof LeftShiftNode) { LeftShiftNode shift = (LeftShiftNode) op; if (shift.getX() == base && shift.getY().isConstant()) { - return ConstantNode.forIntegerStamp(base.stamp(), 1 << shift.getY().asJavaConstant().asInt(), base.graph()); + return ConstantNode.forIntegerStamp(base.stamp(NodeView.DEFAULT), 1 << shift.getY().asJavaConstant().asInt(), base.graph()); } } return null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java index 1a778623b34..9323ec4bb9f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.GuardProxyNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LoopExitNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -460,7 +461,7 @@ public abstract class LoopFragment { if (newVpn != null) { PhiNode phi; if (vpn instanceof ValueProxyNode) { - phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(), merge)); + phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(NodeView.DEFAULT), merge)); } else if (vpn instanceof GuardProxyNode) { phi = graph.addWithoutUnique(new GuardPhiNode(merge)); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java index a13ea7bbf08..8ae334b11d5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java @@ -48,6 +48,7 @@ import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopEndNode; import org.graalvm.compiler.nodes.LoopExitNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.SafepointNode; @@ -213,11 +214,11 @@ public class LoopFragmentInside extends LoopFragment { } long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor; if (iv.direction() == InductionVariable.Direction.Up) { - ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * originalStride)); + ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * originalStride)); ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal)); compareNode.replaceFirstInput(compareBound, newLimit); } else if (iv.direction() == InductionVariable.Direction.Down) { - ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -originalStride)); + ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * -originalStride)); ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal)); compareNode.replaceFirstInput(compareBound, newLimit); } @@ -391,7 +392,7 @@ public class LoopFragmentInside extends LoopFragment { private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) { PhiNode ret; if (phi instanceof ValuePhiNode) { - ret = new ValuePhiNode(phi.stamp(), merge); + ret = new ValuePhiNode(phi.stamp(NodeView.DEFAULT), merge); } else if (phi instanceof GuardPhiNode) { ret = new GuardPhiNode(merge); } else if (phi instanceof MemoryPhiNode) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java index 9c4333edd40..6a20d17faae 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java @@ -24,9 +24,11 @@ package org.graalvm.compiler.loop; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; +import org.graalvm.compiler.nodes.calc.FixedBinaryNode; import org.graalvm.compiler.nodes.calc.SignedDivNode; /** @@ -34,11 +36,11 @@ import org.graalvm.compiler.nodes.calc.SignedDivNode; */ public class MathUtil { private static boolean isConstantOne(ValueNode v1) { - return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 1; + return v1.isConstant() && v1.stamp(NodeView.DEFAULT) instanceof IntegerStamp && v1.asJavaConstant().asLong() == 1; } private static boolean isConstantZero(ValueNode v1) { - return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 0; + return v1.isConstant() && v1.stamp(NodeView.DEFAULT) instanceof IntegerStamp && v1.asJavaConstant().asLong() == 0; } public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) { @@ -48,7 +50,7 @@ public class MathUtil { if (isConstantZero(v2)) { return v1; } - return BinaryArithmeticNode.add(graph, v1, v2); + return BinaryArithmeticNode.add(graph, v1, v2, NodeView.DEFAULT); } public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2) { @@ -58,22 +60,24 @@ public class MathUtil { if (isConstantOne(v2)) { return v1; } - return BinaryArithmeticNode.mul(graph, v1, v2); + return BinaryArithmeticNode.mul(graph, v1, v2, NodeView.DEFAULT); } public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2) { if (isConstantZero(v2)) { return v1; } - return BinaryArithmeticNode.sub(graph, v1, v2); + return BinaryArithmeticNode.sub(graph, v1, v2, NodeView.DEFAULT); } public static ValueNode divBefore(StructuredGraph graph, FixedNode before, ValueNode dividend, ValueNode divisor) { if (isConstantOne(divisor)) { return dividend; } - SignedDivNode div = graph.add(new SignedDivNode(dividend, divisor)); - graph.addBeforeFixed(before, div); + ValueNode div = graph.addOrUniqueWithInputs(SignedDivNode.create(dividend, divisor, NodeView.DEFAULT)); + if (div instanceof FixedBinaryNode) { + graph.addBeforeFixed(before, (FixedBinaryNode) div); + } return div; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java index 1d21ccbcab0..f1ee5d20db8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java @@ -103,7 +103,6 @@ public class GraphNodeProcessor extends AbstractProcessor { private void reportException(Kind kind, Element element, Throwable t) { StringWriter buf = new StringWriter(); t.printStackTrace(new PrintWriter(buf)); - buf.toString(); message(kind, element, "Exception thrown during processing: %s", buf.toString()); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java index 04832ecd1a3..756661318ff 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.test.GraphTest; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.options.OptionValues; @@ -67,52 +68,52 @@ public class IntegerStampTest extends GraphTest { @Test public void testBooleanConstant() { - assertEquals(IntegerStamp.create(32, 1, 1, 0x1, 0x1), ConstantNode.forBoolean(true, graph).stamp()); - assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forBoolean(false, graph).stamp()); + assertEquals(IntegerStamp.create(32, 1, 1, 0x1, 0x1), ConstantNode.forBoolean(true, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forBoolean(false, graph).stamp(NodeView.DEFAULT)); } @Test public void testByteConstant() { - assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forByte((byte) 0, graph).stamp()); - assertEquals(IntegerStamp.create(32, 16, 16, 0x10, 0x10), ConstantNode.forByte((byte) 16, graph).stamp()); - assertEquals(IntegerStamp.create(32, -16, -16, 0xfffffff0L, 0xfffffff0L), ConstantNode.forByte((byte) -16, graph).stamp()); - assertEquals(IntegerStamp.create(32, 127, 127, 0x7f, 0x7f), ConstantNode.forByte((byte) 127, graph).stamp()); - assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forByte((byte) -128, graph).stamp()); + assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forByte((byte) 0, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 16, 16, 0x10, 0x10), ConstantNode.forByte((byte) 16, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, -16, -16, 0xfffffff0L, 0xfffffff0L), ConstantNode.forByte((byte) -16, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 127, 127, 0x7f, 0x7f), ConstantNode.forByte((byte) 127, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forByte((byte) -128, graph).stamp(NodeView.DEFAULT)); } @Test public void testShortConstant() { - assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forShort((short) 0, graph).stamp()); - assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forShort((short) 128, graph).stamp()); - assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forShort((short) -128, graph).stamp()); - assertEquals(IntegerStamp.create(32, 32767, 32767, 0x7fff, 0x7fff), ConstantNode.forShort((short) 32767, graph).stamp()); - assertEquals(IntegerStamp.create(32, -32768, -32768, 0xffff8000L, 0xffff8000L), ConstantNode.forShort((short) -32768, graph).stamp()); + assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forShort((short) 0, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forShort((short) 128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forShort((short) -128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 32767, 32767, 0x7fff, 0x7fff), ConstantNode.forShort((short) 32767, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, -32768, -32768, 0xffff8000L, 0xffff8000L), ConstantNode.forShort((short) -32768, graph).stamp(NodeView.DEFAULT)); } @Test public void testCharConstant() { - assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forChar((char) 0, graph).stamp()); - assertEquals(IntegerStamp.create(32, 'A', 'A', 'A', 'A'), ConstantNode.forChar('A', graph).stamp()); - assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forChar((char) 128, graph).stamp()); - assertEquals(IntegerStamp.create(32, 65535, 65535, 0xffff, 0xffff), ConstantNode.forChar((char) 65535, graph).stamp()); + assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forChar((char) 0, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 'A', 'A', 'A', 'A'), ConstantNode.forChar('A', graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forChar((char) 128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 65535, 65535, 0xffff, 0xffff), ConstantNode.forChar((char) 65535, graph).stamp(NodeView.DEFAULT)); } @Test public void testIntConstant() { - assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forInt(0, graph).stamp()); - assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forInt(128, graph).stamp()); - assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forInt(-128, graph).stamp()); - assertEquals(IntegerStamp.create(32, Integer.MAX_VALUE, Integer.MAX_VALUE, 0x7fffffff, 0x7fffffff), ConstantNode.forInt(Integer.MAX_VALUE, graph).stamp()); - assertEquals(IntegerStamp.create(32, Integer.MIN_VALUE, Integer.MIN_VALUE, 0x80000000L, 0x80000000L), ConstantNode.forInt(Integer.MIN_VALUE, graph).stamp()); + assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forInt(0, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forInt(128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forInt(-128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, Integer.MAX_VALUE, Integer.MAX_VALUE, 0x7fffffff, 0x7fffffff), ConstantNode.forInt(Integer.MAX_VALUE, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(32, Integer.MIN_VALUE, Integer.MIN_VALUE, 0x80000000L, 0x80000000L), ConstantNode.forInt(Integer.MIN_VALUE, graph).stamp(NodeView.DEFAULT)); } @Test public void testLongConstant() { - assertEquals(IntegerStamp.create(64, 0, 0, 0x0, 0x0), ConstantNode.forLong(0, graph).stamp()); - assertEquals(IntegerStamp.create(64, 128, 128, 0x80, 0x80), ConstantNode.forLong(128, graph).stamp()); - assertEquals(IntegerStamp.create(64, -128, -128, 0xffffffffffffff80L, 0xffffffffffffff80L), ConstantNode.forLong(-128, graph).stamp()); - assertEquals(IntegerStamp.create(64, Long.MAX_VALUE, Long.MAX_VALUE, 0x7fffffffffffffffL, 0x7fffffffffffffffL), ConstantNode.forLong(Long.MAX_VALUE, graph).stamp()); - assertEquals(IntegerStamp.create(64, Long.MIN_VALUE, Long.MIN_VALUE, 0x8000000000000000L, 0x8000000000000000L), ConstantNode.forLong(Long.MIN_VALUE, graph).stamp()); + assertEquals(IntegerStamp.create(64, 0, 0, 0x0, 0x0), ConstantNode.forLong(0, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(64, 128, 128, 0x80, 0x80), ConstantNode.forLong(128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(64, -128, -128, 0xffffffffffffff80L, 0xffffffffffffff80L), ConstantNode.forLong(-128, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(64, Long.MAX_VALUE, Long.MAX_VALUE, 0x7fffffffffffffffL, 0x7fffffffffffffffL), ConstantNode.forLong(Long.MAX_VALUE, graph).stamp(NodeView.DEFAULT)); + assertEquals(IntegerStamp.create(64, Long.MIN_VALUE, Long.MIN_VALUE, 0x8000000000000000L, 0x8000000000000000L), ConstantNode.forLong(Long.MIN_VALUE, graph).stamp(NodeView.DEFAULT)); } @Test diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java index d5061078b07..3f7a0b75253 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable; import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.options.OptionValues; @@ -57,7 +58,7 @@ public class NegateNodeCanonicalizationTest { for (byte i : a) { ConstantNode node = ConstantNode.forByte(i, graph); JavaConstant expected = JavaConstant.forInt(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -67,7 +68,7 @@ public class NegateNodeCanonicalizationTest { for (char i : a) { ConstantNode node = ConstantNode.forChar(i, graph); JavaConstant expected = JavaConstant.forInt(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -77,7 +78,7 @@ public class NegateNodeCanonicalizationTest { for (short i : a) { ConstantNode node = ConstantNode.forShort(i, graph); JavaConstant expected = JavaConstant.forInt(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -87,7 +88,7 @@ public class NegateNodeCanonicalizationTest { for (int i : a) { ConstantNode node = ConstantNode.forInt(i, graph); JavaConstant expected = JavaConstant.forInt(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -97,7 +98,7 @@ public class NegateNodeCanonicalizationTest { for (long i : a) { ConstantNode node = ConstantNode.forLong(i, graph); JavaConstant expected = JavaConstant.forLong(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -107,7 +108,7 @@ public class NegateNodeCanonicalizationTest { for (float i : a) { ConstantNode node = ConstantNode.forFloat(i, graph); JavaConstant expected = JavaConstant.forFloat(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } @@ -117,7 +118,7 @@ public class NegateNodeCanonicalizationTest { for (double i : a) { ConstantNode node = ConstantNode.forDouble(i, graph); JavaConstant expected = JavaConstant.forDouble(-i); - assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant())); + assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant())); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java index f2575f20505..e68101e8336 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.graalvm.compiler.nodes.NodeView; +import org.graalvm.compiler.nodes.ValueNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -77,9 +79,9 @@ public class ReinterpretStampDoubleToLongTest extends ReinterpretStampTest { @Test public void run() { ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp)); - ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Long, param); + ValueNode reinterpret = ReinterpretNode.create(JavaKind.Long, param, NodeView.DEFAULT); - IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(); + IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(NodeView.DEFAULT); Assert.assertEquals(Long.SIZE, resultStamp.getBits()); for (long result : interestingLongs) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java index b24285d7a00..fb7f6115761 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.graalvm.compiler.nodes.NodeView; +import org.graalvm.compiler.nodes.ValueNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -77,10 +79,10 @@ public class ReinterpretStampFloatToIntTest extends ReinterpretStampTest { @Test public void run() { ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp)); - ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Int, param); + ValueNode reinterpret = ReinterpretNode.create(JavaKind.Int, param, NodeView.DEFAULT); reinterpret.inferStamp(); - IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(); + IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(NodeView.DEFAULT); Assert.assertEquals(Integer.SIZE, resultStamp.getBits()); for (int result : interestingInts) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java index b4fd7b48011..510680f0934 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.graalvm.compiler.nodes.NodeView; +import org.graalvm.compiler.nodes.ValueNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,10 +70,10 @@ public class ReinterpretStampIntToFloatTest extends ReinterpretStampTest { @Test public void run() { ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp)); - ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Float, param); + ValueNode reinterpret = ReinterpretNode.create(JavaKind.Float, param, NodeView.DEFAULT); reinterpret.inferStamp(); - FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(); + FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(NodeView.DEFAULT); Assert.assertEquals(Float.SIZE, resultStamp.getBits()); for (int input : interestingInts) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java index 08642f3bd14..b18389e9118 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.graalvm.compiler.nodes.NodeView; +import org.graalvm.compiler.nodes.ValueNode; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,10 +70,10 @@ public class ReinterpretStampLongToDoubleTest extends ReinterpretStampTest { @Test public void run() { ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp)); - ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Double, param); + ValueNode reinterpret = ReinterpretNode.create(JavaKind.Double, param, NodeView.DEFAULT); reinterpret.inferStamp(); - FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(); + FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(NodeView.DEFAULT); Assert.assertEquals(Double.SIZE, resultStamp.getBits()); for (long input : interestingLongs) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java index 6f9a420af59..3b7b0f0f9a1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java @@ -68,7 +68,7 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode, @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); return mkStamp(newStamp); } @@ -124,7 +124,8 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode, } ConstantNode constant = (ConstantNode) forValue; - return ConstantNode.forConstant(stamp(), convert(constant.getValue(), tool.getConstantReflection()), constant.getStableDimension(), constant.isDefaultStable(), tool.getMetaAccess()); + return ConstantNode.forConstant(stamp(NodeView.DEFAULT), convert(constant.getValue(), tool.getConstantReflection()), constant.getStableDimension(), constant.isDefaultStable(), + tool.getMetaAccess()); } else if (forValue instanceof CompressionNode) { CompressionNode other = (CompressionNode) forValue; if (op != other.op && encoding.equals(other.encoding)) { @@ -137,8 +138,8 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode, @Override public void generate(NodeLIRBuilderTool gen) { boolean nonNull; - if (value.stamp() instanceof AbstractObjectStamp) { - nonNull = StampTool.isPointerNonNull(value.stamp()); + if (value.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp) { + nonNull = StampTool.isPointerNonNull(value.stamp(NodeView.DEFAULT)); } else { // metaspace pointers are never null nonNull = true; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java index 4dd797fe32c..87ee52b0800 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java @@ -134,7 +134,7 @@ public final class ConstantNode extends FloatingNode implements LIRLowerable { @Override public void generate(NodeLIRBuilderTool gen) { - LIRKind kind = gen.getLIRGeneratorTool().getLIRKind(stamp()); + LIRKind kind = gen.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); if (onlyUsedInVirtualState()) { gen.setResult(this, new ConstantValue(kind, value)); } else { @@ -525,7 +525,7 @@ public final class ConstantNode extends FloatingNode implements LIRLowerable { @Override public String toString(Verbosity verbosity) { if (verbosity == Verbosity.Name) { - return super.toString(Verbosity.Name) + "(" + value.toValueString() + ", " + stamp().unrestricted().toString() + ")"; + return super.toString(Verbosity.Name) + "(" + value.toValueString() + ", " + stamp(NodeView.DEFAULT).unrestricted().toString() + ")"; } else { return super.toString(verbosity); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java index 95d3d17beae..838a11c3dda 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java @@ -44,7 +44,7 @@ public final class EntryProxyNode extends FloatingNode implements ValueProxy { @Input ValueNode value; public EntryProxyNode(ValueNode value, EntryMarkerNode proxyPoint) { - super(TYPE, value.stamp().unrestricted()); + super(TYPE, value.stamp(NodeView.DEFAULT).unrestricted()); this.value = value; this.proxyPoint = proxyPoint; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java index 1f3f2629e48..9b08cbaffaf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java @@ -310,7 +310,7 @@ public class GraphDecoder { @Input(InputType.Unchecked) Node proxyPoint; public ProxyPlaceholder(ValueNode value, MergeNode proxyPoint) { - super(TYPE, value.stamp()); + super(TYPE, value.stamp(NodeView.DEFAULT)); this.value = value; this.proxyPoint = proxyPoint; } @@ -868,7 +868,7 @@ public class GraphDecoder { /* Now we have two different values, so we need to create a phi node. */ PhiNode phi; if (proxy instanceof ValueProxyNode) { - phi = graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(), merge)); + phi = graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(NodeView.DEFAULT), merge)); } else if (proxy instanceof GuardProxyNode) { phi = graph.addWithoutUnique(new GuardPhiNode(merge)); } else { @@ -1630,7 +1630,7 @@ class LoopDetector implements Runnable { List<PhiNode> loopBeginPhis = new ArrayList<>(mergePhis.size()); for (int i = 0; i < mergePhis.size(); i++) { PhiNode mergePhi = mergePhis.get(i); - PhiNode loopBeginPhi = graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(), loopBegin)); + PhiNode loopBeginPhi = graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(NodeView.DEFAULT), loopBegin)); mergePhi.replaceAtUsages(loopBeginPhi); /* * The first input of the new phi function is the original phi function, for the one @@ -1793,7 +1793,7 @@ class LoopDetector implements Runnable { assert irreducibleLoopHandler.header.phis().isEmpty(); /* The new phi function for the loop variable. */ - loopVariablePhi = graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp().unrestricted(), irreducibleLoopHandler.header)); + loopVariablePhi = graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp(NodeView.DEFAULT).unrestricted(), irreducibleLoopHandler.header)); for (int i = 0; i < irreducibleLoopHandler.header.phiPredecessorCount(); i++) { loopVariablePhi.addInput(explosionHeadValue); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java index 42a2e0a277e..2f0ba9b6def 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java @@ -53,7 +53,7 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo @Input ValueNode object; public GuardedValueNode(ValueNode object, GuardingNode guard) { - super(TYPE, object.stamp(), guard); + super(TYPE, object.stamp(NodeView.DEFAULT), guard); this.object = object; } @@ -70,7 +70,7 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo @Override public boolean inferStamp() { - return updateStamp(object().stamp()); + return updateStamp(object().stamp(NodeView.DEFAULT)); } @Override @@ -84,10 +84,10 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo @Override public Node canonical(CanonicalizerTool tool) { if (getGuard() == null) { - if (stamp().equals(object().stamp())) { + if (stamp(NodeView.DEFAULT).equals(object().stamp(NodeView.DEFAULT))) { return object(); } else { - return PiNode.create(object(), stamp()); + return PiNode.create(object(), stamp(NodeView.DEFAULT)); } } return this; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java index 616b1de0ac1..ff7428f7799 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java @@ -65,7 +65,6 @@ import org.graalvm.util.EconomicMap; import org.graalvm.util.Equivalence; import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.PrimitiveConstant; @@ -238,7 +237,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL if (this.trueSuccessorProbability < probabilityB) { // Reordering of those two if statements is beneficial from the point of view of // their probabilities. - if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition())) { + if (prepareForSwap(tool, condition(), nextIf.condition())) { // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1). assert intermediateBegin.next() == nextIf; AbstractBeginNode bothFalseBegin = nextIf.falseSuccessor(); @@ -267,19 +266,19 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } } - private boolean isUnboxedFrom(MetaAccessProvider meta, ValueNode x, ValueNode src) { + private boolean isUnboxedFrom(MetaAccessProvider meta, NodeView view, ValueNode x, ValueNode src) { if (x == src) { return true; } else if (x instanceof UnboxNode) { - return isUnboxedFrom(meta, ((UnboxNode) x).getValue(), src); + return isUnboxedFrom(meta, view, ((UnboxNode) x).getValue(), src); } else if (x instanceof PiNode) { PiNode pi = (PiNode) x; - return isUnboxedFrom(meta, pi.getOriginalNode(), src); + return isUnboxedFrom(meta, view, pi.getOriginalNode(), src); } else if (x instanceof LoadFieldNode) { LoadFieldNode load = (LoadFieldNode) x; ResolvedJavaType integerType = meta.lookupJavaType(Integer.class); - if (load.getValue().stamp().javaType(meta).equals(integerType)) { - return isUnboxedFrom(meta, load.getValue(), src); + if (load.getValue().stamp(view).javaType(meta).equals(integerType)) { + return isUnboxedFrom(meta, view, load.getValue(), src); } else { return false; } @@ -321,7 +320,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL ResolvedJavaType integerType = meta.lookupJavaType(Integer.class); // At least one argument for reference equal must be a boxed primitive. - if (!x.stamp().javaType(meta).equals(integerType) && !y.stamp().javaType(meta).equals(integerType)) { + NodeView view = NodeView.from(tool); + if (!x.stamp(view).javaType(meta).equals(integerType) && !y.stamp(view).javaType(meta).equals(integerType)) { return false; } @@ -366,7 +366,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL continue; } IntegerEqualsNode equals = (IntegerEqualsNode) fixed.condition(); - if ((isUnboxedFrom(meta, equals.getX(), x) && isUnboxedFrom(meta, equals.getY(), y)) || (isUnboxedFrom(meta, equals.getX(), y) && isUnboxedFrom(meta, equals.getY(), x))) { + if ((isUnboxedFrom(meta, view, equals.getX(), x) && isUnboxedFrom(meta, view, equals.getY(), y)) || + (isUnboxedFrom(meta, view, equals.getX(), y) && isUnboxedFrom(meta, view, equals.getY(), x))) { unboxCheck = fixed; } } @@ -406,7 +407,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL ValueNode falseValue = phi.valueAt(falseEnd); ValueNode trueValue = phi.valueAt(trueEnd); - ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp()); + NodeView view = NodeView.from(tool); + ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp(view), view); if (result != null) { /* * canonicalizeConditional returns possibly new nodes so add them to the graph. @@ -477,8 +479,9 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL private boolean checkForUnsignedCompare(SimplifierTool tool) { assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); if (condition() instanceof IntegerLessThanNode) { + NodeView view = NodeView.from(tool); IntegerLessThanNode lessThan = (IntegerLessThanNode) condition(); - Constant y = lessThan.getY().stamp().asConstant(); + Constant y = lessThan.getY().stamp(view).asConstant(); if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() == 0 && falseSuccessor().next() instanceof IfNode) { IfNode ifNode2 = (IfNode) falseSuccessor().next(); if (ifNode2.condition() instanceof IntegerLessThanNode) { @@ -490,7 +493,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL * Convert x >= 0 && x < positive which is represented as !(x < 0) && x < * <positive> into an unsigned compare. */ - if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.getY().stamp()).isPositive() && + if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp(view) instanceof IntegerStamp && + ((IntegerStamp) lessThan2.getY().stamp(view)).isPositive() && sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) { below = graph().unique(new IntegerBelowNode(lessThan2.getX(), lessThan2.getY())); // swap direction @@ -506,7 +510,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL */ JavaConstant positive = lessThan2.getX().asJavaConstant(); if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getJavaKind().getMaxValue()) { - ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(), positive.asLong() + 1, graph()); + ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(view), positive.asLong() + 1, graph()); below = graph().unique(new IntegerBelowNode(lessThan.getX(), newLimit)); } } @@ -574,7 +578,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL return false; } - private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b) { + private static boolean prepareForSwap(SimplifierTool tool, LogicNode a, LogicNode b) { DebugContext debug = a.getDebug(); if (a instanceof InstanceOfNode) { InstanceOfNode instanceOfA = (InstanceOfNode) a; @@ -625,13 +629,13 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) { boolean canSwap = false; - if ((compareA.getX() == compareB.getX() && valuesDistinct(constantReflection, compareA.getY(), compareB.getY()))) { + if ((compareA.getX() == compareB.getX() && valuesDistinct(tool, compareA.getY(), compareB.getY()))) { canSwap = true; - } else if ((compareA.getX() == compareB.getY() && valuesDistinct(constantReflection, compareA.getY(), compareB.getX()))) { + } else if ((compareA.getX() == compareB.getY() && valuesDistinct(tool, compareA.getY(), compareB.getX()))) { canSwap = true; - } else if ((compareA.getY() == compareB.getX() && valuesDistinct(constantReflection, compareA.getX(), compareB.getY()))) { + } else if ((compareA.getY() == compareB.getX() && valuesDistinct(tool, compareA.getX(), compareB.getY()))) { canSwap = true; - } else if ((compareA.getY() == compareB.getY() && valuesDistinct(constantReflection, compareA.getX(), compareB.getX()))) { + } else if ((compareA.getY() == compareB.getY() && valuesDistinct(tool, compareA.getX(), compareB.getX()))) { canSwap = true; } @@ -646,16 +650,17 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL return false; } - private static boolean valuesDistinct(ConstantReflectionProvider constantReflection, ValueNode a, ValueNode b) { + private static boolean valuesDistinct(SimplifierTool tool, ValueNode a, ValueNode b) { if (a.isConstant() && b.isConstant()) { - Boolean equal = constantReflection.constantEquals(a.asConstant(), b.asConstant()); + Boolean equal = tool.getConstantReflection().constantEquals(a.asConstant(), b.asConstant()); if (equal != null) { return !equal.booleanValue(); } } - Stamp stampA = a.stamp(); - Stamp stampB = b.stamp(); + NodeView view = NodeView.from(tool); + Stamp stampA = a.stamp(view); + Stamp stampB = b.stamp(view); return stampA.alwaysDistinct(stampB); } @@ -691,7 +696,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } else if (distinct == 1) { ValueNode trueValue = singlePhi.valueAt(trueEnd); ValueNode falseValue = singlePhi.valueAt(falseEnd); - ValueNode conditional = canonicalizeConditionalCascade(trueValue, falseValue); + ValueNode conditional = canonicalizeConditionalCascade(tool, trueValue, falseValue); if (conditional != null) { singlePhi.setValueAt(trueEnd, conditional); removeThroughFalseBranch(tool, merge); @@ -710,7 +715,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL if (trueValue == falseValue) { value = trueValue; } else { - value = canonicalizeConditionalCascade(trueValue, falseValue); + value = canonicalizeConditionalCascade(tool, trueValue, falseValue); if (value == null) { return false; } @@ -745,7 +750,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } } - private ValueNode canonicalizeConditionalCascade(ValueNode trueValue, ValueNode falseValue) { + private ValueNode canonicalizeConditionalCascade(SimplifierTool tool, ValueNode trueValue, ValueNode falseValue) { if (trueValue.getStackKind() != falseValue.getStackKind()) { return null; } @@ -796,8 +801,9 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL } if (lessThan != null) { assert equals != null; + NodeView view = NodeView.from(tool); if ((lessThan.getX() == equals.getX() && lessThan.getY() == equals.getY()) || (lessThan.getX() == equals.getY() && lessThan.getY() == equals.getX())) { - return graph().unique(new NormalizeCompareNode(lessThan.getX(), lessThan.getY(), conditional.trueValue().stamp().getStackKind(), false)); + return graph().unique(new NormalizeCompareNode(lessThan.getX(), lessThan.getY(), conditional.trueValue().stamp(view).getStackKind(), false)); } } } @@ -1287,10 +1293,11 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL GraphUtil.killCFG(end); } else { // Need a new phi in case the frame state is used by more than the merge being - // removed + // removed. + NodeView view = NodeView.from(tool); AbstractMergeNode newMerge = graph().add(new MergeNode()); PhiNode oldPhi = (PhiNode) oldMerge.usages().first(); - PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(), newMerge)); + PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(view), newMerge)); for (EndNode end : ends) { newPhi.addInput(phiValues.get(end)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java index cf475f2f7bb..0c7da53f3e4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java @@ -342,7 +342,7 @@ public final class LoopBeginNode extends AbstractMergeNode implements IterableNo for (int i = 0; i < phi.valueCount(); i++) { ValueNode input = phi.valueAt(i); long increment = NO_INCREMENT; - if (input != null && input instanceof AddNode && input.stamp() instanceof IntegerStamp) { + if (input != null && input instanceof AddNode && input.stamp(NodeView.DEFAULT) instanceof IntegerStamp) { AddNode add = (AddNode) input; if (add.getX() == phi && add.getY().isConstant()) { increment = add.getY().asJavaConstant().asLong(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java new file mode 100644 index 00000000000..636f2af87c4 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017, 2017, 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. + */ +package org.graalvm.compiler.nodes; + +import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.graph.spi.CanonicalizerTool; + +/** + * Interface that overrides properties of a node, such as the node's stamp. + * + * This interface allows richer canonicalizations when the current compilation context can provide a + * narrower stamp than the one stored in the node itself. One such example is performing + * canonicalization late in the compilation, when the nodes are already scheduled, and benefit from + * additional stamp information from conditional checks in branches. + * + * For example, in the following code, <code>offset + i</code> can be canonicalized once it is + * scheduled into the branch: + * + * <pre> + * public void update(int offset, int i) { + * if (i == 0) { + * array[offset + i]; + * } + * } + * </pre> + */ +public interface NodeView { + + NodeView DEFAULT = new Default(); + + class Default implements NodeView { + @Override + public Stamp stamp(ValueNode node) { + return node.stamp; + } + } + + /** + * Return a view-specific stamp of the node. + * + * This stamp must be more specific than the default stamp. + */ + Stamp stamp(ValueNode node); + + static NodeView from(CanonicalizerTool tool) { + if (tool instanceof NodeView) { + return (NodeView) tool; + } + return DEFAULT; + } +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java index 2d517d17ba5..410b82d6b00 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java @@ -152,7 +152,7 @@ public abstract class PhiNode extends FloatingNode implements Canonicalizable { public void addInput(ValueNode x) { assert !(x instanceof ValuePhiNode) || ((ValuePhiNode) x).merge() instanceof LoopBeginNode || ((ValuePhiNode) x).merge() != this.merge(); - assert !(this instanceof ValuePhiNode) || x.stamp().isCompatible(stamp()); + assert !(this instanceof ValuePhiNode) || x.stamp(NodeView.DEFAULT).isCompatible(stamp(NodeView.DEFAULT)); values().add(x); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java index 2b0207c53b5..6fd619b78a5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java @@ -76,7 +76,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual super(c, stamp, guard); this.object = object; this.piStamp = stamp; - assert piStamp.isCompatible(object.stamp()) : "Object stamp not compatible to piStamp"; + assert piStamp.isCompatible(object.stamp(NodeView.DEFAULT)) : "Object stamp not compatible to piStamp"; inferStamp(); } @@ -89,11 +89,12 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual } public PiNode(ValueNode object, ValueNode guard) { - this(object, AbstractPointerStamp.pointerNonNull(object.stamp()), guard); + this(object, AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT)), guard); } public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) { - this(object, StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp()))); + this(object, StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), + nonNull || StampTool.isPointerNonNull(object.stamp(NodeView.DEFAULT)))); } public static ValueNode create(ValueNode object, Stamp stamp) { @@ -113,7 +114,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual } public static ValueNode create(ValueNode object, ValueNode guard) { - Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp()); + Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT)); ValueNode value = canonical(object, stamp, (GuardingNode) guard); if (value != null) { return value; @@ -123,7 +124,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual @SuppressWarnings("unused") public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ValueNode guard) { - Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp()); + Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT)); ValueNode value = canonical(object, stamp, (GuardingNode) guard); if (value == null) { value = new PiNode(object, stamp, guard); @@ -134,7 +135,8 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual @SuppressWarnings("unused") public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) { - Stamp stamp = StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp())); + Stamp stamp = StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), + nonNull || StampTool.isPointerNonNull(object.stamp(NodeView.DEFAULT))); ValueNode value = canonical(object, stamp, null); if (value == null) { value = new PiNode(object, stamp); @@ -165,7 +167,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual } private Stamp computeStamp() { - return piStamp.improveWith(object().stamp()); + return piStamp.improveWith(object().stamp(NodeView.DEFAULT)); } @Override @@ -181,10 +183,10 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual public static ValueNode canonical(ValueNode object, Stamp stamp, GuardingNode guard) { // Use most up to date stamp. - Stamp computedStamp = stamp.improveWith(object.stamp()); + Stamp computedStamp = stamp.improveWith(object.stamp(NodeView.DEFAULT)); // The pi node does not give any additional information => skip it. - if (computedStamp.equals(object.stamp())) { + if (computedStamp.equals(object.stamp(NodeView.DEFAULT))) { return object; } @@ -192,14 +194,14 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual // Try to merge the pi node with a load node. if (object instanceof ReadNode) { ReadNode readNode = (ReadNode) object; - readNode.setStamp(readNode.stamp().improveWith(stamp)); + readNode.setStamp(readNode.stamp(NodeView.DEFAULT).improveWith(stamp)); return readNode; } } else { for (Node n : guard.asNode().usages()) { if (n instanceof PiNode) { PiNode otherPi = (PiNode) n; - if (object == otherPi.object() && computedStamp.equals(otherPi.stamp())) { + if (object == otherPi.object() && computedStamp.equals(otherPi.stamp(NodeView.DEFAULT))) { /* * Two PiNodes with the same guard and same result, so return the one with * the more precise piStamp. @@ -217,7 +219,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual @Override public Node canonical(CanonicalizerTool tool) { - Node value = canonical(object(), stamp(), getGuard()); + Node value = canonical(object(), stamp(NodeView.DEFAULT), getGuard()); if (value != null) { return value; } @@ -232,7 +234,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual public void setOriginalNode(ValueNode newNode) { this.updateUsages(object, newNode); this.object = newNode; - assert piStamp.isCompatible(object.stamp()) : "New object stamp not compatible to piStamp"; + assert piStamp.isCompatible(object.stamp(NodeView.DEFAULT)) : "New object stamp not compatible to piStamp"; } /** diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java index 2255a228221..8db681571ed 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java @@ -360,9 +360,9 @@ public final class StructuredGraph extends Graph implements JavaMethodContext { ValueNode result = returnNode.result(); if (result != null) { if (returnStamp == null) { - returnStamp = result.stamp(); + returnStamp = result.stamp(NodeView.DEFAULT); } else { - returnStamp = returnStamp.meet(result.stamp()); + returnStamp = returnStamp.meet(result.stamp(NodeView.DEFAULT)); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java index 8dae4d637b3..e2e0b575384 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java @@ -56,8 +56,8 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen this.stamp = stamp; } - public final Stamp stamp() { - return stamp; + public final Stamp stamp(NodeView view) { + return view.stamp(this); } public final void setStamp(Stamp stamp) { @@ -99,7 +99,7 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen } public final JavaKind getStackKind() { - return stamp().getStackKind(); + return stamp(NodeView.DEFAULT).getStackKind(); } /** @@ -197,9 +197,9 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen private boolean checkReplaceAtUsagesInvariants(Node other) { assert other == null || other instanceof ValueNode; - if (this.hasUsages() && !this.stamp().isEmpty() && !(other instanceof PhiNode) && other != null) { - assert ((ValueNode) other).stamp().getClass() == stamp().getClass() : "stamp have to be of same class"; - boolean morePrecise = ((ValueNode) other).stamp().join(stamp()).equals(((ValueNode) other).stamp()); + if (this.hasUsages() && !this.stamp(NodeView.DEFAULT).isEmpty() && !(other instanceof PhiNode) && other != null) { + assert ((ValueNode) other).stamp(NodeView.DEFAULT).getClass() == stamp(NodeView.DEFAULT).getClass() : "stamp have to be of same class"; + boolean morePrecise = ((ValueNode) other).stamp(NodeView.DEFAULT).join(stamp(NodeView.DEFAULT)).equals(((ValueNode) other).stamp(NodeView.DEFAULT)); assert morePrecise : "stamp can only get more precise " + toString(Verbosity.All) + " " + other.toString(Verbosity.All); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java index 8a60c072cdd..978df0e4c9c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java @@ -105,11 +105,11 @@ public class ValuePhiNode extends PhiNode implements ArrayLengthProvider { for (ValueNode input : values()) { assert input != null; if (s == null) { - s = input.stamp(); + s = input.stamp(NodeView.DEFAULT); } else { - if (!s.isCompatible(input.stamp())) { + if (!s.isCompatible(input.stamp(NodeView.DEFAULT))) { fail("Phi Input Stamps are not compatible. Phi:%s inputs:%s", this, - CollectionsUtil.mapAndJoin(values(), x -> x.toString() + ":" + x.stamp(), ", ")); + CollectionsUtil.mapAndJoin(values(), x -> x.toString() + ":" + x.stamp(NodeView.DEFAULT), ", ")); } } } @@ -118,7 +118,7 @@ public class ValuePhiNode extends PhiNode implements ArrayLengthProvider { @Override protected String valueDescription() { - return stamp().unrestricted().toString(); + return stamp(NodeView.DEFAULT).unrestricted().toString(); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java index 413e9060a0b..0e30b466866 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java @@ -41,7 +41,7 @@ public final class ValueProxyNode extends ProxyNode implements Canonicalizable, private final boolean loopPhiProxy; public ValueProxyNode(ValueNode value, LoopExitNode loopExit) { - super(TYPE, value.stamp(), loopExit); + super(TYPE, value.stamp(NodeView.DEFAULT), loopExit); this.value = value; loopPhiProxy = loopExit.loopBegin().isPhiAtMerge(value); } @@ -53,7 +53,7 @@ public final class ValueProxyNode extends ProxyNode implements Canonicalizable, @Override public boolean inferStamp() { - return updateStamp(value.stamp()); + return updateStamp(value.stamp(NodeView.DEFAULT)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java index 7c46a5ceaa6..18a3a6dd876 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -46,6 +47,23 @@ public final class AbsNode extends UnaryArithmeticNode<Abs> implements Arithmeti super(TYPE, ArithmeticOpTable::getAbs, x); } + public static ValueNode create(ValueNode value, NodeView view) { + ValueNode synonym = findSynonym(value, view); + if (synonym != null) { + return synonym; + } + return new NegateNode(value); + } + + protected static ValueNode findSynonym(ValueNode forValue, NodeView view) { + ArithmeticOpTable.UnaryOp<Abs> absOp = ArithmeticOpTable.forStamp(forValue.stamp(view)).getAbs(); + ValueNode synonym = UnaryArithmeticNode.findSynonym(forValue, absOp); + if (synonym != null) { + return synonym; + } + return null; + } + @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { ValueNode ret = super.canonical(tool, forValue); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java index 778bed510cc..dbf2fb13a9d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -51,21 +52,21 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit super(c, ArithmeticOpTable::getAdd, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp()).getAdd(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp(view)).getAdd(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } if (x.isConstant() && !y.isConstant()) { - return canonical(null, op, y, x); + return canonical(null, op, y, x, view); } else { - return canonical(null, op, x, y); + return canonical(null, op, x, y, view); } } - private static ValueNode canonical(AddNode addNode, BinaryOp<Add> op, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(AddNode addNode, BinaryOp<Add> op, ValueNode forX, ValueNode forY, NodeView view) { AddNode self = addNode; boolean associative = op.isAssociative(); if (associative) { @@ -91,16 +92,16 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit } if (associative && self != null) { // canonicalize expressions like "(a + 1) + 2" - ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY); + ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view); if (reassociated != self) { return reassociated; } } } if (forX instanceof NegateNode) { - return BinaryArithmeticNode.sub(forY, ((NegateNode) forX).getValue()); + return BinaryArithmeticNode.sub(forY, ((NegateNode) forX).getValue(), view); } else if (forY instanceof NegateNode) { - return BinaryArithmeticNode.sub(forX, ((NegateNode) forY).getValue()); + return BinaryArithmeticNode.sub(forX, ((NegateNode) forY).getValue(), view); } if (self == null) { self = (AddNode) new AddNode(forX, forY).maybeCommuteInputs(); @@ -125,7 +126,8 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit return new AddNode(forY, forX); } BinaryOp<Add> op = getOp(forX, forY); - return canonical(this, op, forX, forY); + NodeView view = NodeView.from(tool); + return canonical(this, op, forX, forY, view); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java index f97dbb7210d..e6c8231f10b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -51,14 +52,14 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab super(TYPE, ArithmeticOpTable::getAnd, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<And> op = ArithmeticOpTable.forStamp(x.stamp()).getAnd(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<And> op = ArithmeticOpTable.forStamp(x.stamp(view)).getAnd(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } @Override @@ -68,10 +69,11 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab return ret; } - return canonical(this, getOp(forX, forY), stamp(), forX, forY); + NodeView view = NodeView.from(tool); + return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view); } - private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { return forX; } @@ -96,17 +98,17 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab return new ZeroExtendNode(ext.getValue(), ext.getResultBits()); } } - IntegerStamp xStamp = (IntegerStamp) forX.stamp(); + IntegerStamp xStamp = (IntegerStamp) forX.stamp(view); if (((xStamp.upMask() | xStamp.downMask()) & ~rawY) == 0) { // No bits are set which are outside the mask, so the mask will have no effect. return forX; } } - return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY); + return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view); } if (forX instanceof NotNode && forY instanceof NotNode) { - return new NotNode(OrNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue())); + return new NotNode(OrNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue(), view)); } return self != null ? self : new AndNode(forX, forY).maybeCommuteInputs(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java index 7593daecd74..84853e52dd7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java @@ -41,6 +41,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ArithmeticOperation; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.ValuePhiNode; @@ -60,13 +61,13 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari protected final SerializableBinaryFunction<OP> getOp; protected BinaryArithmeticNode(NodeClass<? extends BinaryArithmeticNode<OP>> c, SerializableBinaryFunction<OP> getOp, ValueNode x, ValueNode y) { - super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp())).foldStamp(x.stamp(), y.stamp()), x, y); + super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT))).foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), x, y); this.getOp = getOp; } protected final BinaryOp<OP> getOp(ValueNode forX, ValueNode forY) { - ArithmeticOpTable table = ArithmeticOpTable.forStamp(forX.stamp()); - assert table.equals(ArithmeticOpTable.forStamp(forY.stamp())); + ArithmeticOpTable table = ArithmeticOpTable.forStamp(forX.stamp(NodeView.DEFAULT)); + assert table.equals(ArithmeticOpTable.forStamp(forY.stamp(NodeView.DEFAULT))); return getOp.apply(table); } @@ -81,14 +82,16 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp()); + NodeView view = NodeView.from(tool); + ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp(view), view); if (result != null) { return result; } return this; } - public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp) { + @SuppressWarnings("unused") + public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) { if (forX.isConstant() && forY.isConstant()) { Constant ret = op.foldConstant(forX.asConstant(), forY.asConstant()); if (ret != null) { @@ -100,32 +103,32 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari @Override public Stamp foldStamp(Stamp stampX, Stamp stampY) { - assert stampX.isCompatible(x.stamp()) && stampY.isCompatible(y.stamp()); + assert stampX.isCompatible(x.stamp(NodeView.DEFAULT)) && stampY.isCompatible(y.stamp(NodeView.DEFAULT)); return getArithmeticOp().foldStamp(stampX, stampY); } - public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) { - return graph.addOrUniqueWithInputs(AddNode.create(v1, v2)); + public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) { + return graph.addOrUniqueWithInputs(AddNode.create(v1, v2, view)); } - public static ValueNode add(ValueNode v1, ValueNode v2) { - return AddNode.create(v1, v2); + public static ValueNode add(ValueNode v1, ValueNode v2, NodeView view) { + return AddNode.create(v1, v2, view); } - public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2) { - return graph.addOrUniqueWithInputs(MulNode.create(v1, v2)); + public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) { + return graph.addOrUniqueWithInputs(MulNode.create(v1, v2, view)); } - public static ValueNode mul(ValueNode v1, ValueNode v2) { - return MulNode.create(v1, v2); + public static ValueNode mul(ValueNode v1, ValueNode v2, NodeView view) { + return MulNode.create(v1, v2, view); } - public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2) { - return graph.addOrUniqueWithInputs(SubNode.create(v1, v2)); + public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) { + return graph.addOrUniqueWithInputs(SubNode.create(v1, v2, view)); } - public static ValueNode sub(ValueNode v1, ValueNode v2) { - return SubNode.create(v1, v2); + public static ValueNode sub(ValueNode v1, ValueNode v2, NodeView view) { + return SubNode.create(v1, v2, view); } private enum ReassociateMatch { @@ -193,7 +196,7 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari * @param forY * @param forX */ - public static ValueNode reassociate(BinaryArithmeticNode<?> node, NodePredicate criterion, ValueNode forX, ValueNode forY) { + public static ValueNode reassociate(BinaryArithmeticNode<?> node, NodePredicate criterion, ValueNode forX, ValueNode forY, NodeView view) { assert node.getOp(forX, forY).isAssociative(); ReassociateMatch match1 = findReassociate(node, criterion); if (match1 == null) { @@ -239,21 +242,21 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari if (node instanceof AddNode || node instanceof SubNode) { ValueNode associated; if (invertM1) { - associated = BinaryArithmeticNode.sub(m2, m1); + associated = BinaryArithmeticNode.sub(m2, m1, view); } else if (invertM2) { - associated = BinaryArithmeticNode.sub(m1, m2); + associated = BinaryArithmeticNode.sub(m1, m2, view); } else { - associated = BinaryArithmeticNode.add(m1, m2); + associated = BinaryArithmeticNode.add(m1, m2, view); } if (invertA) { - return BinaryArithmeticNode.sub(associated, a); + return BinaryArithmeticNode.sub(associated, a, view); } if (aSub) { - return BinaryArithmeticNode.sub(a, associated); + return BinaryArithmeticNode.sub(a, associated, view); } - return BinaryArithmeticNode.add(a, associated); + return BinaryArithmeticNode.add(a, associated, view); } else if (node instanceof MulNode) { - return BinaryArithmeticNode.mul(a, AddNode.mul(m1, m2)); + return BinaryArithmeticNode.mul(a, AddNode.mul(m1, m2, view), view); } else if (node instanceof AndNode) { return new AndNode(a, new AndNode(m1, m2)); } else if (node instanceof OrNode) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java index 3e45aa760f1..52cf1a00fef 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java @@ -26,6 +26,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; /** @@ -73,7 +74,7 @@ public abstract class BinaryNode extends FloatingNode implements Canonicalizable @Override public boolean inferStamp() { - return updateStamp(foldStamp(getX().stamp(), getY().stamp())); + return updateStamp(foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT))); } /** diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java index 55437cd2de4..14e102dc243 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -91,7 +92,8 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical return null; } - public static LogicNode tryConstantFoldPrimitive(Condition condition, ValueNode forX, ValueNode forY, boolean unorderedIsTrue) { + @SuppressWarnings("unused") + public static LogicNode tryConstantFoldPrimitive(Condition condition, ValueNode forX, ValueNode forY, boolean unorderedIsTrue, NodeView view) { if (forX.asConstant() instanceof PrimitiveConstant && forY.asConstant() instanceof PrimitiveConstant) { return LogicConstantNode.forBoolean(condition.foldCondition((PrimitiveConstant) forX.asConstant(), (PrimitiveConstant) forY.asConstant(), unorderedIsTrue)); } @@ -110,27 +112,27 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical public abstract static class CompareOp { public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { LogicNode constantCondition = tryConstantFold(condition, forX, forY, constantReflection, unorderedIsTrue); if (constantCondition != null) { return constantCondition; } LogicNode result; if (forX.isConstant()) { - if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forX.asConstant(), forY, true, unorderedIsTrue)) != null) { + if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forX.asConstant(), forY, true, unorderedIsTrue, view)) != null) { return result; } } else if (forY.isConstant()) { - if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forY.asConstant(), forX, false, unorderedIsTrue)) != null) { + if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forY.asConstant(), forX, false, unorderedIsTrue, view)) != null) { return result; } } else if (forX instanceof ConvertNode && forY instanceof ConvertNode) { ConvertNode convertX = (ConvertNode) forX; ConvertNode convertY = (ConvertNode) forY; - if (convertX.preservesOrder(condition) && convertY.preservesOrder(condition) && convertX.getValue().stamp().isCompatible(convertY.getValue().stamp())) { + if (convertX.preservesOrder(condition) && convertY.preservesOrder(condition) && convertX.getValue().stamp(view).isCompatible(convertY.getValue().stamp(view))) { boolean supported = true; - if (convertX.getValue().stamp() instanceof IntegerStamp) { - IntegerStamp intStamp = (IntegerStamp) convertX.getValue().stamp(); + if (convertX.getValue().stamp(view) instanceof IntegerStamp) { + IntegerStamp intStamp = (IntegerStamp) convertX.getValue().stamp(view); supported = smallestCompareWidth != null && intStamp.getBits() >= smallestCompareWidth; } @@ -141,7 +143,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical // of the value. return null; } - return duplicateModified(convertX.getValue(), convertY.getValue(), unorderedIsTrue); + return duplicateModified(convertX.getValue(), convertY.getValue(), unorderedIsTrue, view); } } } @@ -149,11 +151,11 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical } protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) { + Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) { if (nonConstant instanceof ConditionalNode) { return optimizeConditional(constant, (ConditionalNode) nonConstant, constantReflection, mirrored ? condition.mirror() : condition, unorderedIsTrue); } else if (nonConstant instanceof NormalizeCompareNode) { - return optimizeNormalizeCompare(constantReflection, metaAccess, options, smallestCompareWidth, constant, (NormalizeCompareNode) nonConstant, mirrored); + return optimizeNormalizeCompare(constantReflection, metaAccess, options, smallestCompareWidth, constant, (NormalizeCompareNode) nonConstant, mirrored, view); } else if (nonConstant instanceof ConvertNode) { ConvertNode convert = (ConvertNode) nonConstant; boolean multiUsage = (convert.asNode().hasMoreThanOneUsage() && convert.getValue().hasExactlyOneUsage()); @@ -164,18 +166,18 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical } boolean supported = true; - if (convert.getValue().stamp() instanceof IntegerStamp) { - IntegerStamp intStamp = (IntegerStamp) convert.getValue().stamp(); + if (convert.getValue().stamp(view) instanceof IntegerStamp) { + IntegerStamp intStamp = (IntegerStamp) convert.getValue().stamp(view); supported = smallestCompareWidth != null && intStamp.getBits() > smallestCompareWidth; } if (supported) { - ConstantNode newConstant = canonicalConvertConstant(constantReflection, metaAccess, options, condition, convert, constant); + ConstantNode newConstant = canonicalConvertConstant(constantReflection, metaAccess, options, condition, convert, constant, view); if (newConstant != null) { if (mirrored) { - return duplicateModified(newConstant, convert.getValue(), unorderedIsTrue); + return duplicateModified(newConstant, convert.getValue(), unorderedIsTrue, view); } else { - return duplicateModified(convert.getValue(), newConstant, unorderedIsTrue); + return duplicateModified(convert.getValue(), newConstant, unorderedIsTrue, view); } } } @@ -185,7 +187,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical } private static ConstantNode canonicalConvertConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Condition condition, - ConvertNode convert, Constant constant) { + ConvertNode convert, Constant constant, NodeView view) { if (convert.preservesOrder(condition, constant, constantReflection)) { Constant reverseConverted = convert.reverse(constant, constantReflection); if (reverseConverted != null && convert.convert(reverseConverted, constantReflection).equals(constant)) { @@ -193,7 +195,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical // We always want uncompressed constants return null; } - return ConstantNode.forConstant(convert.getValue().stamp(), reverseConverted, metaAccess); + return ConstantNode.forConstant(convert.getValue().stamp(view), reverseConverted, metaAccess); } } return null; @@ -201,7 +203,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical @SuppressWarnings("unused") protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) { + Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) { throw new GraalError("NormalizeCompareNode connected to %s (%s %s %s)", this, constant, normalizeNode, mirrored); } @@ -230,71 +232,71 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical return null; } - protected abstract LogicNode duplicateModified(ValueNode newW, ValueNode newY, boolean unorderedIsTrue); + protected abstract LogicNode duplicateModified(ValueNode newW, ValueNode newY, boolean unorderedIsTrue, NodeView view); } - public static LogicNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) { - LogicNode result = createCompareNode(condition, x, y, constantReflection); + public static LogicNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) { + LogicNode result = createCompareNode(condition, x, y, constantReflection, view); return (result.graph() == null ? graph.addOrUniqueWithInputs(result) : result); } - public static LogicNode createCompareNode(Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) { + public static LogicNode createCompareNode(Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) { assert x.getStackKind() == y.getStackKind(); assert condition.isCanonical(); assert !x.getStackKind().isNumericFloat(); LogicNode comparison; if (condition == Condition.EQ) { - if (x.stamp() instanceof AbstractObjectStamp) { - comparison = ObjectEqualsNode.create(x, y, constantReflection); - } else if (x.stamp() instanceof AbstractPointerStamp) { - comparison = PointerEqualsNode.create(x, y); + if (x.stamp(view) instanceof AbstractObjectStamp) { + comparison = ObjectEqualsNode.create(x, y, constantReflection, view); + } else if (x.stamp(view) instanceof AbstractPointerStamp) { + comparison = PointerEqualsNode.create(x, y, view); } else { assert x.getStackKind().isNumericInteger(); - comparison = IntegerEqualsNode.create(x, y); + comparison = IntegerEqualsNode.create(x, y, view); } } else if (condition == Condition.LT) { assert x.getStackKind().isNumericInteger(); - comparison = IntegerLessThanNode.create(x, y); + comparison = IntegerLessThanNode.create(x, y, view); } else { assert condition == Condition.BT; assert x.getStackKind().isNumericInteger(); - comparison = IntegerBelowNode.create(x, y); + comparison = IntegerBelowNode.create(x, y, view); } return comparison; } public static LogicNode createCompareNode(StructuredGraph graph, ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Condition condition, ValueNode x, ValueNode y) { - LogicNode result = createCompareNode(constantReflection, metaAccess, options, smallestCompareWidth, condition, x, y); + Condition condition, ValueNode x, ValueNode y, NodeView view) { + LogicNode result = createCompareNode(constantReflection, metaAccess, options, smallestCompareWidth, condition, x, y, view); return (result.graph() == null ? graph.addOrUniqueWithInputs(result) : result); } public static LogicNode createCompareNode(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Condition condition, ValueNode x, ValueNode y) { + Condition condition, ValueNode x, ValueNode y, NodeView view) { assert x.getStackKind() == y.getStackKind(); assert condition.isCanonical(); assert !x.getStackKind().isNumericFloat(); LogicNode comparison; if (condition == Condition.EQ) { - if (x.stamp() instanceof AbstractObjectStamp) { + if (x.stamp(view) instanceof AbstractObjectStamp) { assert smallestCompareWidth == null; - comparison = ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y); - } else if (x.stamp() instanceof AbstractPointerStamp) { - comparison = PointerEqualsNode.create(x, y); + comparison = ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y, view); + } else if (x.stamp(view) instanceof AbstractPointerStamp) { + comparison = PointerEqualsNode.create(x, y, view); } else { assert x.getStackKind().isNumericInteger(); - comparison = IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y); + comparison = IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view); } } else if (condition == Condition.LT) { assert x.getStackKind().isNumericInteger(); - comparison = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y); + comparison = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view); } else { assert condition == Condition.BT; assert x.getStackKind().isNumericInteger(); - comparison = IntegerBelowNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y); + comparison = IntegerBelowNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view); } return comparison; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java index c06eea37c9b..bff140ebc63 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -55,8 +56,8 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab public static final NodeClass<ConditionalNode> TYPE = NodeClass.create(ConditionalNode.class); @Input(InputType.Condition) LogicNode condition; - @Input ValueNode trueValue; - @Input ValueNode falseValue; + @Input(InputType.Value) ValueNode trueValue; + @Input(InputType.Value) ValueNode falseValue; public LogicNode condition() { return condition; @@ -67,23 +68,23 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab } public ConditionalNode(LogicNode condition, ValueNode trueValue, ValueNode falseValue) { - super(TYPE, trueValue.stamp().meet(falseValue.stamp())); - assert trueValue.stamp().isCompatible(falseValue.stamp()); + super(TYPE, trueValue.stamp(NodeView.DEFAULT).meet(falseValue.stamp(NodeView.DEFAULT))); + assert trueValue.stamp(NodeView.DEFAULT).isCompatible(falseValue.stamp(NodeView.DEFAULT)); this.condition = condition; this.trueValue = trueValue; this.falseValue = falseValue; } - public static ValueNode create(LogicNode condition) { - return create(condition, ConstantNode.forInt(1, condition.graph()), ConstantNode.forInt(0, condition.graph())); + public static ValueNode create(LogicNode condition, NodeView view) { + return create(condition, ConstantNode.forInt(1, condition.graph()), ConstantNode.forInt(0, condition.graph()), view); } - public static ValueNode create(LogicNode condition, ValueNode trueValue, ValueNode falseValue) { - ValueNode synonym = findSynonym(condition, trueValue, falseValue); + public static ValueNode create(LogicNode condition, ValueNode trueValue, ValueNode falseValue, NodeView view) { + ValueNode synonym = findSynonym(condition, trueValue, falseValue, view); if (synonym != null) { return synonym; } - ValueNode result = canonicalizeConditional(condition, trueValue, falseValue, trueValue.stamp().meet(falseValue.stamp())); + ValueNode result = canonicalizeConditional(condition, trueValue, falseValue, trueValue.stamp(view).meet(falseValue.stamp(view)), view); if (result != null) { return result; } @@ -92,7 +93,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab @Override public boolean inferStamp() { - Stamp valueStamp = trueValue.stamp().meet(falseValue.stamp()); + Stamp valueStamp = trueValue.stamp(NodeView.DEFAULT).meet(falseValue.stamp(NodeView.DEFAULT)); if (condition instanceof IntegerLessThanNode) { IntegerLessThanNode lessThan = (IntegerLessThanNode) condition; if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) { @@ -130,12 +131,13 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab @Override public ValueNode canonical(CanonicalizerTool tool) { - ValueNode synonym = findSynonym(condition, trueValue(), falseValue()); + NodeView view = NodeView.from(tool); + ValueNode synonym = findSynonym(condition, trueValue(), falseValue(), view); if (synonym != null) { return synonym; } - ValueNode result = canonicalizeConditional(condition, trueValue(), falseValue(), stamp); + ValueNode result = canonicalizeConditional(condition, trueValue(), falseValue(), stamp, view); if (result != null) { return result; } @@ -143,7 +145,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab return this; } - public static ValueNode canonicalizeConditional(LogicNode condition, ValueNode trueValue, ValueNode falseValue, Stamp stamp) { + public static ValueNode canonicalizeConditional(LogicNode condition, ValueNode trueValue, ValueNode falseValue, Stamp stamp, NodeView view) { if (trueValue == falseValue) { return trueValue; } @@ -156,12 +158,12 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab } } - if (trueValue.stamp() instanceof IntegerStamp) { + if (trueValue.stamp(view) instanceof IntegerStamp) { // check if the conditional is redundant if (condition instanceof IntegerLessThanNode) { IntegerLessThanNode lessThan = (IntegerLessThanNode) condition; - IntegerStamp falseValueStamp = (IntegerStamp) falseValue.stamp(); - IntegerStamp trueValueStamp = (IntegerStamp) trueValue.stamp(); + IntegerStamp falseValueStamp = (IntegerStamp) falseValue.stamp(view); + IntegerStamp trueValueStamp = (IntegerStamp) trueValue.stamp(view); if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) { // return "x" for "x < y ? x : y" in case that we know "x <= y" if (trueValueStamp.upperBound() <= falseValueStamp.lowerBound()) { @@ -182,25 +184,25 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab long constFalseValue = falseValue.asJavaConstant().asLong(); if (condition instanceof IntegerEqualsNode) { IntegerEqualsNode equals = (IntegerEqualsNode) condition; - if (equals.getY().isConstant() && equals.getX().stamp() instanceof IntegerStamp) { - IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp(); + if (equals.getY().isConstant() && equals.getX().stamp(view) instanceof IntegerStamp) { + IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp(view); if (equalsXStamp.upMask() == 1) { long equalsY = equals.getY().asJavaConstant().asLong(); if (equalsY == 0) { if (constTrueValue == 0 && constFalseValue == 1) { // return x when: x == 0 ? 0 : 1; - return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); + return IntegerConvertNode.convertUnsigned(equals.getX(), stamp, view); } else if (constTrueValue == 1 && constFalseValue == 0) { // negate a boolean value via xor - return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); + return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(view), 1), view), stamp, view); } } else if (equalsY == 1) { if (constTrueValue == 1 && constFalseValue == 0) { // return x when: x == 1 ? 1 : 0; - return IntegerConvertNode.convertUnsigned(equals.getX(), stamp); + return IntegerConvertNode.convertUnsigned(equals.getX(), stamp, view); } else if (constTrueValue == 0 && constFalseValue == 1) { // negate a boolean value via xor - return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp); + return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(view), 1), view), stamp, view); } } } @@ -211,10 +213,10 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab // (value & 1) == 1 ? 1 : 0 IntegerTestNode integerTestNode = (IntegerTestNode) condition; if (integerTestNode.getY().isConstant()) { - assert integerTestNode.getX().stamp() instanceof IntegerStamp; + assert integerTestNode.getX().stamp(view) instanceof IntegerStamp; long testY = integerTestNode.getY().asJavaConstant().asLong(); if (testY == 1 && constTrueValue == 0 && constFalseValue == 1) { - return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY()), stamp); + return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY(), view), stamp, view); } } } @@ -231,7 +233,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab if (trueValue instanceof AddNode) { AddNode add = (AddNode) trueValue; if (add.getX() == falseValue) { - int bits = ((IntegerStamp) trueValue.stamp()).getBits(); + int bits = ((IntegerStamp) trueValue.stamp(NodeView.DEFAULT)).getBits(); ValueNode shift = new RightShiftNode(lt.getX(), ConstantNode.forIntegerBits(32, bits - 1)); ValueNode and = new AndNode(shift, add.getY()); return new AddNode(add.getX(), and); @@ -245,10 +247,10 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab return null; } - private static ValueNode findSynonym(ValueNode condition, ValueNode trueValue, ValueNode falseValue) { + private static ValueNode findSynonym(ValueNode condition, ValueNode trueValue, ValueNode falseValue, NodeView view) { if (condition instanceof LogicNegationNode) { LogicNegationNode negated = (LogicNegationNode) condition; - return ConditionalNode.create(negated.getValue(), falseValue, trueValue); + return ConditionalNode.create(negated.getValue(), falseValue, trueValue, view); } if (condition instanceof LogicConstantNode) { LogicConstantNode c = (LogicConstantNode) condition; @@ -267,6 +269,6 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab } public ConditionalNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y) { - this(createCompareNode(graph, condition, x, y, null)); + this(createCompareNode(graph, condition, x, y, null, NodeView.DEFAULT)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java index 25a82292cb4..450fa2a782e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -65,8 +66,8 @@ public final class FloatConvertNode extends UnaryArithmeticNode<FloatConvertOp> this.op = op; } - public static ValueNode create(FloatConvert op, ValueNode input) { - ValueNode synonym = findSynonym(input, ArithmeticOpTable.forStamp(input.stamp()).getFloatConvert(op)); + public static ValueNode create(FloatConvert op, ValueNode input, NodeView view) { + ValueNode synonym = findSynonym(input, ArithmeticOpTable.forStamp(input.stamp(view)).getFloatConvert(op)); if (synonym != null) { return synonym; } @@ -84,7 +85,7 @@ public final class FloatConvertNode extends UnaryArithmeticNode<FloatConvertOp> @Override public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) { - FloatConvertOp reverse = ArithmeticOpTable.forStamp(stamp()).getFloatConvert(op.reverse()); + FloatConvertOp reverse = ArithmeticOpTable.forStamp(stamp(NodeView.DEFAULT)).getFloatConvert(op.reverse()); return reverse.foldConstant(c); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java index 195241a9c2a..0f2c081fea5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -53,10 +54,10 @@ public class FloatDivNode extends BinaryArithmeticNode<Div> { assert stamp instanceof FloatStamp; } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Div> op = ArithmeticOpTable.forStamp(x.stamp()).getDiv(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Div> op = ArithmeticOpTable.forStamp(x.stamp(view)).getDiv(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java index 871973aa2b2..09b7072d31b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.options.OptionValues; @@ -50,12 +51,12 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat public FloatEqualsNode(ValueNode x, ValueNode y) { super(TYPE, Condition.EQ, false, x, y); - assert x.stamp() instanceof FloatStamp && y.stamp() instanceof FloatStamp : x.stamp() + " " + y.stamp(); - assert x.stamp().isCompatible(y.stamp()); + assert x.stamp(NodeView.DEFAULT) instanceof FloatStamp && y.stamp(NodeView.DEFAULT) instanceof FloatStamp : x.stamp(NodeView.DEFAULT) + " " + y.stamp(NodeView.DEFAULT); + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)); } - public static LogicNode create(ValueNode x, ValueNode y) { - LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false); + public static LogicNode create(ValueNode x, ValueNode y, NodeView view) { + LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false, view); if (result != null) { return result; } else { @@ -64,18 +65,18 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat } public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - ValueNode x, ValueNode y) { - LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y); + ValueNode x, ValueNode y, NodeView view) { + LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y, view); if (value != null) { return value; } - return create(x, y); + return create(x, y, view); } @Override public boolean isIdentityComparison() { - FloatStamp xStamp = (FloatStamp) x.stamp(); - FloatStamp yStamp = (FloatStamp) y.stamp(); + FloatStamp xStamp = (FloatStamp) x.stamp(NodeView.DEFAULT); + FloatStamp yStamp = (FloatStamp) y.stamp(NodeView.DEFAULT); /* * If both stamps have at most one 0.0 and it's the same 0.0 then this is an identity * comparison. FloatStamp isn't careful about tracking the presence of -0.0 so assume that @@ -87,7 +88,8 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, unorderedIsTrue, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, unorderedIsTrue, forX, forY, view); if (value != null) { return value; } @@ -98,13 +100,13 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat @Override public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { - LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY); + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { + LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); if (result != null) { return result; } - Stamp xStampGeneric = forX.stamp(); - Stamp yStampGeneric = forY.stamp(); + Stamp xStampGeneric = forX.stamp(view); + Stamp yStampGeneric = forY.stamp(view); if (xStampGeneric instanceof FloatStamp && yStampGeneric instanceof FloatStamp) { FloatStamp xStamp = (FloatStamp) xStampGeneric; FloatStamp yStamp = (FloatStamp) yStampGeneric; @@ -118,10 +120,10 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat } @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) { return new FloatEqualsNode(newX, newY); - } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) { + } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) { return new IntegerEqualsNode(newX, newY); } throw GraalError.shouldNotReachHere(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java index 2f6666bab4f..8b1885ff9bd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.options.OptionValues; @@ -49,12 +50,12 @@ public final class FloatLessThanNode extends CompareNode { public FloatLessThanNode(ValueNode x, ValueNode y, boolean unorderedIsTrue) { super(TYPE, Condition.LT, unorderedIsTrue, x, y); - assert x.stamp() instanceof FloatStamp && y.stamp() instanceof FloatStamp; - assert x.stamp().isCompatible(y.stamp()); + assert x.stamp(NodeView.DEFAULT) instanceof FloatStamp && y.stamp(NodeView.DEFAULT) instanceof FloatStamp; + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)); } - public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue) { - LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.LT, x, y, unorderedIsTrue); + public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue, NodeView view) { + LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.LT, x, y, unorderedIsTrue, view); if (result != null) { return result; } @@ -62,17 +63,18 @@ public final class FloatLessThanNode extends CompareNode { } public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - ValueNode x, ValueNode y, boolean unorderedIsTrue) { - LogicNode result = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.LT, unorderedIsTrue, x, y); + ValueNode x, ValueNode y, boolean unorderedIsTrue, NodeView view) { + LogicNode result = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.LT, unorderedIsTrue, x, y, view); if (result != null) { return result; } - return create(x, y, unorderedIsTrue); + return create(x, y, unorderedIsTrue, view); } @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.LT, unorderedIsTrue, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.LT, unorderedIsTrue, forX, forY, view); if (value != null) { return value; } @@ -83,8 +85,8 @@ public final class FloatLessThanNode extends CompareNode { @Override public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { - LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY); + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { + LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); if (result != null) { return result; } @@ -95,10 +97,10 @@ public final class FloatLessThanNode extends CompareNode { } @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + if (newX.stamp(NodeView.DEFAULT) instanceof FloatStamp && newY.stamp(NodeView.DEFAULT) instanceof FloatStamp) { return new FloatLessThanNode(newX, newY, unorderedIsTrue); - } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) { + } else if (newX.stamp(NodeView.DEFAULT) instanceof IntegerStamp && newY.stamp(NodeView.DEFAULT) instanceof IntegerStamp) { return new IntegerLessThanNode(newX, newY); } throw GraalError.shouldNotReachHere(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java index 4d870566e73..03e963d18d4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.code.CodeUtil; @@ -45,25 +46,27 @@ public final class IntegerBelowNode extends IntegerLowerThanNode { public IntegerBelowNode(ValueNode x, ValueNode y) { super(TYPE, x, y, OP); - assert x.stamp() instanceof IntegerStamp; - assert y.stamp() instanceof IntegerStamp; + assert x.stamp(NodeView.DEFAULT) instanceof IntegerStamp; + assert y.stamp(NodeView.DEFAULT) instanceof IntegerStamp; } - public static LogicNode create(ValueNode x, ValueNode y) { - return OP.create(x, y); + public static LogicNode create(ValueNode x, ValueNode y, NodeView view) { + return OP.create(x, y, view); } - public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y) { - LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y); + public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y, + NodeView view) { + LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y, view); if (value != null) { return value; } - return create(x, y); + return create(x, y, view); } @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY, view); if (value != null) { return value; } @@ -72,8 +75,8 @@ public final class IntegerBelowNode extends IntegerLowerThanNode { public static class BelowOp extends LowerOp { @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - assert newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp; + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + assert newX.stamp(NodeView.DEFAULT) instanceof IntegerStamp && newY.stamp(NodeView.DEFAULT) instanceof IntegerStamp; return new IntegerBelowNode(newX, newY); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java index 90bfdfde901..0e601f2910e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ArithmeticOperation; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -61,12 +62,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A protected IntegerConvertNode(NodeClass<? extends IntegerConvertNode<OP, REV>> c, SerializableIntegerConvertFunction<OP> getOp, SerializableIntegerConvertFunction<REV> getReverseOp, int inputBits, int resultBits, ValueNode input) { - super(c, getOp.apply(ArithmeticOpTable.forStamp(input.stamp())).foldStamp(inputBits, resultBits, input.stamp()), input); + super(c, getOp.apply(ArithmeticOpTable.forStamp(input.stamp(NodeView.DEFAULT))).foldStamp(inputBits, resultBits, input.stamp(NodeView.DEFAULT)), input); this.getOp = getOp; this.getReverseOp = getReverseOp; this.inputBits = inputBits; this.resultBits = resultBits; - assert ((PrimitiveStamp) input.stamp()).getBits() == inputBits; + assert ((PrimitiveStamp) input.stamp(NodeView.DEFAULT)).getBits() == inputBits; } public int getInputBits() { @@ -78,7 +79,7 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A } protected final IntegerConvertOp<OP> getOp(ValueNode forValue) { - return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp())); + return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT))); } @Override @@ -93,19 +94,19 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A @Override public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) { - IntegerConvertOp<REV> reverse = getReverseOp.apply(ArithmeticOpTable.forStamp(stamp())); + IntegerConvertOp<REV> reverse = getReverseOp.apply(ArithmeticOpTable.forStamp(stamp(NodeView.DEFAULT))); return reverse.foldConstant(getResultBits(), getInputBits(), c); } @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); return getArithmeticOp().foldStamp(inputBits, resultBits, newStamp); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { - ValueNode synonym = findSynonym(getOp(forValue), forValue, inputBits, resultBits, stamp()); + ValueNode synonym = findSynonym(getOp(forValue), forValue, inputBits, resultBits, stamp(NodeView.DEFAULT)); if (synonym != null) { return synonym; } @@ -121,12 +122,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A return null; } - public static ValueNode convert(ValueNode input, Stamp stamp) { - return convert(input, stamp, false); + public static ValueNode convert(ValueNode input, Stamp stamp, NodeView view) { + return convert(input, stamp, false, view); } - public static ValueNode convert(ValueNode input, Stamp stamp, StructuredGraph graph) { - ValueNode convert = convert(input, stamp, false); + public static ValueNode convert(ValueNode input, Stamp stamp, StructuredGraph graph, NodeView view) { + ValueNode convert = convert(input, stamp, false, view); if (!convert.isAlive()) { assert !convert.isDeleted(); convert = graph.addOrUniqueWithInputs(convert); @@ -134,12 +135,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A return convert; } - public static ValueNode convertUnsigned(ValueNode input, Stamp stamp) { - return convert(input, stamp, true); + public static ValueNode convertUnsigned(ValueNode input, Stamp stamp, NodeView view) { + return convert(input, stamp, true, view); } - public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend) { - IntegerStamp fromStamp = (IntegerStamp) input.stamp(); + public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend, NodeView view) { + IntegerStamp fromStamp = (IntegerStamp) input.stamp(view); IntegerStamp toStamp = (IntegerStamp) stamp; ValueNode result; @@ -149,13 +150,13 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A result = new NarrowNode(input, fromStamp.getBits(), toStamp.getBits()); } else if (zeroExtend) { // toStamp.getBits() > fromStamp.getBits() - result = ZeroExtendNode.create(input, toStamp.getBits()); + result = ZeroExtendNode.create(input, toStamp.getBits(), view); } else { // toStamp.getBits() > fromStamp.getBits() - result = SignExtendNode.create(input, toStamp.getBits()); + result = SignExtendNode.create(input, toStamp.getBits(), view); } - IntegerStamp resultStamp = (IntegerStamp) result.stamp(); + IntegerStamp resultStamp = (IntegerStamp) result.stamp(view); assert toStamp.getBits() == resultStamp.getBits(); return result; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java index 97766cabec8..7a25afd4025 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -59,7 +60,7 @@ public abstract class IntegerDivRemNode extends FixedBinaryNode implements Lower // Assigning canDeopt during constructor, because it must never change during lifetime of // the node. - this.canDeopt = ((IntegerStamp) getY().stamp()).contains(0); + this.canDeopt = ((IntegerStamp) getY().stamp(NodeView.DEFAULT)).contains(0); } public final Op getOp() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java index dbb929d9ff9..8408d73e315 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -59,8 +60,8 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object; } - public static LogicNode create(ValueNode x, ValueNode y) { - LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false); + public static LogicNode create(ValueNode x, ValueNode y, NodeView view) { + LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false, view); if (result != null) { return result; } @@ -84,17 +85,19 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut return new IntegerEqualsNode(x, y).maybeCommuteInputs(); } - public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y) { - LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y); + public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y, + NodeView view) { + LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y, view); if (value != null) { return value; } - return create(x, y); + return create(x, y, view); } @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view); if (value != null) { return value; } @@ -104,7 +107,7 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut public static class IntegerEqualsOp extends CompareOp { @Override protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) { + Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) { PrimitiveConstant primitive = (PrimitiveConstant) constant; ValueNode a = normalizeNode.getX(); ValueNode b = normalizeNode.getY(); @@ -112,21 +115,21 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut if (cst == 0) { if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) { - return FloatEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b); + return FloatEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view); } else { - return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b); + return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view); } } else if (cst == 1) { if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) { - return FloatLessThanNode.create(b, a, !normalizeNode.isUnorderedLess); + return FloatLessThanNode.create(b, a, !normalizeNode.isUnorderedLess, view); } else { - return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a); + return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, view); } } else if (cst == -1) { if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) { - return FloatLessThanNode.create(a, b, normalizeNode.isUnorderedLess); + return FloatLessThanNode.create(a, b, normalizeNode.isUnorderedLess, view); } else { - return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b); + return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view); } } else { return LogicConstantNode.contradiction(); @@ -134,12 +137,12 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut } @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) { return new FloatEqualsNode(newX, newY); - } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) { + } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) { return new IntegerEqualsNode(newX, newY); - } else if (newX.stamp() instanceof AbstractPointerStamp && newY.stamp() instanceof AbstractPointerStamp) { + } else if (newX.stamp(view) instanceof AbstractPointerStamp && newY.stamp(view) instanceof AbstractPointerStamp) { return new IntegerEqualsNode(newX, newY); } throw GraalError.shouldNotReachHere(); @@ -147,10 +150,10 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut @Override public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { return LogicConstantNode.tautology(); - } else if (forX.stamp().alwaysDistinct(forY.stamp())) { + } else if (forX.stamp(view).alwaysDistinct(forY.stamp(view))) { return LogicConstantNode.contradiction(); } @@ -174,19 +177,19 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut } if (v1 != null) { assert v2 != null; - return create(v1, v2); + return create(v1, v2, view); } } - return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY); + return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); } @Override protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) { + Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) { if (constant instanceof PrimitiveConstant) { PrimitiveConstant primitiveConstant = (PrimitiveConstant) constant; - IntegerStamp nonConstantStamp = ((IntegerStamp) nonConstant.stamp()); + IntegerStamp nonConstantStamp = ((IntegerStamp) nonConstant.stamp(view)); if ((primitiveConstant.asLong() == 1 && nonConstantStamp.upperBound() == 1 && nonConstantStamp.lowerBound() == 0) || (primitiveConstant.asLong() == -1 && nonConstantStamp.upperBound() == 0 && nonConstantStamp.lowerBound() == -1)) { // nonConstant can only be 0 or 1 (respective -1), test against 0 instead of 1 @@ -194,15 +197,16 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut // execution // on specific platforms. return LogicNegationNode.create( - IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0))); + IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0), + view)); } else if (primitiveConstant.asLong() == 0) { if (nonConstant instanceof AndNode) { AndNode andNode = (AndNode) nonConstant; return new IntegerTestNode(andNode.getX(), andNode.getY()); } else if (nonConstant instanceof SubNode) { SubNode subNode = (SubNode) nonConstant; - return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, subNode.getX(), subNode.getY()); - } else if (nonConstant instanceof ShiftNode && nonConstant.stamp() instanceof IntegerStamp) { + return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, subNode.getX(), subNode.getY(), view); + } else if (nonConstant instanceof ShiftNode && nonConstant.stamp(view) instanceof IntegerStamp) { if (nonConstant instanceof LeftShiftNode) { LeftShiftNode shift = (LeftShiftNode) nonConstant; if (shift.getY().isConstant()) { @@ -217,7 +221,7 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut } } else if (nonConstant instanceof RightShiftNode) { RightShiftNode shift = (RightShiftNode) nonConstant; - if (shift.getY().isConstant() && ((IntegerStamp) shift.getX().stamp()).isPositive()) { + if (shift.getY().isConstant() && ((IntegerStamp) shift.getX().stamp(view)).isPositive()) { int mask = shift.getShiftAmountMask(); int amount = shift.getY().asJavaConstant().asInt() & mask; if (shift.getX().getStackKind() == JavaKind.Int) { @@ -258,16 +262,16 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut } } - if (nonConstant instanceof XorNode && nonConstant.stamp() instanceof IntegerStamp) { + if (nonConstant instanceof XorNode && nonConstant.stamp(view) instanceof IntegerStamp) { XorNode xorNode = (XorNode) nonConstant; - if (xorNode.getY().isJavaConstant() && xorNode.getY().asJavaConstant().asLong() == 1 && ((IntegerStamp) xorNode.getX().stamp()).upMask() == 1) { + if (xorNode.getY().isJavaConstant() && xorNode.getY().asJavaConstant().asLong() == 1 && ((IntegerStamp) xorNode.getX().stamp(view)).upMask() == 1) { // x ^ 1 == 0 is the same as x == 1 if x in [0, 1] // x ^ 1 == 1 is the same as x == 0 if x in [0, 1] - return new IntegerEqualsNode(xorNode.getX(), ConstantNode.forIntegerStamp(xorNode.getX().stamp(), primitiveConstant.asLong() ^ 1)); + return new IntegerEqualsNode(xorNode.getX(), ConstantNode.forIntegerStamp(xorNode.getX().stamp(view), primitiveConstant.asLong() ^ 1)); } } } - return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue); + return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue, view); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java index bce2a2c39f3..bab63e1f7d9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java @@ -40,6 +40,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.code.CodeUtil; @@ -60,22 +61,23 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object; } - public static LogicNode create(ValueNode x, ValueNode y) { - return OP.create(x, y); + public static LogicNode create(ValueNode x, ValueNode y, NodeView view) { + return OP.create(x, y, view); } public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - ValueNode x, ValueNode y) { - LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y); + ValueNode x, ValueNode y, NodeView view) { + LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y, view); if (value != null) { return value; } - return create(x, y); + return create(x, y, view); } @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY, view); if (value != null) { return value; } @@ -98,11 +100,11 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { public static class LessThanOp extends LowerOp { @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) { return new FloatLessThanNode(newX, newY, unorderedIsTrue); // TODO: Is the last arg // supposed to be true? - } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) { + } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) { return new IntegerLessThanNode(newX, newY); } throw GraalError.shouldNotReachHere(); @@ -110,7 +112,7 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { @Override protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) { + Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) { PrimitiveConstant primitive = (PrimitiveConstant) constant; /* @formatter:off * a NC b < c (not mirrored) @@ -138,18 +140,18 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { if (cst == 0) { if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) { - return FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, mirrored ^ normalizeNode.isUnorderedLess); + return FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, mirrored ^ normalizeNode.isUnorderedLess, view); } else { - return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b); + return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view); } } else if (cst == 1) { // a <= b <=> !(a > b) LogicNode compare; if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) { // since we negate, we have to reverse the unordered result - compare = FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, mirrored == normalizeNode.isUnorderedLess); + compare = FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, mirrored == normalizeNode.isUnorderedLess, view); } else { - compare = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a); + compare = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, view); } return LogicNegationNode.create(compare); } else if (cst <= -1) { @@ -161,13 +163,13 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { } @Override - protected LogicNode findSynonym(ValueNode forX, ValueNode forY) { - LogicNode result = super.findSynonym(forX, forY); + protected LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) { + LogicNode result = super.findSynonym(forX, forY, view); if (result != null) { return result; } - if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) { - if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) { + if (forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) { + if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(view), (IntegerStamp) forY.stamp(view))) { return new IntegerBelowNode(forX, forY); } } @@ -188,8 +190,8 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { } if (xx != null) { assert yy != null; - IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp(); - IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp(); + IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp(view); + IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp(view); long minValue = CodeUtil.minValue(xStamp.getBits()); long maxValue = CodeUtil.maxValue(xStamp.getBits()); @@ -203,10 +205,10 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { } } - if (forX.stamp() instanceof IntegerStamp) { - assert forY.stamp() instanceof IntegerStamp; - int bits = ((IntegerStamp) forX.stamp()).getBits(); - assert ((IntegerStamp) forY.stamp()).getBits() == bits; + if (forX.stamp(view) instanceof IntegerStamp) { + assert forY.stamp(view) instanceof IntegerStamp; + int bits = ((IntegerStamp) forX.stamp(view)).getBits(); + assert ((IntegerStamp) forY.stamp(view)).getBits() == bits; long min = OP.minValue(bits); long xResidue = 0; ValueNode left = null; @@ -240,12 +242,12 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode { if (left == null) { left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min); } else if (xResidue != 0) { - left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue)); + left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue), view); } if (right == null) { right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min); } else if (yResidue != 0) { - right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue)); + right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue), view); } return new IntegerBelowNode(left, right); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java index 9ec1d8fe20d..0294ef39a7f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -76,12 +77,12 @@ public abstract class IntegerLowerThanNode extends CompareNode { IntegerStamp xStamp = (IntegerStamp) xStampGeneric; AddNode addNode = (AddNode) forY; IntegerStamp aStamp = null; - if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) { + if (addNode.getX() == forX && addNode.getY().stamp(NodeView.DEFAULT) instanceof IntegerStamp) { // x < x + a - aStamp = (IntegerStamp) addNode.getY().stamp(); - } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) { + aStamp = (IntegerStamp) addNode.getY().stamp(NodeView.DEFAULT); + } else if (addNode.getY() == forX && addNode.getX().stamp(NodeView.DEFAULT) instanceof IntegerStamp) { // x < a + x - aStamp = (IntegerStamp) addNode.getX().stamp(); + aStamp = (IntegerStamp) addNode.getX().stamp(NodeView.DEFAULT); } if (aStamp != null) { IntegerStamp result = getOp().getSucceedingStampForXLowerXPlusA(mirror, strict, aStamp); @@ -117,12 +118,12 @@ public abstract class IntegerLowerThanNode extends CompareNode { public abstract static class LowerOp extends CompareOp { @Override public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { - LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY); + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { + LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); if (result != null) { return result; } - LogicNode synonym = findSynonym(forX, forY); + LogicNode synonym = findSynonym(forX, forY, view); if (synonym != null) { return synonym; } @@ -159,12 +160,12 @@ public abstract class IntegerLowerThanNode extends CompareNode { protected abstract IntegerLowerThanNode createNode(ValueNode x, ValueNode y); - public LogicNode create(ValueNode x, ValueNode y) { - LogicNode result = CompareNode.tryConstantFoldPrimitive(getCondition(), x, y, false); + public LogicNode create(ValueNode x, ValueNode y, NodeView view) { + LogicNode result = CompareNode.tryConstantFoldPrimitive(getCondition(), x, y, false, view); if (result != null) { return result; } else { - result = findSynonym(x, y); + result = findSynonym(x, y, view); if (result != null) { return result; } @@ -172,47 +173,47 @@ public abstract class IntegerLowerThanNode extends CompareNode { } } - protected LogicNode findSynonym(ValueNode forX, ValueNode forY) { + protected LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { return LogicConstantNode.contradiction(); } - TriState fold = tryFold(forX.stamp(), forY.stamp()); + TriState fold = tryFold(forX.stamp(view), forY.stamp(view)); if (fold.isTrue()) { return LogicConstantNode.tautology(); } else if (fold.isFalse()) { return LogicConstantNode.contradiction(); } - if (forY.stamp() instanceof IntegerStamp) { - IntegerStamp yStamp = (IntegerStamp) forY.stamp(); + if (forY.stamp(view) instanceof IntegerStamp) { + IntegerStamp yStamp = (IntegerStamp) forY.stamp(view); int bits = yStamp.getBits(); if (forX.isJavaConstant() && !forY.isConstant()) { // bring the constant on the right long xValue = forX.asJavaConstant().asLong(); if (xValue != maxValue(bits)) { // c < x <=> !(c >= x) <=> !(x <= c) <=> !(x < c + 1) - return LogicNegationNode.create(create(forY, ConstantNode.forIntegerStamp(yStamp, xValue + 1))); + return LogicNegationNode.create(create(forY, ConstantNode.forIntegerStamp(yStamp, xValue + 1), view)); } } if (forY.isJavaConstant()) { long yValue = forY.asJavaConstant().asLong(); if (yValue == maxValue(bits)) { // x < MAX <=> x != MAX - return LogicNegationNode.create(IntegerEqualsNode.create(forX, forY)); + return LogicNegationNode.create(IntegerEqualsNode.create(forX, forY, view)); } if (yValue == minValue(bits) + 1) { // x < MIN + 1 <=> x <= MIN <=> x == MIN - return IntegerEqualsNode.create(forX, ConstantNode.forIntegerStamp(yStamp, minValue(bits))); + return IntegerEqualsNode.create(forX, ConstantNode.forIntegerStamp(yStamp, minValue(bits)), view); } } else if (forY instanceof AddNode) { AddNode addNode = (AddNode) forY; - LogicNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true); + LogicNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true, view); if (canonical != null) { return canonical; } } if (forX instanceof AddNode) { AddNode addNode = (AddNode) forX; - LogicNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false); + LogicNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false, view); if (canonical != null) { return canonical; } @@ -221,32 +222,32 @@ public abstract class IntegerLowerThanNode extends CompareNode { return null; } - private LogicNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean mirrored, boolean strict) { + private LogicNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean mirrored, boolean strict, NodeView view) { // x < x + a IntegerStamp succeedingXStamp; boolean exact; - if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) { - IntegerStamp aStamp = (IntegerStamp) addNode.getY().stamp(); + if (addNode.getX() == forX && addNode.getY().stamp(view) instanceof IntegerStamp) { + IntegerStamp aStamp = (IntegerStamp) addNode.getY().stamp(view); succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp); exact = aStamp.lowerBound() == aStamp.upperBound(); - } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) { - IntegerStamp aStamp = (IntegerStamp) addNode.getX().stamp(); + } else if (addNode.getY() == forX && addNode.getX().stamp(view) instanceof IntegerStamp) { + IntegerStamp aStamp = (IntegerStamp) addNode.getX().stamp(view); succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp); exact = aStamp.lowerBound() == aStamp.upperBound(); } else { return null; } - if (succeedingXStamp.join(forX.stamp()).isEmpty()) { + if (succeedingXStamp.join(forX.stamp(view)).isEmpty()) { return LogicConstantNode.contradiction(); } else if (exact && !succeedingXStamp.isEmpty()) { int bits = succeedingXStamp.getBits(); if (compare(lowerBound(succeedingXStamp), minValue(bits)) > 0) { assert upperBound(succeedingXStamp) == maxValue(bits); // x must be in [L..MAX] <=> x >= L <=> !(x < L) - return LogicNegationNode.create(create(forX, ConstantNode.forIntegerStamp(succeedingXStamp, lowerBound(succeedingXStamp)))); + return LogicNegationNode.create(create(forX, ConstantNode.forIntegerStamp(succeedingXStamp, lowerBound(succeedingXStamp)), view)); } else if (compare(upperBound(succeedingXStamp), maxValue(bits)) < 0) { // x must be in [MIN..H] <=> x <= H <=> !(H < x) - return LogicNegationNode.create(create(ConstantNode.forIntegerStamp(succeedingXStamp, upperBound(succeedingXStamp)), forX)); + return LogicNegationNode.create(create(ConstantNode.forIntegerStamp(succeedingXStamp, upperBound(succeedingXStamp)), forX, view)); } } return null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java index cf7feb6971e..0e837972d75 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.BinaryOpLogicNode; import org.graalvm.compiler.nodes.LogicConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.meta.TriState; @@ -52,12 +53,13 @@ public final class IntegerTestNode extends BinaryOpLogicNode implements BinaryCo @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); if (forX.isConstant() && forY.isConstant()) { return LogicConstantNode.forBoolean((forX.asJavaConstant().asLong() & forY.asJavaConstant().asLong()) == 0); } - if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) { - IntegerStamp xStamp = (IntegerStamp) forX.stamp(); - IntegerStamp yStamp = (IntegerStamp) forY.stamp(); + if (forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) { + IntegerStamp xStamp = (IntegerStamp) forX.stamp(view); + IntegerStamp yStamp = (IntegerStamp) forY.stamp(view); if ((xStamp.upMask() & yStamp.upMask()) == 0) { return LogicConstantNode.tautology(); } else if ((xStamp.downMask() & yStamp.downMask()) != 0) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java index 68b5700559b..85de8712869 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.UnaryOpLogicNode; import org.graalvm.compiler.nodes.ValueNode; @@ -77,7 +78,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable, @Override public boolean verify() { assertTrue(getValue() != null, "is null input must not be null"); - assertTrue(getValue().stamp() instanceof AbstractPointerStamp, "input must be a pointer not %s", getValue().stamp()); + assertTrue(getValue().stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp, "input must be a pointer not %s", getValue().stamp(NodeView.DEFAULT)); return super.verify(); } @@ -113,7 +114,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable, @Override public void virtualize(VirtualizerTool tool) { ValueNode alias = tool.getAlias(getValue()); - TriState fold = tryFold(alias.stamp()); + TriState fold = tryFold(alias.stamp(NodeView.DEFAULT)); if (fold != TriState.UNKNOWN) { tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph())); } @@ -122,7 +123,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable, @Override public Stamp getSucceedingStampForValue(boolean negated) { // Ignore any more precise input stamp since canonicalization will skip through PiNodes - AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp().unrestricted(); + AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp(NodeView.DEFAULT).unrestricted(); return negated ? pointerStamp.asNonNull() : pointerStamp.asAlwaysNull(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java index f9d7cf3ed6e..98186ee15c3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -45,10 +46,10 @@ public final class LeftShiftNode extends ShiftNode<Shl> { super(TYPE, ArithmeticOpTable::getShl, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - ArithmeticOpTable.ShiftOp<Shl> op = ArithmeticOpTable.forStamp(x.stamp()).getShl(); - Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp()); - ValueNode value = ShiftNode.canonical(op, stamp, x, y); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + ArithmeticOpTable.ShiftOp<Shl> op = ArithmeticOpTable.forStamp(x.stamp(view)).getShl(); + Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view)); + ValueNode value = ShiftNode.canonical(op, stamp, x, y, view); if (value != null) { return value; } @@ -63,7 +64,7 @@ public final class LeftShiftNode extends ShiftNode<Shl> { return ret; } - return canonical(this, getArithmeticOp(), stamp(), forX, forY); + return canonical(this, getArithmeticOp(), stamp(NodeView.DEFAULT), forX, forY); } private static ValueNode canonical(LeftShiftNode leftShiftNode, ArithmeticOpTable.ShiftOp<Shl> op, Stamp stamp, ValueNode forX, ValueNode forY) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java index 8f1c8879172..6f5cec5c454 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -56,14 +57,14 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit super(c, ArithmeticOpTable::getMul, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Mul> op = ArithmeticOpTable.forStamp(x.stamp()).getMul(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Mul> op = ArithmeticOpTable.forStamp(x.stamp(view)).getMul(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } @Override @@ -83,10 +84,11 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit return new MulNode(forY, forX); } BinaryOp<Mul> op = getOp(forX, forY); - return canonical(this, op, stamp(), forX, forY); + NodeView view = NodeView.from(tool); + return canonical(this, op, stamp(view), forX, forY, view); } - private static ValueNode canonical(MulNode self, BinaryOp<Mul> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(MulNode self, BinaryOp<Mul> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (forY.isConstant()) { Constant c = forY.asConstant(); if (op.isNeutral(c)) { @@ -95,57 +97,64 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { long i = ((PrimitiveConstant) c).asLong(); - - if (i == 0) { - return ConstantNode.forIntegerStamp(stamp, 0); - } else if (i == 1) { - return forX; - } else if (i == -1) { - return NegateNode.create(forX); - } else if (i > 0) { - if (CodeUtil.isPowerOf2(i)) { - return new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i))); - } else if (CodeUtil.isPowerOf2(i - 1)) { - return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX); - } else if (CodeUtil.isPowerOf2(i + 1)) { - return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX); - } else { - int bitCount = Long.bitCount(i); - long highestBitValue = Long.highestOneBit(i); - if (bitCount == 2) { - // e.g., 0b1000_0010 - long lowerBitValue = i - highestBitValue; - assert highestBitValue > 0 && lowerBitValue > 0; - ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue))); - ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue))); - return AddNode.create(left, right); - } else { - // e.g., 0b1111_1101 - int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1; - long subValue = (1 << shiftToRoundUpToPowerOf2) - i; - if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) { - assert CodeUtil.log2(subValue) >= 1; - ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2)); - ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue))); - return SubNode.create(left, right); - } - } - } - } else if (i < 0) { - if (CodeUtil.isPowerOf2(-i)) { - return NegateNode.create(LeftShiftNode.create(forX, ConstantNode.forInt(CodeUtil.log2(-i)))); - } + ValueNode result = canonical(stamp, forX, i, view); + if (result != null) { + return result; } } if (op.isAssociative()) { // canonicalize expressions like "(a * 1) * 2" - return reassociate(self != null ? self : (MulNode) new MulNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY); + return reassociate(self != null ? self : (MulNode) new MulNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view); } } return self != null ? self : new MulNode(forX, forY).maybeCommuteInputs(); } + public static ValueNode canonical(Stamp stamp, ValueNode forX, long i, NodeView view) { + if (i == 0) { + return ConstantNode.forIntegerStamp(stamp, 0); + } else if (i == 1) { + return forX; + } else if (i == -1) { + return NegateNode.create(forX, view); + } else if (i > 0) { + if (CodeUtil.isPowerOf2(i)) { + return new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i))); + } else if (CodeUtil.isPowerOf2(i - 1)) { + return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX, view); + } else if (CodeUtil.isPowerOf2(i + 1)) { + return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX, view); + } else { + int bitCount = Long.bitCount(i); + long highestBitValue = Long.highestOneBit(i); + if (bitCount == 2) { + // e.g., 0b1000_0010 + long lowerBitValue = i - highestBitValue; + assert highestBitValue > 0 && lowerBitValue > 0; + ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue))); + ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue))); + return AddNode.create(left, right, view); + } else { + // e.g., 0b1111_1101 + int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1; + long subValue = (1 << shiftToRoundUpToPowerOf2) - i; + if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) { + assert CodeUtil.log2(subValue) >= 1; + ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2)); + ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue))); + return SubNode.create(left, right, view); + } + } + } + } else if (i < 0) { + if (CodeUtil.isPowerOf2(-i)) { + return NegateNode.create(LeftShiftNode.create(forX, ConstantNode.forInt(CodeUtil.log2(-i)), view), view); + } + } + return null; + } + @Override public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) { Value op1 = nodeValueMap.operand(getX()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java index 766c6f854a9..e417e65cb88 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -48,21 +49,21 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> { public static final NodeClass<NarrowNode> TYPE = NodeClass.create(NarrowNode.class); public NarrowNode(ValueNode input, int resultBits) { - this(input, PrimitiveStamp.getBits(input.stamp()), resultBits); - assert 0 < resultBits && resultBits <= PrimitiveStamp.getBits(input.stamp()); + this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits); + assert 0 < resultBits && resultBits <= PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)); } public NarrowNode(ValueNode input, int inputBits, int resultBits) { super(TYPE, ArithmeticOpTable::getNarrow, ArithmeticOpTable::getSignExtend, inputBits, resultBits, input); } - public static ValueNode create(ValueNode input, int resultBits) { - return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits); + public static ValueNode create(ValueNode input, int resultBits, NodeView view) { + return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view); } - public static ValueNode create(ValueNode input, int inputBits, int resultBits) { - IntegerConvertOp<Narrow> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getNarrow(); - ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp())); + public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) { + IntegerConvertOp<Narrow> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getNarrow(); + ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view))); if (synonym != null) { return synonym; } else { @@ -77,6 +78,7 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> { @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forValue); if (ret != this) { return ret; @@ -108,7 +110,7 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> { if (other instanceof SignExtendNode) { // sxxx -(sign-extend)-> ssssssss sssssxxx -(narrow)-> sssssxxx // ==> sxxx -(sign-extend)-> sssssxxx - return SignExtendNode.create(other.getValue(), other.getInputBits(), getResultBits()); + return SignExtendNode.create(other.getValue(), other.getInputBits(), getResultBits(), view); } else if (other instanceof ZeroExtendNode) { // xxxx -(zero-extend)-> 00000000 00000xxx -(narrow)-> 0000xxxx // ==> xxxx -(zero-extend)-> 0000xxxx @@ -117,13 +119,13 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> { } } else if (forValue instanceof AndNode) { AndNode andNode = (AndNode) forValue; - IntegerStamp yStamp = (IntegerStamp) andNode.getY().stamp(); - IntegerStamp xStamp = (IntegerStamp) andNode.getX().stamp(); + IntegerStamp yStamp = (IntegerStamp) andNode.getY().stamp(view); + IntegerStamp xStamp = (IntegerStamp) andNode.getX().stamp(view); long relevantMask = CodeUtil.mask(this.getResultBits()); if ((relevantMask & yStamp.downMask()) == relevantMask) { - return create(andNode.getX(), this.getResultBits()); + return create(andNode.getX(), this.getResultBits(), view); } else if ((relevantMask & xStamp.downMask()) == relevantMask) { - return create(andNode.getY(), this.getResultBits()); + return create(andNode.getY(), this.getResultBits(), view); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java index ff563c7a94d..0ee81cc2bf8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.spi.StampInverter; @@ -49,8 +50,8 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow super(TYPE, ArithmeticOpTable::getNeg, value); } - public static ValueNode create(ValueNode value) { - ValueNode synonym = findSynonym(value); + public static ValueNode create(ValueNode value, NodeView view) { + ValueNode synonym = findSynonym(value, view); if (synonym != null) { return synonym; } @@ -66,8 +67,8 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow return this; } - protected static ValueNode findSynonym(ValueNode forValue) { - ArithmeticOpTable.UnaryOp<Neg> negOp = ArithmeticOpTable.forStamp(forValue.stamp()).getNeg(); + protected static ValueNode findSynonym(ValueNode forValue, NodeView view) { + ArithmeticOpTable.UnaryOp<Neg> negOp = ArithmeticOpTable.forStamp(forValue.stamp(view)).getNeg(); ValueNode synonym = UnaryArithmeticNode.findSynonym(forValue, negOp); if (synonym != null) { return synonym; @@ -75,9 +76,9 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow if (forValue instanceof NegateNode) { return ((NegateNode) forValue).getValue(); } - if (forValue instanceof SubNode && !(forValue.stamp() instanceof FloatStamp)) { + if (forValue instanceof SubNode && !(forValue.stamp(view) instanceof FloatStamp)) { SubNode sub = (SubNode) forValue; - return SubNode.create(sub.getY(), sub.getX()); + return SubNode.create(sub.getY(), sub.getX(), view); } return null; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java index 049ff137986..6faad2746c4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.meta.ConstantReflectionProvider; @@ -86,7 +87,8 @@ public final class NormalizeCompareNode extends BinaryNode implements IterableNo @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode result = tryConstantFold(x, y, isUnorderedLess, stamp().getStackKind(), tool.getConstantReflection()); + NodeView view = NodeView.from(tool); + ValueNode result = tryConstantFold(x, y, isUnorderedLess, stamp(view).getStackKind(), tool.getConstantReflection()); if (result != null) { return result; } @@ -100,7 +102,7 @@ public final class NormalizeCompareNode extends BinaryNode implements IterableNo @Override public Stamp foldStamp(Stamp stampX, Stamp stampY) { - return stamp(); + return stamp(NodeView.DEFAULT); } public boolean isUnorderedLess() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java index 0fbfd223295..3e79b87c572 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java @@ -45,20 +45,31 @@ public final class NotNode extends UnaryArithmeticNode<Not> implements Arithmeti public static final NodeClass<NotNode> TYPE = NodeClass.create(NotNode.class); - public NotNode(ValueNode x) { + protected NotNode(ValueNode x) { super(TYPE, ArithmeticOpTable::getNot, x); } + public static ValueNode create(ValueNode x) { + return canonicalize(null, x); + } + @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { ValueNode ret = super.canonical(tool, forValue); if (ret != this) { return ret; } - if (forValue instanceof NotNode) { - return ((NotNode) forValue).getValue(); + return canonicalize(this, forValue); + } + + private static ValueNode canonicalize(NotNode node, ValueNode x) { + if (x instanceof NotNode) { + return ((NotNode) x).getValue(); } - return this; + if (node != null) { + return node; + } + return new NotNode(x); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java index 06e156a8daf..9a9f2f402fb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.GetClassNode; import org.graalvm.compiler.nodes.java.InstanceOfNode; @@ -58,16 +59,16 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual public ObjectEqualsNode(ValueNode x, ValueNode y) { super(TYPE, x, y); - assert x.stamp() instanceof AbstractObjectStamp; - assert y.stamp() instanceof AbstractObjectStamp; + assert x.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp; + assert y.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp; } - public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) { + public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) { LogicNode result = CompareNode.tryConstantFold(Condition.EQ, x, y, constantReflection, false); if (result != null) { return result; } else { - result = findSynonym(x, y); + result = findSynonym(x, y, view); if (result != null) { return result; } @@ -75,17 +76,18 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual } } - public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, ValueNode x, ValueNode y) { - LogicNode result = OP.canonical(constantReflection, metaAccess, options, null, Condition.EQ, false, x, y); + public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, ValueNode x, ValueNode y, NodeView view) { + LogicNode result = OP.canonical(constantReflection, metaAccess, options, null, Condition.EQ, false, x, y, view); if (result != null) { return result; } - return create(x, y, constantReflection); + return create(x, y, constantReflection, view); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view); if (value != null) { return value; } @@ -96,25 +98,25 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual @Override protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, - Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) { + Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) { ResolvedJavaType type = constantReflection.asJavaType(constant); if (type != null && nonConstant instanceof GetClassNode) { GetClassNode getClassNode = (GetClassNode) nonConstant; ValueNode object = getClassNode.getObject(); - assert ((ObjectStamp) object.stamp()).nonNull(); + assert ((ObjectStamp) object.stamp(view)).nonNull(); if (!type.isPrimitive() && (type.isConcrete() || type.isArray())) { return InstanceOfNode.create(TypeReference.createExactTrusted(type), object); } return LogicConstantNode.forBoolean(false); } - return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue); + return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue, view); } @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { - if (newX.stamp() instanceof ObjectStamp && newY.stamp() instanceof ObjectStamp) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { + if (newX.stamp(view) instanceof ObjectStamp && newY.stamp(view) instanceof ObjectStamp) { return new ObjectEqualsNode(newX, newY); - } else if (newX.stamp() instanceof AbstractPointerStamp && newY.stamp() instanceof AbstractPointerStamp) { + } else if (newX.stamp(view) instanceof AbstractPointerStamp && newY.stamp(view) instanceof AbstractPointerStamp) { return new PointerEqualsNode(newX, newY); } throw GraalError.shouldNotReachHere(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java index 417ee0d101d..65ab3a01f23 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -50,27 +51,28 @@ public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryComm super(TYPE, ArithmeticOpTable::getOr, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(view)).getOr(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forX, forY); if (ret != this) { return ret; } - return canonical(this, getOp(forX, forY), stamp(), forX, forY); + return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view); } - private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { return forX; } @@ -90,10 +92,10 @@ public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryComm return ConstantNode.forIntegerStamp(stamp, mask); } } - return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY); + return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view); } if (forX instanceof NotNode && forY instanceof NotNode) { - return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue())); + return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue(), view)); } return self != null ? self : new OrNode(forX, forY).maybeCommuteInputs(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java index 798982d25e4..d1e399d1c13 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.LoadHubNode; import org.graalvm.compiler.nodes.extended.LoadMethodNode; @@ -56,8 +57,8 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative< this(TYPE, x, y); } - public static LogicNode create(ValueNode x, ValueNode y) { - LogicNode result = findSynonym(x, y); + public static LogicNode create(ValueNode x, ValueNode y, NodeView view) { + LogicNode result = findSynonym(x, y, view); if (result != null) { return result; } @@ -66,13 +67,14 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative< protected PointerEqualsNode(NodeClass<? extends PointerEqualsNode> c, ValueNode x, ValueNode y) { super(c, Condition.EQ, false, x, y); - assert x.stamp() instanceof AbstractPointerStamp; - assert y.stamp() instanceof AbstractPointerStamp; + assert x.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp; + assert y.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp; } @Override public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY); + NodeView view = NodeView.from(tool); + ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view); if (value != null) { return value; } @@ -111,31 +113,31 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative< @Override public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition, - boolean unorderedIsTrue, ValueNode forX, ValueNode forY) { - LogicNode result = findSynonym(forX, forY); + boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) { + LogicNode result = findSynonym(forX, forY, view); if (result != null) { return result; } if (isAlwaysFailingVirtualDispatchTest(condition, forX, forY)) { return LogicConstantNode.contradiction(); } - return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY); + return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); } @Override - protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) { + protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) { return new PointerEqualsNode(newX, newY); } } - public static LogicNode findSynonym(ValueNode forX, ValueNode forY) { + public static LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { return LogicConstantNode.tautology(); - } else if (forX.stamp().alwaysDistinct(forY.stamp())) { + } else if (forX.stamp(view).alwaysDistinct(forY.stamp(view))) { return LogicConstantNode.contradiction(); - } else if (((AbstractPointerStamp) forX.stamp()).alwaysNull()) { + } else if (((AbstractPointerStamp) forX.stamp(view)).alwaysNull()) { return IsNullNode.create(forY); - } else if (((AbstractPointerStamp) forY.stamp()).alwaysNull()) { + } else if (((AbstractPointerStamp) forY.stamp(view)).alwaysNull()) { return IsNullNode.create(forX); } else { return null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java index dc5e60d39d0..b77e3c02102 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -56,16 +57,24 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow public static final NodeClass<ReinterpretNode> TYPE = NodeClass.create(ReinterpretNode.class); - public ReinterpretNode(JavaKind to, ValueNode value) { + protected ReinterpretNode(JavaKind to, ValueNode value) { this(StampFactory.forKind(to), value); } - public ReinterpretNode(Stamp to, ValueNode value) { - super(TYPE, getReinterpretStamp(to, value.stamp()), value); + protected ReinterpretNode(Stamp to, ValueNode value) { + super(TYPE, getReinterpretStamp(to, value.stamp(NodeView.DEFAULT)), value); assert to instanceof ArithmeticStamp; } - private SerializableConstant evalConst(SerializableConstant c) { + public static ValueNode create(JavaKind to, ValueNode value, NodeView view) { + return create(StampFactory.forKind(to), value, view); + } + + public static ValueNode create(Stamp to, ValueNode value, NodeView view) { + return canonical(null, to, value, view); + } + + private static SerializableConstant evalConst(Stamp stamp, SerializableConstant c) { /* * We don't care about byte order here. Either would produce the correct result. */ @@ -73,7 +82,7 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow c.serialize(buffer); buffer.rewind(); - SerializableConstant ret = ((ArithmeticStamp) stamp()).deserialize(buffer); + SerializableConstant ret = ((ArithmeticStamp) stamp).deserialize(buffer); assert !buffer.hasRemaining(); return ret; @@ -81,17 +90,22 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { + NodeView view = NodeView.from(tool); + return canonical(this, this.stamp(view), forValue, view); + } + + public static ValueNode canonical(ReinterpretNode node, Stamp forStamp, ValueNode forValue, NodeView view) { if (forValue.isConstant()) { - return ConstantNode.forConstant(stamp(), evalConst((SerializableConstant) forValue.asConstant()), null); + return ConstantNode.forConstant(forStamp, evalConst(forStamp, (SerializableConstant) forValue.asConstant()), null); } - if (stamp().isCompatible(forValue.stamp())) { + if (forStamp.isCompatible(forValue.stamp(view))) { return forValue; } if (forValue instanceof ReinterpretNode) { ReinterpretNode reinterpret = (ReinterpretNode) forValue; - return new ReinterpretNode(stamp(), reinterpret.getValue()); + return new ReinterpretNode(forStamp, reinterpret.getValue()); } - return this; + return node != null ? node : new ReinterpretNode(forStamp, forValue); } /** @@ -269,12 +283,12 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow @Override public boolean inferStamp() { - return updateStamp(getReinterpretStamp(stamp(), getValue().stamp())); + return updateStamp(getReinterpretStamp(stamp(NodeView.DEFAULT), getValue().stamp(NodeView.DEFAULT))); } @Override public void generate(NodeLIRBuilderTool builder, ArithmeticLIRGeneratorTool gen) { - LIRKind kind = builder.getLIRGeneratorTool().getLIRKind(stamp()); + LIRKind kind = builder.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); builder.setResult(this, gen.emitReinterpret(kind, builder.operand(getValue()))); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java index 3ef7eebb6b8..54201e79071 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java @@ -25,10 +25,14 @@ package org.graalvm.compiler.nodes.calc; import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_32; import org.graalvm.compiler.core.common.type.ArithmeticOpTable; +import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -39,7 +43,7 @@ public class RemNode extends BinaryArithmeticNode<Rem> implements Lowerable { public static final NodeClass<RemNode> TYPE = NodeClass.create(RemNode.class); - public RemNode(ValueNode x, ValueNode y) { + protected RemNode(ValueNode x, ValueNode y) { this(TYPE, x, y); } @@ -47,6 +51,16 @@ public class RemNode extends BinaryArithmeticNode<Rem> implements Lowerable { super(c, ArithmeticOpTable::getRem, x, y); } + public static ValueNode create(ValueNode forX, ValueNode forY, NodeView view) { + BinaryOp<Rem> op = ArithmeticOpTable.forStamp(forX.stamp(view)).getRem(); + Stamp stamp = op.foldStamp(forX.stamp(view), forY.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, forX, forY, stamp, view); + if (tryConstantFold != null) { + return tryConstantFold; + } + return new RemNode(forX, forY); + } + @Override public void lower(LoweringTool tool) { tool.getLowerer().lower(this, tool); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java index 7b5661b698b..621cb865afc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -45,30 +46,31 @@ public final class RightShiftNode extends ShiftNode<Shr> { super(TYPE, ArithmeticOpTable::getShr, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - ArithmeticOpTable.ShiftOp<Shr> op = ArithmeticOpTable.forStamp(x.stamp()).getShr(); - Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp()); - ValueNode value = ShiftNode.canonical(op, stamp, x, y); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + ArithmeticOpTable.ShiftOp<Shr> op = ArithmeticOpTable.forStamp(x.stamp(view)).getShr(); + Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view)); + ValueNode value = ShiftNode.canonical(op, stamp, x, y, view); if (value != null) { return value; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forX, forY); if (ret != this) { return ret; } - return canonical(this, getArithmeticOp(), stamp(), forX, forY); + return canonical(this, getArithmeticOp(), stamp(view), forX, forY, view); } - private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTable.ShiftOp<Shr> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTable.ShiftOp<Shr> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { RightShiftNode self = rightShiftNode; - if (forX.stamp() instanceof IntegerStamp && ((IntegerStamp) forX.stamp()).isPositive()) { + if (forX.stamp(view) instanceof IntegerStamp && ((IntegerStamp) forX.stamp(view)).isPositive()) { return new UnsignedRightShiftNode(forX, forY); } @@ -87,8 +89,8 @@ public final class RightShiftNode extends ShiftNode<Shr> { if (other instanceof RightShiftNode) { int total = amount + otherAmount; if (total != (total & mask)) { - assert other.getX().stamp() instanceof IntegerStamp; - IntegerStamp istamp = (IntegerStamp) other.getX().stamp(); + assert other.getX().stamp(view) instanceof IntegerStamp; + IntegerStamp istamp = (IntegerStamp) other.getX().stamp(view); if (istamp.isPositive()) { return ConstantNode.forIntegerKind(stamp.getStackKind(), 0); @@ -131,7 +133,7 @@ public final class RightShiftNode extends ShiftNode<Shr> { * are all equal to the sign bit of the input. That's equivalent to the condition that * the input is in the signed range of the narrow type. */ - IntegerStamp inputStamp = (IntegerStamp) getX().stamp(); + IntegerStamp inputStamp = (IntegerStamp) getX().stamp(NodeView.DEFAULT); return CodeUtil.minValue(resultBits) <= inputStamp.lowerBound() && inputStamp.upperBound() <= CodeUtil.maxValue(resultBits); } else { return false; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java index 1e997bc5b8d..062a861f828 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ArithmeticOperation; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -64,13 +65,13 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper * @param s the second input value */ protected ShiftNode(NodeClass<? extends ShiftNode<OP>> c, SerializableShiftFunction<OP> getOp, ValueNode x, ValueNode s) { - super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp())).foldStamp(x.stamp(), (IntegerStamp) s.stamp()), x, s); - assert ((IntegerStamp) s.stamp()).getBits() == 32; + super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT))).foldStamp(x.stamp(NodeView.DEFAULT), (IntegerStamp) s.stamp(NodeView.DEFAULT)), x, s); + assert ((IntegerStamp) s.stamp(NodeView.DEFAULT)).getBits() == 32; this.getOp = getOp; } protected final ShiftOp<OP> getOp(ValueNode forValue) { - return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp())); + return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT))); } @Override @@ -85,14 +86,16 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - ValueNode valueNode = canonical(getOp(forX), stamp(), forX, forY); + NodeView view = NodeView.from(tool); + ValueNode valueNode = canonical(getOp(forX), stamp(NodeView.DEFAULT), forX, forY, view); if (valueNode != null) { return valueNode; } return this; } - public static <OP> ValueNode canonical(ShiftOp<OP> op, Stamp stamp, ValueNode forX, ValueNode forY) { + @SuppressWarnings("unused") + public static <OP> ValueNode canonical(ShiftOp<OP> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (forX.isConstant() && forY.isConstant()) { JavaConstant amount = forY.asJavaConstant(); assert amount.getJavaKind() == JavaKind.Int; @@ -102,7 +105,7 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper } public int getShiftAmountMask() { - return getArithmeticOp().getShiftAmountMask(stamp()); + return getArithmeticOp().getShiftAmountMask(stamp(NodeView.DEFAULT)); } @Override @@ -117,7 +120,7 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper * amount. We can narrow only if (y & wideMask) == (y & narrowMask) for all possible values * of y. */ - IntegerStamp yStamp = (IntegerStamp) getY().stamp(); + IntegerStamp yStamp = (IntegerStamp) getY().stamp(NodeView.DEFAULT); return (yStamp.upMask() & (wideMask & ~narrowMask)) == 0; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java index 65c4a7050e5..a603eda3934 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -46,25 +47,25 @@ public final class SignExtendNode extends IntegerConvertNode<SignExtend, Narrow> public static final NodeClass<SignExtendNode> TYPE = NodeClass.create(SignExtendNode.class); public SignExtendNode(ValueNode input, int resultBits) { - this(input, PrimitiveStamp.getBits(input.stamp()), resultBits); - assert 0 < PrimitiveStamp.getBits(input.stamp()) && PrimitiveStamp.getBits(input.stamp()) <= resultBits; + this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits); + assert 0 < PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) && PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) <= resultBits; } public SignExtendNode(ValueNode input, int inputBits, int resultBits) { super(TYPE, ArithmeticOpTable::getSignExtend, ArithmeticOpTable::getNarrow, inputBits, resultBits, input); } - public static ValueNode create(ValueNode input, int resultBits) { - return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits); + public static ValueNode create(ValueNode input, int resultBits, NodeView view) { + return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view); } - public static ValueNode create(ValueNode input, int inputBits, int resultBits) { - IntegerConvertOp<SignExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getSignExtend(); - ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp())); + public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) { + IntegerConvertOp<SignExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getSignExtend(); + ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view))); if (synonym != null) { return synonym; } - return canonical(null, input, inputBits, resultBits); + return canonical(null, input, inputBits, resultBits, view); } @Override @@ -74,35 +75,36 @@ public final class SignExtendNode extends IntegerConvertNode<SignExtend, Narrow> @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forValue); if (ret != this) { return ret; } - return canonical(this, forValue, getInputBits(), getResultBits()); + return canonical(this, forValue, getInputBits(), getResultBits(), view); } - private static ValueNode canonical(SignExtendNode self, ValueNode forValue, int inputBits, int resultBits) { + private static ValueNode canonical(SignExtendNode self, ValueNode forValue, int inputBits, int resultBits, NodeView view) { if (forValue instanceof SignExtendNode) { // sxxx -(sign-extend)-> ssss sxxx -(sign-extend)-> ssssssss sssssxxx // ==> sxxx -(sign-extend)-> ssssssss sssssxxx SignExtendNode other = (SignExtendNode) forValue; - return SignExtendNode.create(other.getValue(), other.getInputBits(), resultBits); + return SignExtendNode.create(other.getValue(), other.getInputBits(), resultBits, view); } else if (forValue instanceof ZeroExtendNode) { ZeroExtendNode other = (ZeroExtendNode) forValue; if (other.getResultBits() > other.getInputBits()) { // sxxx -(zero-extend)-> 0000 sxxx -(sign-extend)-> 00000000 0000sxxx // ==> sxxx -(zero-extend)-> 00000000 0000sxxx - return ZeroExtendNode.create(other.getValue(), other.getInputBits(), resultBits); + return ZeroExtendNode.create(other.getValue(), other.getInputBits(), resultBits, view); } } - if (forValue.stamp() instanceof IntegerStamp) { - IntegerStamp inputStamp = (IntegerStamp) forValue.stamp(); + if (forValue.stamp(view) instanceof IntegerStamp) { + IntegerStamp inputStamp = (IntegerStamp) forValue.stamp(view); if ((inputStamp.upMask() & (1L << (inputBits - 1))) == 0L) { // 0xxx -(sign-extend)-> 0000 0xxx // ==> 0xxx -(zero-extend)-> 0000 0xxx - return ZeroExtendNode.create(forValue, inputBits, resultBits); + return ZeroExtendNode.create(forValue, inputBits, resultBits, view); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java index c8ba664abad..dc206008f60 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java @@ -24,10 +24,12 @@ package org.graalvm.compiler.nodes.calc; import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.PrimitiveStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -39,31 +41,42 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable { public static final NodeClass<SignedDivNode> TYPE = NodeClass.create(SignedDivNode.class); - public SignedDivNode(ValueNode x, ValueNode y) { + protected SignedDivNode(ValueNode x, ValueNode y) { this(TYPE, x, y); } protected SignedDivNode(NodeClass<? extends SignedDivNode> c, ValueNode x, ValueNode y) { - super(c, IntegerStamp.OPS.getDiv().foldStamp(x.stamp(), y.stamp()), Op.DIV, Type.SIGNED, x, y); + super(c, IntegerStamp.OPS.getDiv().foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), Op.DIV, Type.SIGNED, x, y); + } + + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + return canonical(null, x, y, view); } @Override public boolean inferStamp() { - return updateStamp(IntegerStamp.OPS.getDiv().foldStamp(getX().stamp(), getY().stamp())); + return updateStamp(IntegerStamp.OPS.getDiv().foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT))); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); + return canonical(this, forX, forY, view); + } + + public static ValueNode canonical(SignedDivNode self, ValueNode forX, ValueNode forY, NodeView view) { + Stamp predictedStamp = IntegerStamp.OPS.getDiv().foldStamp(forX.stamp(NodeView.DEFAULT), forY.stamp(NodeView.DEFAULT)); + Stamp stamp = self != null ? self.stamp(view) : predictedStamp; if (forX.isConstant() && forY.isConstant()) { - @SuppressWarnings("hiding") long y = forY.asJavaConstant().asLong(); if (y == 0) { - return this; // this will trap, can not canonicalize + return self != null ? self : new SignedDivNode(forX, forY); // this will trap, can + // not canonicalize } - return ConstantNode.forIntegerStamp(stamp(), forX.asJavaConstant().asLong() / y); + return ConstantNode.forIntegerStamp(stamp, forX.asJavaConstant().asLong() / y); } else if (forY.isConstant()) { long c = forY.asJavaConstant().asLong(); - ValueNode v = canonical(forX, c); + ValueNode v = canonical(forX, c, view); if (v != null) { return v; } @@ -74,47 +87,47 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable { SubNode integerSubNode = (SubNode) forX; if (integerSubNode.getY() instanceof SignedRemNode) { SignedRemNode integerRemNode = (SignedRemNode) integerSubNode.getY(); - if (integerSubNode.stamp().isCompatible(this.stamp()) && integerRemNode.stamp().isCompatible(this.stamp()) && integerSubNode.getX() == integerRemNode.getX() && + if (integerSubNode.stamp(view).isCompatible(stamp) && integerRemNode.stamp(view).isCompatible(stamp) && integerSubNode.getX() == integerRemNode.getX() && forY == integerRemNode.getY()) { SignedDivNode sd = new SignedDivNode(integerSubNode.getX(), forY); - sd.stateBefore = this.stateBefore; + sd.stateBefore = self != null ? self.stateBefore : null; return sd; } } } - if (next() instanceof SignedDivNode) { - NodeClass<?> nodeClass = getNodeClass(); - if (next().getClass() == this.getClass() && nodeClass.equalInputs(this, next()) && valueEquals(next())) { - return next(); + if (self != null && self.next() instanceof SignedDivNode) { + NodeClass<?> nodeClass = self.getNodeClass(); + if (self.next().getClass() == self.getClass() && nodeClass.equalInputs(self, self.next()) && self.valueEquals(self.next())) { + return self.next(); } } - return this; + return self != null ? self : new SignedDivNode(forX, forY); } - public static ValueNode canonical(ValueNode forX, long c) { + public static ValueNode canonical(ValueNode forX, long c, NodeView view) { if (c == 1) { return forX; } if (c == -1) { - return NegateNode.create(forX); + return NegateNode.create(forX, view); } long abs = Math.abs(c); - if (CodeUtil.isPowerOf2(abs) && forX.stamp() instanceof IntegerStamp) { + if (CodeUtil.isPowerOf2(abs) && forX.stamp(view) instanceof IntegerStamp) { ValueNode dividend = forX; - IntegerStamp stampX = (IntegerStamp) forX.stamp(); + IntegerStamp stampX = (IntegerStamp) forX.stamp(view); int log2 = CodeUtil.log2(abs); // no rounding if dividend is positive or if its low bits are always 0 if (stampX.canBeNegative() || (stampX.upMask() & (abs - 1)) != 0) { - int bits = PrimitiveStamp.getBits(forX.stamp()); + int bits = PrimitiveStamp.getBits(forX.stamp(view)); RightShiftNode sign = new RightShiftNode(forX, ConstantNode.forInt(bits - 1)); UnsignedRightShiftNode round = new UnsignedRightShiftNode(sign, ConstantNode.forInt(bits - log2)); - dividend = BinaryArithmeticNode.add(dividend, round); + dividend = BinaryArithmeticNode.add(dividend, round, view); } RightShiftNode shift = new RightShiftNode(dividend, ConstantNode.forInt(log2)); if (c < 0) { - return NegateNode.create(shift); + return NegateNode.create(shift, view); } return shift; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java index 4dbfd845b54..432cb047cd2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java @@ -23,10 +23,12 @@ package org.graalvm.compiler.nodes.calc; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -38,52 +40,63 @@ public class SignedRemNode extends IntegerDivRemNode implements LIRLowerable { public static final NodeClass<SignedRemNode> TYPE = NodeClass.create(SignedRemNode.class); - public SignedRemNode(ValueNode x, ValueNode y) { + protected SignedRemNode(ValueNode x, ValueNode y) { this(TYPE, x, y); } protected SignedRemNode(NodeClass<? extends SignedRemNode> c, ValueNode x, ValueNode y) { - super(c, IntegerStamp.OPS.getRem().foldStamp(x.stamp(), y.stamp()), Op.REM, Type.SIGNED, x, y); + super(c, IntegerStamp.OPS.getRem().foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), Op.REM, Type.SIGNED, x, y); + } + + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + Stamp stamp = IntegerStamp.OPS.getRem().foldStamp(x.stamp(view), y.stamp(view)); + return canonical(null, x, y, stamp, view); } @Override public boolean inferStamp() { - return updateStamp(IntegerStamp.OPS.getRem().foldStamp(getX().stamp(), getY().stamp())); + return updateStamp(IntegerStamp.OPS.getRem().foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT))); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); + return canonical(this, forX, forY, stamp(view), view); + } + + private static ValueNode canonical(SignedRemNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) { if (forX.isConstant() && forY.isConstant()) { - @SuppressWarnings("hiding") long y = forY.asJavaConstant().asLong(); if (y == 0) { - return this; // this will trap, can not canonicalize + return self != null ? self : new SignedRemNode(forX, forY); // this will trap, can + // not canonicalize } - return ConstantNode.forIntegerStamp(stamp(), forX.asJavaConstant().asLong() % y); - } else if (forY.isConstant() && forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) { + return ConstantNode.forIntegerStamp(stamp, forX.asJavaConstant().asLong() % y); + } else if (forY.isConstant() && forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) { long constY = forY.asJavaConstant().asLong(); - IntegerStamp xStamp = (IntegerStamp) forX.stamp(); - IntegerStamp yStamp = (IntegerStamp) forY.stamp(); + IntegerStamp xStamp = (IntegerStamp) forX.stamp(view); + IntegerStamp yStamp = (IntegerStamp) forY.stamp(view); if (constY < 0 && constY != CodeUtil.minValue(yStamp.getBits())) { - return new SignedRemNode(forX, ConstantNode.forIntegerStamp(yStamp, -constY)).canonical(tool); + Stamp newStamp = IntegerStamp.OPS.getRem().foldStamp(forX.stamp(view), forY.stamp(view)); + return canonical(null, forX, ConstantNode.forIntegerStamp(yStamp, -constY), newStamp, view); } if (constY == 1) { - return ConstantNode.forIntegerStamp(stamp(), 0); + return ConstantNode.forIntegerStamp(stamp, 0); } else if (CodeUtil.isPowerOf2(constY)) { if (xStamp.isPositive()) { // x & (y - 1) - return new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), constY - 1)); + return new AndNode(forX, ConstantNode.forIntegerStamp(stamp, constY - 1)); } else if (xStamp.isNegative()) { // -((-x) & (y - 1)) - return new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp(), constY - 1))); + return new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp, constY - 1))); } else { // x - ((x / y) << log2(y)) - return SubNode.create(forX, LeftShiftNode.create(SignedDivNode.canonical(forX, constY), ConstantNode.forInt(CodeUtil.log2(constY)))); + return SubNode.create(forX, LeftShiftNode.create(SignedDivNode.canonical(forX, constY, view), ConstantNode.forInt(CodeUtil.log2(constY)), view), view); } } } - return this; + return self != null ? self : new SignedRemNode(forX, forY); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java index 8b400619d2a..ae80f135ca4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java @@ -30,6 +30,8 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -42,10 +44,18 @@ public final class SqrtNode extends UnaryArithmeticNode<Sqrt> implements Arithme public static final NodeClass<SqrtNode> TYPE = NodeClass.create(SqrtNode.class); - public SqrtNode(ValueNode x) { + protected SqrtNode(ValueNode x) { super(TYPE, ArithmeticOpTable::getSqrt, x); } + public static ValueNode create(ValueNode x, NodeView view) { + if (x.isConstant()) { + ArithmeticOpTable.UnaryOp<Sqrt> op = ArithmeticOpTable.forStamp(x.stamp(view)).getSqrt(); + return ConstantNode.forPrimitive(op.foldStamp(x.stamp(view)), op.foldConstant(x.asConstant())); + } + return new SqrtNode(x); + } + @Override public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) { nodeValueMap.setResult(this, gen.emitMathSqrt(nodeValueMap.operand(getValue()))); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java index bfeca0be95c..f6fe0ab5e4c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -53,20 +54,20 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit super(c, ArithmeticOpTable::getSub, x, y); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Sub> op = ArithmeticOpTable.forStamp(x.stamp()).getSub(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Sub> op = ArithmeticOpTable.forStamp(x.stamp(view)).getSub(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } - private static ValueNode canonical(SubNode subNode, BinaryOp<Sub> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(SubNode subNode, BinaryOp<Sub> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { SubNode self = subNode; if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { - Constant zero = op.getZero(forX.stamp()); + Constant zero = op.getZero(forX.stamp(view)); if (zero != null) { return ConstantNode.forPrimitive(stamp, zero); } @@ -87,18 +88,18 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit SubNode x = (SubNode) forX; if (x.getX() == forY) { // (a - b) - a - return NegateNode.create(x.getY()); + return NegateNode.create(x.getY(), view); } } if (forY instanceof AddNode) { AddNode y = (AddNode) forY; if (y.getX() == forX) { // a - (a + b) - return NegateNode.create(y.getY()); + return NegateNode.create(y.getY(), view); } if (y.getY() == forX) { // b - (a + b) - return NegateNode.create(y.getX()); + return NegateNode.create(y.getX(), view); } } else if (forY instanceof SubNode) { SubNode y = (SubNode) forY; @@ -114,7 +115,7 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit return forX; } if (associative && self != null) { - ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY); + ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view); if (reassociated != self) { return reassociated; } @@ -124,7 +125,7 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit if (i < 0 || ((IntegerStamp) StampFactory.forKind(forY.getStackKind())).contains(-i)) { // Adding a negative is more friendly to the backend since adds are // commutative, so prefer add when it fits. - return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp, -i)); + return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp, -i), view); } } } else if (forX.isConstant()) { @@ -135,30 +136,28 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit * have to test for the neutral element of +, because we are doing this * transformation: 0 - x == (-x) + 0 == -x. */ - return NegateNode.create(forY); + return NegateNode.create(forY, view); } if (associative && self != null) { - return reassociate(self, ValueNode.isConstantPredicate(), forX, forY); + return reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view); } } if (forY instanceof NegateNode) { - return BinaryArithmeticNode.add(forX, ((NegateNode) forY).getValue()); + return BinaryArithmeticNode.add(forX, ((NegateNode) forY).getValue(), view); } - if (self == null) { - self = new SubNode(forX, forY); - } - return self; + return self != null ? self : new SubNode(forX, forY); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forX, forY); if (ret != this) { return ret; } BinaryOp<Sub> op = getOp(forX, forY); - return canonical(this, op, stamp, forX, forY); + return canonical(this, op, stamp, forX, forY, view); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java index 7f3d019888b..d2f0418d6d8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ArithmeticOperation; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -47,12 +48,12 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith protected final SerializableUnaryFunction<OP> getOp; protected UnaryArithmeticNode(NodeClass<? extends UnaryArithmeticNode<OP>> c, SerializableUnaryFunction<OP> getOp, ValueNode value) { - super(c, getOp.apply(ArithmeticOpTable.forStamp(value.stamp())).foldStamp(value.stamp()), value); + super(c, getOp.apply(ArithmeticOpTable.forStamp(value.stamp(NodeView.DEFAULT))).foldStamp(value.stamp(NodeView.DEFAULT)), value); this.getOp = getOp; } protected final UnaryOp<OP> getOp(ValueNode forValue) { - return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp())); + return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT))); } @Override @@ -62,7 +63,7 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); return getOp(getValue()).foldStamp(newStamp); } @@ -77,7 +78,7 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith protected static <OP> ValueNode findSynonym(ValueNode forValue, UnaryOp<OP> op) { if (forValue.isConstant()) { - return ConstantNode.forPrimitive(op.foldStamp(forValue.stamp()), op.foldConstant(forValue.asConstant())); + return ConstantNode.forPrimitive(op.foldStamp(forValue.stamp(NodeView.DEFAULT)), op.foldConstant(forValue.asConstant())); } return null; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java index 66a8ee32342..e09f50788a2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; /** @@ -63,7 +64,7 @@ public abstract class UnaryNode extends FloatingNode implements Canonicalizable. @Override public boolean inferStamp() { - return updateStamp(foldStamp(value.stamp())); + return updateStamp(foldStamp(value.stamp(NodeView.DEFAULT))); } /** @@ -74,6 +75,6 @@ public abstract class UnaryNode extends FloatingNode implements Canonicalizable. * @param newStamp */ public Stamp foldStamp(Stamp newStamp) { - return stamp(); + return stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java index ad7cfdc9932..dadd326bf12 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -52,7 +53,8 @@ public final class UnpackEndianHalfNode extends UnaryNode implements Lowerable { this.firstHalf = firstHalf; } - public static ValueNode create(ValueNode value, boolean firstHalf) { + @SuppressWarnings("unused") + public static ValueNode create(ValueNode value, boolean firstHalf, NodeView view) { if (value.isConstant() && value.asConstant().isDefaultForKind()) { return ConstantNode.defaultForKind(JavaKind.Int); } @@ -84,7 +86,7 @@ public final class UnpackEndianHalfNode extends UnaryNode implements Lowerable { if ((byteOrder == ByteOrder.BIG_ENDIAN) == firstHalf) { result = graph().unique(new UnsignedRightShiftNode(result, ConstantNode.forInt(32, graph()))); } - result = IntegerConvertNode.convert(result, StampFactory.forKind(JavaKind.Int), graph()); + result = IntegerConvertNode.convert(result, StampFactory.forKind(JavaKind.Int), graph(), NodeView.DEFAULT); replaceAtUsagesAndDelete(result); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java index 313407800a8..792e4388a5d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java @@ -23,10 +23,12 @@ package org.graalvm.compiler.nodes.calc; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -43,18 +45,30 @@ public class UnsignedDivNode extends IntegerDivRemNode implements LIRLowerable { } protected UnsignedDivNode(NodeClass<? extends UnsignedDivNode> c, ValueNode x, ValueNode y) { - super(c, x.stamp().unrestricted(), Op.DIV, Type.UNSIGNED, x, y); + super(c, x.stamp(NodeView.DEFAULT).unrestricted(), Op.DIV, Type.UNSIGNED, x, y); + } + + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + Stamp stamp = x.stamp(view).unrestricted(); + return canonical(null, x, y, stamp, view); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - int bits = ((IntegerStamp) stamp()).getBits(); + NodeView view = NodeView.from(tool); + return canonical(this, forX, forY, stamp(view), view); + } + + @SuppressWarnings("unused") + private static ValueNode canonical(UnsignedDivNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) { + int bits = ((IntegerStamp) stamp).getBits(); if (forX.isConstant() && forY.isConstant()) { long yConst = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits); if (yConst == 0) { - return this; // this will trap, cannot canonicalize + return self != null ? self : new UnsignedDivNode(forX, forY); // this will trap, + // cannot canonicalize } - return ConstantNode.forIntegerStamp(stamp(), Long.divideUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst)); + return ConstantNode.forIntegerStamp(stamp, Long.divideUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst)); } else if (forY.isConstant()) { long c = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits); if (c == 1) { @@ -64,7 +78,7 @@ public class UnsignedDivNode extends IntegerDivRemNode implements LIRLowerable { return new UnsignedRightShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(c))); } } - return this; + return self != null ? self : new UnsignedDivNode(forX, forY); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java index 849324c4263..bfba2c43283 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java @@ -23,10 +23,12 @@ package org.graalvm.compiler.nodes.calc; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -43,27 +45,39 @@ public class UnsignedRemNode extends IntegerDivRemNode implements LIRLowerable { } protected UnsignedRemNode(NodeClass<? extends UnsignedRemNode> c, ValueNode x, ValueNode y) { - super(c, x.stamp().unrestricted(), Op.REM, Type.UNSIGNED, x, y); + super(c, x.stamp(NodeView.DEFAULT).unrestricted(), Op.REM, Type.UNSIGNED, x, y); + } + + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + Stamp stamp = x.stamp(view).unrestricted(); + return canonical(null, x, y, stamp, view); } @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { - int bits = ((IntegerStamp) stamp()).getBits(); + NodeView view = NodeView.from(tool); + return canonical(this, forX, forY, stamp(view), view); + } + + @SuppressWarnings("unused") + public static ValueNode canonical(UnsignedRemNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) { + int bits = ((IntegerStamp) stamp).getBits(); if (forX.isConstant() && forY.isConstant()) { long yConst = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits); if (yConst == 0) { - return this; // this will trap, cannot canonicalize + return self != null ? self : new UnsignedRemNode(forX, forY); // this will trap, + // cannot canonicalize } - return ConstantNode.forIntegerStamp(stamp(), Long.remainderUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst)); + return ConstantNode.forIntegerStamp(stamp, Long.remainderUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst)); } else if (forY.isConstant()) { long c = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits); if (c == 1) { - return ConstantNode.forIntegerStamp(stamp(), 0); + return ConstantNode.forIntegerStamp(stamp, 0); } else if (CodeUtil.isPowerOf2(c)) { - return new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), c - 1)); + return new AndNode(forX, ConstantNode.forIntegerStamp(stamp, c - 1)); } } - return this; + return self != null ? self : new UnsignedRemNode(forX, forY); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java index 7845b059401..7edf7d4ec20 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java @@ -25,11 +25,13 @@ package org.graalvm.compiler.nodes.calc; import org.graalvm.compiler.core.common.type.ArithmeticOpTable; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.UShr; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -44,17 +46,34 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> { super(TYPE, ArithmeticOpTable::getUShr, x, y); } + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + ArithmeticOpTable.ShiftOp<UShr> op = ArithmeticOpTable.forStamp(x.stamp(view)).getUShr(); + Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view)); + ValueNode value = ShiftNode.canonical(op, stamp, x, y, view); + if (value != null) { + return value; + } + + return canonical(null, op, stamp, x, y, view); + } + @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forX, forY); if (ret != this) { return ret; } + return canonical(this, this.getArithmeticOp(), this.stamp(view), forX, forY, view); + } + + @SuppressWarnings("unused") + private static ValueNode canonical(UnsignedRightShiftNode node, ArithmeticOpTable.ShiftOp<UShr> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (forY.isConstant()) { int amount = forY.asJavaConstant().asInt(); int originalAmout = amount; - int mask = getShiftAmountMask(); + int mask = op.getShiftAmountMask(stamp); amount &= mask; if (amount == 0) { return forX; @@ -66,14 +85,14 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> { if (other instanceof UnsignedRightShiftNode) { int total = amount + otherAmount; if (total != (total & mask)) { - return ConstantNode.forIntegerKind(getStackKind(), 0); + return ConstantNode.forIntegerKind(stamp.getStackKind(), 0); } return new UnsignedRightShiftNode(other.getX(), ConstantNode.forInt(total)); } else if (other instanceof LeftShiftNode && otherAmount == amount) { - if (getStackKind() == JavaKind.Long) { + if (stamp.getStackKind() == JavaKind.Long) { return new AndNode(other.getX(), ConstantNode.forLong(-1L >>> amount)); } else { - assert getStackKind() == JavaKind.Int; + assert stamp.getStackKind() == JavaKind.Int; return new AndNode(other.getX(), ConstantNode.forInt(-1 >>> amount)); } } @@ -83,7 +102,11 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> { return new UnsignedRightShiftNode(forX, ConstantNode.forInt(amount)); } } - return this; + + if (node != null) { + return node; + } + return new UnsignedRightShiftNode(forX, forY); } @Override @@ -98,7 +121,7 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> { * For unsigned right shifts, the narrow can be done before the shift if the cut off * bits are all zero. */ - IntegerStamp inputStamp = (IntegerStamp) getX().stamp(); + IntegerStamp inputStamp = (IntegerStamp) getX().stamp(NodeView.DEFAULT); return (inputStamp.upMask() & ~(resultBits - 1)) == 0; } else { return false; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java index 93d33ada9f2..a204f24d93c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; import org.graalvm.compiler.nodes.util.GraphUtil; @@ -48,17 +49,17 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo public XorNode(ValueNode x, ValueNode y) { super(TYPE, ArithmeticOpTable::getXor, x, y); - assert x.stamp().isCompatible(y.stamp()); + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)); } - public static ValueNode create(ValueNode x, ValueNode y) { - BinaryOp<Xor> op = ArithmeticOpTable.forStamp(x.stamp()).getXor(); - Stamp stamp = op.foldStamp(x.stamp(), y.stamp()); - ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp); + public static ValueNode create(ValueNode x, ValueNode y, NodeView view) { + BinaryOp<Xor> op = ArithmeticOpTable.forStamp(x.stamp(view)).getXor(); + Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view)); + ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view); if (tryConstantFold != null) { return tryConstantFold; } - return canonical(null, op, stamp, x, y); + return canonical(null, op, stamp, x, y, view); } @Override @@ -68,12 +69,13 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo return ret; } - return canonical(this, getOp(forX, forY), stamp(), forX, forY); + NodeView view = NodeView.from(tool); + return canonical(this, getOp(forX, forY), stamp(NodeView.DEFAULT), forX, forY, view); } - private static ValueNode canonical(XorNode self, BinaryOp<Xor> op, Stamp stamp, ValueNode forX, ValueNode forY) { + private static ValueNode canonical(XorNode self, BinaryOp<Xor> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { - return ConstantNode.forPrimitive(stamp, op.getZero(forX.stamp())); + return ConstantNode.forPrimitive(stamp, op.getZero(forX.stamp(view))); } if (forX.isConstant() && !forY.isConstant()) { return new XorNode(forY, forX); @@ -91,7 +93,7 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo return new NotNode(forX); } } - return reassociate(self != null ? self : (XorNode) new XorNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY); + return reassociate(self != null ? self : (XorNode) new XorNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view); } return self != null ? self : new XorNode(forX, forY).maybeCommuteInputs(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java index 83b5b3a4471..8574c816fca 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -50,25 +51,25 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow> public static final NodeClass<ZeroExtendNode> TYPE = NodeClass.create(ZeroExtendNode.class); public ZeroExtendNode(ValueNode input, int resultBits) { - this(input, PrimitiveStamp.getBits(input.stamp()), resultBits); - assert 0 < PrimitiveStamp.getBits(input.stamp()) && PrimitiveStamp.getBits(input.stamp()) <= resultBits; + this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits); + assert 0 < PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) && PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) <= resultBits; } public ZeroExtendNode(ValueNode input, int inputBits, int resultBits) { super(TYPE, ArithmeticOpTable::getZeroExtend, ArithmeticOpTable::getNarrow, inputBits, resultBits, input); } - public static ValueNode create(ValueNode input, int resultBits) { - return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits); + public static ValueNode create(ValueNode input, int resultBits, NodeView view) { + return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view); } - public static ValueNode create(ValueNode input, int inputBits, int resultBits) { - IntegerConvertOp<ZeroExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getZeroExtend(); - ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp())); + public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) { + IntegerConvertOp<ZeroExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getZeroExtend(); + ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view))); if (synonym != null) { return synonym; } - return canonical(null, input, inputBits, resultBits); + return canonical(null, input, inputBits, resultBits, view); } @Override @@ -91,15 +92,16 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow> @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { + NodeView view = NodeView.from(tool); ValueNode ret = super.canonical(tool, forValue); if (ret != this) { return ret; } - return canonical(this, forValue, getInputBits(), getResultBits()); + return canonical(this, forValue, getInputBits(), getResultBits(), view); } - private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits) { + private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits, NodeView view) { ZeroExtendNode self = zeroExtendNode; if (forValue instanceof ZeroExtendNode) { // xxxx -(zero-extend)-> 0000 xxxx -(zero-extend)-> 00000000 0000xxxx @@ -109,20 +111,20 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow> } if (forValue instanceof NarrowNode) { NarrowNode narrow = (NarrowNode) forValue; - Stamp inputStamp = narrow.getValue().stamp(); + Stamp inputStamp = narrow.getValue().stamp(view); if (inputStamp instanceof IntegerStamp) { IntegerStamp istamp = (IntegerStamp) inputStamp; - long mask = CodeUtil.mask(PrimitiveStamp.getBits(narrow.stamp())); + long mask = CodeUtil.mask(PrimitiveStamp.getBits(narrow.stamp(view))); if ((istamp.upMask() & ~mask) == 0) { // The original value cannot change because of the narrow and zero extend. if (istamp.getBits() < resultBits) { // Need to keep the zero extend, skip the narrow. - return create(narrow.getValue(), resultBits); + return create(narrow.getValue(), resultBits, view); } else if (istamp.getBits() > resultBits) { // Need to keep the narrow, skip the zero extend. - return NarrowNode.create(narrow.getValue(), resultBits); + return NarrowNode.create(narrow.getValue(), resultBits, view); } else { assert istamp.getBits() == resultBits; // Just return the original value. diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java index 300ac616bae..0dbce573cab 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java @@ -44,6 +44,10 @@ public final class BlackholeNode extends FixedWithNextNode implements LIRLowerab this.value = value; } + public ValueNode getValue() { + return value; + } + @Override public void generate(NodeLIRBuilderTool gen) { gen.getLIRGeneratorTool().emitBlackhole(gen.operand(value)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java index 62b564f2c38..cf5ee474356 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java @@ -27,6 +27,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -39,7 +40,7 @@ public final class OpaqueNode extends FloatingNode implements LIRLowerable { @Input protected ValueNode value; public OpaqueNode(ValueNode value) { - super(TYPE, value.stamp().unrestricted()); + super(TYPE, value.stamp(NodeView.DEFAULT).unrestricted()); this.value = value; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java index 38376fbf6cb..02cb3b8f607 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -90,7 +91,7 @@ public class BoxNode extends FixedWithNextNode implements VirtualizableAllocatio } protected VirtualBoxingNode createVirtualBoxingNode() { - return new VirtualBoxingNode(StampTool.typeOrNull(stamp()), boxingKind); + return new VirtualBoxingNode(StampTool.typeOrNull(stamp(NodeView.DEFAULT)), boxingKind); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java index cc3e63d14f8..e6f436d5729 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.ConditionalNode; @@ -67,7 +68,7 @@ public final class BranchProbabilityNode extends FloatingNode implements Simplif @Input ValueNode condition; public BranchProbabilityNode(ValueNode probability, ValueNode condition) { - super(TYPE, condition.stamp()); + super(TYPE, condition.stamp(NodeView.DEFAULT)); this.probability = probability; this.condition = condition; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java index b490e846ba1..6fef3a662d9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -46,7 +47,7 @@ public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowera } protected FixedValueAnchorNode(NodeClass<? extends FixedValueAnchorNode> c, ValueNode object) { - super(c, object.stamp()); + super(c, object.stamp(NodeView.DEFAULT)); this.object = object; } @@ -63,7 +64,7 @@ public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowera @Override public boolean inferStamp() { if (predefinedStamp == null) { - return updateStamp(object.stamp()); + return updateStamp(object.stamp(NodeView.DEFAULT)); } else { return false; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java index 15f73937b12..43f17e9462e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -60,7 +61,7 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon public GetClassNode(Stamp stamp, ValueNode object) { super(TYPE, stamp); this.object = object; - assert ((ObjectStamp) object.stamp()).nonNull(); + assert ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull(); } @Override @@ -68,9 +69,9 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon tool.getLowerer().lower(this, tool); } - public static ValueNode tryFold(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ValueNode object) { - if (metaAccess != null && object != null && object.stamp() instanceof ObjectStamp) { - ObjectStamp objectStamp = (ObjectStamp) object.stamp(); + public static ValueNode tryFold(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, NodeView view, ValueNode object) { + if (metaAccess != null && object != null && object.stamp(view) instanceof ObjectStamp) { + ObjectStamp objectStamp = (ObjectStamp) object.stamp(view); if (objectStamp.isExactType()) { return ConstantNode.forConstant(constantReflection.asJavaClass(objectStamp.type()), metaAccess); } @@ -80,7 +81,8 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon @Override public ValueNode canonical(CanonicalizerTool tool) { - ValueNode folded = tryFold(tool.getMetaAccess(), tool.getConstantReflection(), getObject()); + NodeView view = NodeView.from(tool); + ValueNode folded = tryFold(tool.getMetaAccess(), tool.getConstantReflection(), view, getObject()); return folded == null ? this : folded; } @@ -90,7 +92,7 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon if (alias instanceof VirtualObjectNode) { VirtualObjectNode virtual = (VirtualObjectNode) alias; Constant javaClass = tool.getConstantReflectionProvider().asJavaClass(virtual.type()); - tool.replaceWithValue(ConstantNode.forConstant(stamp(), javaClass, tool.getMetaAccessProvider(), graph())); + tool.replaceWithValue(ConstantNode.forConstant(stamp(NodeView.DEFAULT), javaClass, tool.getMetaAccessProvider(), graph())); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java index 75afb6f506f..d63cd4d311b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java @@ -43,6 +43,7 @@ import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.IntegerBelowNode; import org.graalvm.compiler.nodes.java.LoadIndexedNode; @@ -70,7 +71,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable, assert keySuccessors.length == keys.length + 1; assert keySuccessors.length == keyProbabilities.length; this.keys = keys; - assert value.stamp() instanceof PrimitiveStamp && value.stamp().getStackKind().isNumericInteger(); + assert value.stamp(NodeView.DEFAULT) instanceof PrimitiveStamp && value.stamp(NodeView.DEFAULT).getStackKind().isNumericInteger(); assert assertSorted(); } @@ -135,6 +136,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable, @Override public void simplify(SimplifierTool tool) { + NodeView view = NodeView.from(tool); if (blockSuccessorCount() == 1) { tool.addToWorkList(defaultSuccessor()); graph().removeSplitPropagate(this, defaultSuccessor()); @@ -142,7 +144,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable, killOtherSuccessors(tool, successorIndexAtKey(value().asJavaConstant().asInt())); } else if (tryOptimizeEnumSwitch(tool)) { return; - } else if (tryRemoveUnreachableKeys(tool, value().stamp())) { + } else if (tryRemoveUnreachableKeys(tool, value().stamp(view))) { return; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java index 1000a9e0ca9..5e19233ac26 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -61,8 +62,8 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni } private static Stamp hubStamp(StampProvider stampProvider, ValueNode value) { - assert value.stamp() instanceof ObjectStamp; - return stampProvider.createHubStamp(((ObjectStamp) value.stamp())); + assert value.stamp(NodeView.DEFAULT) instanceof ObjectStamp; + return stampProvider.createHubStamp(((ObjectStamp) value.stamp(NodeView.DEFAULT))); } public static ValueNode create(ValueNode value, StampProvider stampProvider, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) { @@ -91,9 +92,10 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni @Override public ValueNode canonical(CanonicalizerTool tool) { if (!GeneratePIC.getValue(tool.getOptions())) { + NodeView view = NodeView.from(tool); MetaAccessProvider metaAccess = tool.getMetaAccess(); ValueNode curValue = getValue(); - ValueNode newNode = findSynonym(curValue, stamp(), metaAccess, tool.getConstantReflection()); + ValueNode newNode = findSynonym(curValue, stamp(view), metaAccess, tool.getConstantReflection()); if (newNode != null) { return newNode; } @@ -117,7 +119,7 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni ValueNode alias = tool.getAlias(getValue()); TypeReference type = StampTool.typeReferenceOrNull(alias); if (type != null && type.isExact()) { - tool.replaceWithValue(ConstantNode.forConstant(stamp(), tool.getConstantReflectionProvider().asObjectHub(type.getType()), tool.getMetaAccessProvider(), graph())); + tool.replaceWithValue(ConstantNode.forConstant(stamp(NodeView.DEFAULT), tool.getConstantReflectionProvider().asObjectHub(type.getType()), tool.getMetaAccessProvider(), graph())); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java index 85f0c218414..c0b12be3443 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -96,8 +97,9 @@ public final class LoadMethodNode extends FixedWithNextNode implements Lowerable Assumptions assumptions = graph().getAssumptions(); AssumptionResult<ResolvedJavaMethod> resolvedMethod = type.getType().findUniqueConcreteMethod(method); if (resolvedMethod != null && resolvedMethod.canRecordTo(assumptions) && !type.getType().isInterface() && method.getDeclaringClass().isAssignableFrom(type.getType())) { + NodeView view = NodeView.from(tool); resolvedMethod.recordTo(assumptions); - return ConstantNode.forConstant(stamp(), resolvedMethod.getResult().getEncoding(), tool.getMetaAccess()); + return ConstantNode.forConstant(stamp(view), resolvedMethod.getResult().getEncoding(), tool.getMetaAccess()); } } } @@ -123,9 +125,9 @@ public final class LoadMethodNode extends FixedWithNextNode implements Lowerable * This really represent a misuse of LoadMethod since we're loading from a class which * isn't known to implement the original method but for now at least fold it away. */ - return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, null); + return ConstantNode.forConstant(stamp(NodeView.DEFAULT), JavaConstant.NULL_POINTER, null); } else { - return ConstantNode.forConstant(stamp(), newMethod.getEncoding(), tool.getMetaAccess()); + return ConstantNode.forConstant(stamp(NodeView.DEFAULT), newMethod.getEncoding(), tool.getMetaAccess()); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java index de715c7e2c3..6638e3f0d14 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.ReinterpretNode; import org.graalvm.compiler.nodes.java.LoadFieldNode; @@ -103,14 +104,14 @@ public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtuali JavaKind entryKind = virtual.entryKind(entryIndex); if (entry.getStackKind() == getStackKind() || entryKind == accessKind()) { - if (!(entry.stamp().isCompatible(stamp()))) { - if (entry.stamp() instanceof PrimitiveStamp && stamp instanceof PrimitiveStamp) { + if (!(entry.stamp(NodeView.DEFAULT).isCompatible(stamp(NodeView.DEFAULT)))) { + if (entry.stamp(NodeView.DEFAULT) instanceof PrimitiveStamp && stamp instanceof PrimitiveStamp) { PrimitiveStamp p1 = (PrimitiveStamp) stamp; - PrimitiveStamp p2 = (PrimitiveStamp) entry.stamp(); + PrimitiveStamp p2 = (PrimitiveStamp) entry.stamp(NodeView.DEFAULT); int width1 = p1.getBits(); int width2 = p2.getBits(); if (width1 == width2) { - Node replacement = new ReinterpretNode(p2, entry); + Node replacement = ReinterpretNode.create(p2, entry, NodeView.DEFAULT); tool.replaceWith((ValueNode) replacement); return; } else { @@ -141,11 +142,12 @@ public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtuali if (arrayConstant != null) { int stableDimension = objectConstant.getStableDimension(); if (stableDimension > 0) { + NodeView view = NodeView.from(tool); long constantOffset = offset().asJavaConstant().asLong(); - Constant constant = stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), arrayConstant, constantOffset); + Constant constant = stamp(view).readConstant(tool.getConstantReflection().getMemoryAccessProvider(), arrayConstant, constantOffset); boolean isDefaultStable = objectConstant.isDefaultStable(); if (constant != null && (isDefaultStable || !constant.isDefaultForKind())) { - return ConstantNode.forConstant(stamp(), constant, stableDimension - 1, isDefaultStable, tool.getMetaAccess()); + return ConstantNode.forConstant(stamp(view), constant, stableDimension - 1, isDefaultStable, tool.getMetaAccess()); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java index 4f9e076d4d6..14ae73e47ef 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java @@ -46,6 +46,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodeinfo.NodeSize; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ControlSplitNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.meta.Constant; @@ -79,7 +80,8 @@ public abstract class SwitchNode extends ControlSplitNode { */ protected SwitchNode(NodeClass<? extends SwitchNode> c, ValueNode value, AbstractBeginNode[] successors, int[] keySuccessors, double[] keyProbabilities) { super(c, StampFactory.forVoid()); - assert value.stamp().getStackKind().isNumericInteger() || value.stamp() instanceof AbstractPointerStamp : value.stamp() + " key not supported by SwitchNode"; + assert value.stamp(NodeView.DEFAULT).getStackKind().isNumericInteger() || value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp : value.stamp(NodeView.DEFAULT) + + " key not supported by SwitchNode"; assert keySuccessors.length == keyProbabilities.length; this.successors = new NodeSuccessorList<>(this, successors); this.value = value; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java index 09b4cc72c89..4fbbcef8ce8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.ValueNode; @@ -118,7 +119,7 @@ public interface GraphBuilderContext extends GraphBuilderTool { } default ValueNode addNonNullCast(ValueNode value) { - AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp(); + AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp(NodeView.DEFAULT); if (valueStamp.nonNull()) { return value; } else { @@ -277,7 +278,7 @@ public interface GraphBuilderContext extends GraphBuilderTool { default ValueNode nullCheckedValue(ValueNode value, DeoptimizationAction action) { if (!StampTool.isPointerNonNull(value)) { LogicNode condition = getGraph().unique(IsNullNode.create(value)); - ObjectStamp receiverStamp = (ObjectStamp) value.stamp(); + ObjectStamp receiverStamp = (ObjectStamp) value.stamp(NodeView.DEFAULT); Stamp stamp = receiverStamp.join(objectNonNull()); FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, action, true)); ValueNode nonNullReceiver = getGraph().addOrUniqueWithInputs(PiNode.create(value, stamp, fixedGuard)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java index cff46027cdb..332c51053ed 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.FixedAccessNode; @@ -88,6 +89,6 @@ public abstract class AbstractCompareAndSwapNode extends FixedAccessNode impleme @Override public Stamp getAccessStamp() { - return expectedValue.stamp().meet(newValue.stamp()).unrestricted(); + return expectedValue.stamp(NodeView.DEFAULT).meet(newValue.stamp(NodeView.DEFAULT)).unrestricted(); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java index fd1409e2184..0ad6b6b4ad3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.meta.MetaAccessProvider; @@ -59,7 +60,7 @@ public class DynamicNewInstanceNode extends AbstractNewObjectNode implements Can protected DynamicNewInstanceNode(NodeClass<? extends DynamicNewInstanceNode> c, ValueNode clazz, boolean fillContents, FrameState stateBefore) { super(c, StampFactory.objectNonNull(), fillContents, stateBefore); this.clazz = clazz; - assert ((ObjectStamp) clazz.stamp()).nonNull(); + assert ((ObjectStamp) clazz.stamp(NodeView.DEFAULT)).nonNull(); } public ValueNode getInstanceType() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java index d97d138f211..c00e7e842f5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.BeginStateSplitNode; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.KillingBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -79,7 +80,7 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo */ LocationIdentity locationsKilledByInvoke = ((InvokeWithExceptionNode) predecessor()).getLocationIdentity(); AbstractBeginNode entry = graph().add(KillingBeginNode.create(locationsKilledByInvoke)); - LoadExceptionObjectNode loadException = graph().add(new LoadExceptionObjectNode(stamp())); + LoadExceptionObjectNode loadException = graph().add(new LoadExceptionObjectNode(stamp(NodeView.DEFAULT))); loadException.setStateAfter(stateAfter()); replaceAtUsages(InputType.Value, loadException); @@ -93,7 +94,7 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo @Override public boolean verify() { assertTrue(stateAfter() != null, "an exception handler needs a frame state"); - assertTrue(stateAfter().stackSize() == 1 && stateAfter().stackAt(0).stamp().getStackKind() == JavaKind.Object, + assertTrue(stateAfter().stackSize() == 1 && stateAfter().stackAt(0).stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Object, "an exception handler's frame state must have only the exception on the stack"); return super.verify(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java index e63cffefe88..1aac395df8a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNegationNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.UnaryOpLogicNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.IsNullNode; @@ -92,7 +93,7 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu } public static LogicNode createHelper(ObjectStamp checkedStamp, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) { - LogicNode synonym = findSynonym(checkedStamp, object); + LogicNode synonym = findSynonym(checkedStamp, object, NodeView.DEFAULT); if (synonym != null) { return synonym; } else { @@ -107,7 +108,8 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { - LogicNode synonym = findSynonym(checkedStamp, forValue); + NodeView view = NodeView.from(tool); + LogicNode synonym = findSynonym(checkedStamp, forValue, view); if (synonym != null) { return synonym; } else { @@ -115,8 +117,8 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu } } - public static LogicNode findSynonym(ObjectStamp checkedStamp, ValueNode object) { - ObjectStamp inputStamp = (ObjectStamp) object.stamp(); + public static LogicNode findSynonym(ObjectStamp checkedStamp, ValueNode object, NodeView view) { + ObjectStamp inputStamp = (ObjectStamp) object.stamp(view); ObjectStamp joinedStamp = (ObjectStamp) checkedStamp.join(inputStamp); if (joinedStamp.isEmpty()) { @@ -158,7 +160,7 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu @Override public void virtualize(VirtualizerTool tool) { ValueNode alias = tool.getAlias(getValue()); - TriState fold = tryFold(alias.stamp()); + TriState fold = tryFold(alias.stamp(NodeView.DEFAULT)); if (fold != TriState.UNKNOWN) { tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph())); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java index 5ff55d2a65f..56f1ea939e0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.ValuePhiNode; @@ -98,7 +99,8 @@ public final class LoadFieldNode extends AccessFieldNode implements Canonicaliza @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forObject) { - if (tool.allUsagesAvailable() && hasNoUsages() && !isVolatile() && (isStatic() || StampTool.isPointerNonNull(forObject.stamp()))) { + NodeView view = NodeView.from(tool); + if (tool.allUsagesAvailable() && hasNoUsages() && !isVolatile() && (isStatic() || StampTool.isPointerNonNull(forObject.stamp(view)))) { return null; } return canonical(this, StampPair.create(stamp, uncheckedStamp), forObject, field, tool.getConstantFieldProvider(), @@ -178,10 +180,10 @@ public final class LoadFieldNode extends AccessFieldNode implements Canonicaliza int fieldIndex = ((VirtualInstanceNode) alias).fieldIndex(field()); if (fieldIndex != -1) { ValueNode entry = tool.getEntry((VirtualObjectNode) alias, fieldIndex); - if (stamp.isCompatible(entry.stamp())) { + if (stamp.isCompatible(entry.stamp(NodeView.DEFAULT))) { tool.replaceWith(entry); } else { - assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double || + assert stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Int && (entry.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double || entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields."; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java index dc2a857c5fa..cb731818fae 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.Virtualizable; import org.graalvm.compiler.nodes.spi.VirtualizerTool; @@ -92,7 +93,7 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable, private static JavaKind determinePreciseArrayElementType(ValueNode array, JavaKind kind) { if (kind == JavaKind.Byte) { - ResolvedJavaType javaType = ((ObjectStamp) array.stamp()).type(); + ResolvedJavaType javaType = ((ObjectStamp) array.stamp(NodeView.DEFAULT)).type(); if (javaType != null && javaType.isArray() && javaType.getComponentType() != null && javaType.getComponentType().getJavaKind() == JavaKind.Boolean) { return JavaKind.Boolean; } @@ -114,10 +115,10 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable, int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1; if (idx >= 0 && idx < virtual.entryCount()) { ValueNode entry = tool.getEntry(virtual, idx); - if (stamp.isCompatible(entry.stamp())) { + if (stamp.isCompatible(entry.stamp(NodeView.DEFAULT))) { tool.replaceWith(entry); } else { - assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double || + assert stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Int && (entry.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double || entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields."; } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java index 46e49c5f606..8cc692a0d7d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -58,11 +59,11 @@ public final class LogicCompareAndSwapNode extends AbstractCompareAndSwapNode { @Override public void generate(NodeLIRBuilderTool gen) { - assert getNewValue().stamp().isCompatible(getExpectedValue().stamp()); + assert getNewValue().stamp(NodeView.DEFAULT).isCompatible(getExpectedValue().stamp(NodeView.DEFAULT)); assert !this.canDeoptimize(); LIRGeneratorTool tool = gen.getLIRGeneratorTool(); - LIRKind resultKind = tool.getLIRKind(stamp()); + LIRKind resultKind = tool.getLIRKind(stamp(NodeView.DEFAULT)); Value trueResult = tool.emitConstant(resultKind, JavaConstant.TRUE); Value falseResult = tool.emitConstant(resultKind, JavaConstant.FALSE); Value result = tool.emitLogicCompareAndSwap(gen.operand(getAddress()), gen.operand(getExpectedValue()), gen.operand(getNewValue()), trueResult, falseResult); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java index dfd3c83419c..f477655786b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StateSplit; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.FixedAccessNode; @@ -55,7 +56,7 @@ public final class LoweredAtomicReadAndWriteNode extends FixedAccessNode impleme @OptionalInput(State) FrameState stateAfter; public LoweredAtomicReadAndWriteNode(AddressNode address, LocationIdentity location, ValueNode newValue, BarrierType barrierType) { - super(TYPE, address, location, newValue.stamp().unrestricted(), barrierType); + super(TYPE, address, location, newValue.stamp(NodeView.DEFAULT).unrestricted(), barrierType); this.newValue = newValue; } @@ -93,6 +94,6 @@ public final class LoweredAtomicReadAndWriteNode extends FixedAccessNode impleme @Override public Stamp getAccessStamp() { - return stamp(); + return stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java index c1e3ce64825..105f433f859 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -132,7 +133,7 @@ public class MethodCallTargetNode extends CallTargetNode implements IterableNode return targetMethod; } - return devirtualizeCall(invokeKind, targetMethod, contextType, receiver.graph().getAssumptions(), receiver.stamp()); + return devirtualizeCall(invokeKind, targetMethod, contextType, receiver.graph().getAssumptions(), receiver.stamp(NodeView.DEFAULT)); } public static ResolvedJavaMethod devirtualizeCall(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType, Assumptions assumptions, Stamp receiverStamp) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java index a62e08e333d..89fd3ddda60 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; import org.graalvm.compiler.nodes.spi.VirtualizableAllocation; @@ -119,7 +120,8 @@ public class NewArrayNode extends AbstractNewArrayNode implements VirtualizableA @Override public void simplify(SimplifierTool tool) { if (hasNoUsages()) { - Stamp lengthStamp = length().stamp(); + NodeView view = NodeView.from(tool); + Stamp lengthStamp = length().stamp(view); if (lengthStamp instanceof IntegerStamp) { IntegerStamp lengthIntegerStamp = (IntegerStamp) lengthStamp; if (lengthIntegerStamp.isPositive()) { @@ -130,7 +132,7 @@ public class NewArrayNode extends AbstractNewArrayNode implements VirtualizableA // Should be areFrameStatesAtSideEffects but currently SVM will complain about // RuntimeConstraint if (graph().getGuardsStage().allowsFloatingGuards()) { - LogicNode lengthNegativeCondition = CompareNode.createCompareNode(graph(), Condition.LT, length(), ConstantNode.forInt(0, graph()), tool.getConstantReflection()); + LogicNode lengthNegativeCondition = CompareNode.createCompareNode(graph(), Condition.LT, length(), ConstantNode.forInt(0, graph()), tool.getConstantReflection(), view); // we do not have a non-deopting path for that at the moment so action=None. FixedGuardNode guard = graph().add(new FixedGuardNode(lengthNegativeCondition, DeoptimizationReason.RuntimeConstraint, DeoptimizationAction.None, true)); graph().replaceFixedWithFixed(this, guard); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java index 59d3d000ff8..9870bbb9994 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.graph.IterableNodeType; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.MonitorEnter; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; @@ -56,7 +57,7 @@ public final class RawMonitorEnterNode extends AccessMonitorNode implements Virt public RawMonitorEnterNode(ValueNode object, ValueNode hub, MonitorIdNode monitorId) { super(TYPE, object, monitorId); - assert ((ObjectStamp) object.stamp()).nonNull(); + assert ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull(); this.hub = hub; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java index c1d2d996ac5..7898cc85688 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractStateSplit; import org.graalvm.compiler.nodes.DeoptimizingNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -87,7 +88,7 @@ public final class RegisterFinalizerNode extends AbstractStateSplit implements C * that must be registered with the runtime upon object initialization. */ public static boolean mayHaveFinalizer(ValueNode object, Assumptions assumptions) { - ObjectStamp objectStamp = (ObjectStamp) object.stamp(); + ObjectStamp objectStamp = (ObjectStamp) object.stamp(NodeView.DEFAULT); if (objectStamp.isExactType()) { return objectStamp.type().hasFinalizer(); } else if (objectStamp.type() != null) { @@ -102,7 +103,8 @@ public final class RegisterFinalizerNode extends AbstractStateSplit implements C @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) { - if (!(forValue.stamp() instanceof ObjectStamp)) { + NodeView view = NodeView.from(tool); + if (!(forValue.stamp(view) instanceof ObjectStamp)) { return this; } if (!mayHaveFinalizer(forValue, graph().getAssumptions())) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java index 7bc851ad1e4..36223bde288 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.LoadHubNode; import org.graalvm.compiler.nodes.extended.SwitchNode; @@ -64,7 +65,7 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si assert successors.length <= keys.length + 1; assert keySuccessors.length == keyProbabilities.length; this.keys = keys; - assert value.stamp() instanceof AbstractPointerStamp; + assert value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp; assert assertKeys(); hubs = new Constant[keys.length]; @@ -123,6 +124,7 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si @Override public void simplify(SimplifierTool tool) { + NodeView view = NodeView.from(tool); if (value() instanceof ConstantNode) { Constant constant = value().asConstant(); @@ -139,8 +141,8 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si } killOtherSuccessors(tool, survivingEdge); } - if (value() instanceof LoadHubNode && ((LoadHubNode) value()).getValue().stamp() instanceof ObjectStamp) { - ObjectStamp objectStamp = (ObjectStamp) ((LoadHubNode) value()).getValue().stamp(); + if (value() instanceof LoadHubNode && ((LoadHubNode) value()).getValue().stamp(view) instanceof ObjectStamp) { + ObjectStamp objectStamp = (ObjectStamp) ((LoadHubNode) value()).getValue().stamp(view); if (objectStamp.type() != null) { int validKeys = 0; for (int i = 0; i < keyCount(); i++) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java index 951201be41f..4bbdb302829 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java @@ -30,6 +30,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; @@ -57,7 +58,7 @@ public final class UnsafeCompareAndSwapNode extends AbstractMemoryCheckpoint imp public UnsafeCompareAndSwapNode(ValueNode object, ValueNode offset, ValueNode expected, ValueNode newValue, JavaKind valueKind, LocationIdentity locationIdentity) { super(TYPE, StampFactory.forKind(JavaKind.Boolean.getStackKind())); - assert expected.stamp().isCompatible(newValue.stamp()); + assert expected.stamp(NodeView.DEFAULT).isCompatible(newValue.stamp(NodeView.DEFAULT)); this.object = object; this.offset = offset; this.expected = expected; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java index 8f8b9536b5b..5125954d93c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java @@ -28,6 +28,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.gen.LIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -46,12 +47,12 @@ public final class ValueCompareAndSwapNode extends AbstractCompareAndSwapNode { } public ValueCompareAndSwapNode(AddressNode address, ValueNode expectedValue, ValueNode newValue, LocationIdentity location, BarrierType barrierType) { - super(TYPE, address, location, expectedValue, newValue, barrierType, expectedValue.stamp().meet(newValue.stamp()).unrestricted()); + super(TYPE, address, location, expectedValue, newValue, barrierType, expectedValue.stamp(NodeView.DEFAULT).meet(newValue.stamp(NodeView.DEFAULT)).unrestricted()); } @Override public void generate(NodeLIRBuilderTool gen) { - assert getNewValue().stamp().isCompatible(getExpectedValue().stamp()); + assert getNewValue().stamp(NodeView.DEFAULT).isCompatible(getExpectedValue().stamp(NodeView.DEFAULT)); LIRGeneratorTool tool = gen.getLIRGeneratorTool(); assert !this.canDeoptimize(); gen.setResult(this, tool.emitValueCompareAndSwap(gen.operand(getAddress()), gen.operand(getExpectedValue()), gen.operand(getNewValue()))); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java index c7aac9dcc52..f277b0ad1f0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java @@ -30,6 +30,8 @@ public interface Access extends GuardedNode, HeapAccess { AddressNode getAddress(); + void setAddress(AddressNode address); + LocationIdentity getLocationIdentity(); boolean canNullCheck(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java index 06b3a7f5d2c..9df482b446b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java @@ -54,6 +54,7 @@ public abstract class FixedAccessNode extends DeoptimizingFixedWithNextNode impl return address; } + @Override public void setAddress(AddressNode address) { updateUsages(this.address, address); this.address = address; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java index 894b6f62691..43dd02ca8d3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java @@ -58,6 +58,12 @@ public abstract class FloatingAccessNode extends FloatingGuardedNode implements return address; } + @Override + public void setAddress(AddressNode address) { + updateUsages(this.address, address); + this.address = address; + } + @Override public LocationIdentity getLocationIdentity() { return location; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java index 4d2705da114..40dc67fd9a6 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNodeUtil; import org.graalvm.compiler.nodes.ValuePhiNode; import org.graalvm.compiler.nodes.extended.GuardingNode; @@ -65,8 +66,8 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow this.lastLocationAccess = lastLocationAccess; // The input to floating reads must be always non-null or have at least a guard. - assert guard != null || !(address.getBase().stamp() instanceof ObjectStamp) || address.getBase() instanceof ValuePhiNode || - ((ObjectStamp) address.getBase().stamp()).nonNull() : address.getBase(); + assert guard != null || !(address.getBase().stamp(NodeView.DEFAULT) instanceof ObjectStamp) || address.getBase() instanceof ValuePhiNode || + ((ObjectStamp) address.getBase().stamp(NodeView.DEFAULT)).nonNull() : address.getBase(); } @Override @@ -82,7 +83,7 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow @Override public void generate(NodeLIRBuilderTool gen) { - LIRKind readKind = gen.getLIRGeneratorTool().getLIRKind(stamp()); + LIRKind readKind = gen.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); gen.setResult(this, gen.getLIRGeneratorTool().getArithmetic().emitLoad(readKind, gen.operand(address), null)); } @@ -106,7 +107,7 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow @Override public FixedAccessNode asFixedNode() { try (DebugCloseable position = withNodeSourcePosition()) { - ReadNode result = graph().add(new ReadNode(getAddress(), getLocationIdentity(), stamp(), getBarrierType())); + ReadNode result = graph().add(new ReadNode(getAddress(), getLocationIdentity(), stamp(NodeView.DEFAULT), getBarrierType())); result.setGuard(getGuard()); return result; } @@ -121,6 +122,6 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow @Override public Stamp getAccessStamp() { - return stamp(); + return stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java index aac2c80c812..fbe26e2653f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CanonicalizableLocation; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.extended.GuardingNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; @@ -94,7 +95,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess, @Override public FloatingAccessNode asFloatingNode(MemoryNode lastLocationAccess) { try (DebugCloseable position = withNodeSourcePosition()) { - return graph().unique(new FloatingReadNode(getAddress(), getLocationIdentity(), lastLocationAccess, stamp(), getGuard(), getBarrierType())); + return graph().unique(new FloatingReadNode(getAddress(), getLocationIdentity(), lastLocationAccess, stamp(NodeView.DEFAULT), getGuard(), getBarrierType())); } } @@ -104,6 +105,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess, } public static ValueNode canonicalizeRead(ValueNode read, AddressNode address, LocationIdentity locationIdentity, CanonicalizerTool tool) { + NodeView view = NodeView.from(tool); MetaAccessProvider metaAccess = tool.getMetaAccess(); if (tool.canonicalizeReads() && address instanceof OffsetAddressNode) { OffsetAddressNode objAddress = (OffsetAddressNode) address; @@ -112,10 +114,10 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess, long displacement = objAddress.getOffset().asJavaConstant().asLong(); int stableDimension = ((ConstantNode) object).getStableDimension(); if (locationIdentity.isImmutable() || stableDimension > 0) { - Constant constant = read.stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), object.asConstant(), displacement); + Constant constant = read.stamp(view).readConstant(tool.getConstantReflection().getMemoryAccessProvider(), object.asConstant(), displacement); boolean isDefaultStable = locationIdentity.isImmutable() || ((ConstantNode) object).isDefaultStable(); if (constant != null && (isDefaultStable || !constant.isDefaultForKind())) { - return ConstantNode.forConstant(read.stamp(), constant, Math.max(stableDimension - 1, 0), isDefaultStable, metaAccess); + return ConstantNode.forConstant(read.stamp(view), constant, Math.max(stableDimension - 1, 0), isDefaultStable, metaAccess); } } } @@ -128,7 +130,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess, if (locationIdentity instanceof CanonicalizableLocation) { CanonicalizableLocation canonicalize = (CanonicalizableLocation) locationIdentity; ValueNode result = canonicalize.canonicalizeRead(read, address, object, tool); - assert result != null && result.stamp().isCompatible(read.stamp()); + assert result != null && result.stamp(view).isCompatible(read.stamp(view)); return result; } @@ -148,6 +150,6 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess, @Override public Stamp getAccessStamp() { - return stamp(); + return stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java index bcce27b1a65..a2ecb533fd8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -52,7 +53,7 @@ public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess, @Override public void generate(NodeLIRBuilderTool gen) { - LIRKind writeKind = gen.getLIRGeneratorTool().getLIRKind(value().stamp()); + LIRKind writeKind = gen.getLIRGeneratorTool().getLIRKind(value().stamp(NodeView.DEFAULT)); gen.getLIRGeneratorTool().getArithmetic().emitStore(writeKind, gen.operand(address), gen.operand(value()), gen.state(this)); } @@ -63,7 +64,7 @@ public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess, @Override public Stamp getAccessStamp() { - return value().stamp(); + return value().stamp(NodeView.DEFAULT); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java index 345bef6cd99..2c73ce4537e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.nodes.memory.address; import org.graalvm.compiler.core.common.type.AbstractPointerStamp; import org.graalvm.compiler.core.common.type.IntegerStamp; +import org.graalvm.compiler.core.common.type.PrimitiveStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; @@ -31,6 +32,8 @@ import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; @@ -54,8 +57,12 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { this.base = base; this.offset = offset; - assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64) && - offset != null && IntegerStamp.getBits(offset.stamp()) == 64 : "both values must have 64 bits"; + assert base != null && (base.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp(NodeView.DEFAULT)) == 64) && + offset != null && IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)) == 64 : "both values must have 64 bits"; + } + + public static OffsetAddressNode create(ValueNode base) { + return new OffsetAddressNode(base, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(base.stamp(NodeView.DEFAULT)), 0)); } @Override @@ -66,7 +73,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { public void setBase(ValueNode base) { updateUsages(this.base, base); this.base = base; - assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64); + assert base != null && (base.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp(NodeView.DEFAULT)) == 64); } public ValueNode getOffset() { @@ -76,18 +83,16 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { public void setOffset(ValueNode offset) { updateUsages(this.offset, offset); this.offset = offset; - assert offset != null && IntegerStamp.getBits(offset.stamp()) == 64; + assert offset != null && IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)) == 64; } @Override public Node canonical(CanonicalizerTool tool) { - if (base instanceof RawAddressNode) { - // The RawAddressNode is redundant, just directly use its input as base. - return new OffsetAddressNode(((RawAddressNode) base).getAddress(), offset); - } else if (base instanceof OffsetAddressNode) { + if (base instanceof OffsetAddressNode) { + NodeView view = NodeView.from(tool); // Rewrite (&base[offset1])[offset2] to base[offset1 + offset2]. OffsetAddressNode b = (OffsetAddressNode) base; - return new OffsetAddressNode(b.getBase(), BinaryArithmeticNode.add(b.getOffset(), this.getOffset())); + return new OffsetAddressNode(b.getBase(), BinaryArithmeticNode.add(b.getOffset(), this.getOffset(), view)); } else if (base instanceof AddNode) { AddNode add = (AddNode) base; if (add.getY().isConstant()) { @@ -102,7 +107,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable { @Override public long getMaxConstantDisplacement() { - Stamp curStamp = offset.stamp(); + Stamp curStamp = offset.stamp(NodeView.DEFAULT); if (curStamp instanceof IntegerStamp) { IntegerStamp integerStamp = (IntegerStamp) curStamp; if (integerStamp.lowerBound() >= 0) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java deleted file mode 100644 index 3a68bf45e04..00000000000 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2015, 2015, 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. - */ -package org.graalvm.compiler.nodes.memory.address; - -import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodeinfo.InputType; -import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.ValueNode; - -/** - * Convert a word-sized integer to a raw address. - */ -@NodeInfo(allowedUsageTypes = InputType.Association) -public class RawAddressNode extends AddressNode { - public static final NodeClass<RawAddressNode> TYPE = NodeClass.create(RawAddressNode.class); - - @Input ValueNode address; - - public RawAddressNode(ValueNode address) { - super(TYPE); - this.address = address; - } - - public ValueNode getAddress() { - return address; - } - - public void setAddress(ValueNode address) { - updateUsages(this.address, address); - this.address = address; - } - - @Override - public ValueNode getBase() { - return address; - } - - @Override - public long getMaxConstantDisplacement() { - return 0; - } - - @Override - public ValueNode getIndex() { - return null; - } -} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java index dec36504dde..027b21f3c99 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.TypeReference; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import jdk.vm.ci.code.CodeUtil; @@ -61,9 +62,9 @@ public class StampTool { ValueNode nextValue = iterator.next(); if (nextValue != selfValue) { if (stamp == null) { - stamp = nextValue.stamp(); + stamp = nextValue.stamp(NodeView.DEFAULT); } else { - stamp = stamp.meet(nextValue.stamp()); + stamp = stamp.meet(nextValue.stamp(NodeView.DEFAULT)); } } } @@ -132,7 +133,7 @@ public class StampTool { * @return true if this node represents a legal object value which is known to be always null */ public static boolean isPointerAlwaysNull(ValueNode node) { - return isPointerAlwaysNull(node.stamp()); + return isPointerAlwaysNull(node.stamp(NodeView.DEFAULT)); } /** @@ -158,7 +159,7 @@ public class StampTool { * @return true if this node represents a legal object value which is known to never be null */ public static boolean isPointerNonNull(ValueNode node) { - return isPointerNonNull(node.stamp()); + return isPointerNonNull(node.stamp(NodeView.DEFAULT)); } /** @@ -184,11 +185,11 @@ public class StampTool { * @return the Java type this value has if it is a legal Object type, null otherwise */ public static TypeReference typeReferenceOrNull(ValueNode node) { - return typeReferenceOrNull(node.stamp()); + return typeReferenceOrNull(node.stamp(NodeView.DEFAULT)); } public static ResolvedJavaType typeOrNull(ValueNode node) { - return typeOrNull(node.stamp()); + return typeOrNull(node.stamp(NodeView.DEFAULT)); } public static ResolvedJavaType typeOrNull(Stamp stamp) { @@ -210,7 +211,7 @@ public class StampTool { } public static ResolvedJavaType typeOrNull(ValueNode node, MetaAccessProvider metaAccess) { - return typeOrNull(node.stamp(), metaAccess); + return typeOrNull(node.stamp(NodeView.DEFAULT), metaAccess); } /** @@ -242,7 +243,7 @@ public class StampTool { * @return true if this node represents a legal object value whose Java type is known exactly */ public static boolean isExactType(ValueNode node) { - return isExactType(node.stamp()); + return isExactType(node.stamp(NodeView.DEFAULT)); } /** diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java index 4990cc79e70..fcb46500de5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java @@ -55,6 +55,7 @@ import org.graalvm.compiler.nodes.GuardNode; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopEndNode; import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ProxyNode; @@ -652,7 +653,7 @@ public class GraphUtil { ValueNode n = node; while (n instanceof PiNode) { PiNode piNode = (PiNode) n; - ObjectStamp originalStamp = (ObjectStamp) piNode.getOriginalNode().stamp(); + ObjectStamp originalStamp = (ObjectStamp) piNode.getOriginalNode().stamp(NodeView.DEFAULT); if (originalStamp.nonNull()) { n = piNode.getOriginalNode(); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java index 0e6839c3120..222dd1d7ed4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodeinfo.NodeSize; import org.graalvm.compiler.nodeinfo.Verbosity; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.AbstractNewObjectNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; @@ -111,7 +112,7 @@ public final class CommitAllocationNode extends FixedWithNextNode implements Vir public void lower(LoweringTool tool) { for (int i = 0; i < virtualObjects.size(); i++) { if (ensureVirtual.get(i)) { - EnsureVirtualizedNode.ensureVirtualFailure(this, virtualObjects.get(i).stamp()); + EnsureVirtualizedNode.ensureVirtualFailure(this, virtualObjects.get(i).stamp(NodeView.DEFAULT)); } } tool.getLowerer().lower(this, tool); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java index a24ccf926fd..13ec603f708 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.nodes.AbstractEndNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.StoreFieldNode; import org.graalvm.compiler.nodes.spi.Lowerable; @@ -76,7 +77,7 @@ public final class EnsureVirtualizedNode extends FixedWithNextNode implements Vi @Override public void lower(LoweringTool tool) { - ensureVirtualFailure(this, object.stamp()); + ensureVirtualFailure(this, object.stamp(NodeView.DEFAULT)); } public static void ensureVirtualFailure(Node location, Stamp stamp) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java index c6e8632e5e7..6c7f3c8ce15 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java @@ -23,10 +23,10 @@ */ package org.graalvm.compiler.phases.common; -import jdk.vm.ci.meta.JavaKind; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PrefetchAllocateNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -36,10 +36,11 @@ import org.graalvm.compiler.nodes.memory.FloatingReadNode; import org.graalvm.compiler.nodes.memory.ReadNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; -import org.graalvm.compiler.nodes.memory.address.RawAddressNode; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.phases.Phase; +import jdk.vm.ci.meta.JavaKind; + /** * Created by adinn on 09/05/17. */ @@ -66,22 +67,22 @@ public class AddressLoweringByUsePhase extends Phase { AddressNode lowered; if (node instanceof ReadNode) { ReadNode readNode = (ReadNode) node; - Stamp stamp = readNode.stamp(); + Stamp stamp = readNode.stamp(NodeView.DEFAULT); address = readNode.getAddress(); lowered = lowering.lower(readNode, stamp, address); } else if (node instanceof JavaReadNode) { JavaReadNode javaReadNode = (JavaReadNode) node; - Stamp stamp = javaReadNode.stamp(); + Stamp stamp = javaReadNode.stamp(NodeView.DEFAULT); address = javaReadNode.getAddress(); lowered = lowering.lower(javaReadNode, stamp, address); } else if (node instanceof FloatingReadNode) { FloatingReadNode floatingReadNode = (FloatingReadNode) node; - Stamp stamp = floatingReadNode.stamp(); + Stamp stamp = floatingReadNode.stamp(NodeView.DEFAULT); address = floatingReadNode.getAddress(); lowered = lowering.lower(floatingReadNode, stamp, address); } else if (node instanceof AbstractWriteNode) { AbstractWriteNode abstractWriteNode = (AbstractWriteNode) node; - Stamp stamp = abstractWriteNode.value().stamp(); + Stamp stamp = abstractWriteNode.value().stamp(NodeView.DEFAULT); address = abstractWriteNode.getAddress(); lowered = lowering.lower(abstractWriteNode, stamp, address); } else if (node instanceof PrefetchAllocateNode) { @@ -108,7 +109,7 @@ public class AddressLoweringByUsePhase extends Phase { // now replace any remaining unlowered address nodes for (Node node : graph.getNodes()) { AddressNode lowered; - if (node instanceof RawAddressNode || node instanceof OffsetAddressNode) { + if (node instanceof OffsetAddressNode) { AddressNode address = (AddressNode) node; lowered = lowering.lower(address); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java index dbdada7b0ce..333fbccb610 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java @@ -27,16 +27,12 @@ import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; -import org.graalvm.compiler.nodes.memory.address.RawAddressNode; import org.graalvm.compiler.nodes.util.GraphUtil; import org.graalvm.compiler.phases.Phase; public class AddressLoweringPhase extends Phase { public abstract static class AddressLowering { - - public abstract AddressNode lower(ValueNode address); - public abstract AddressNode lower(ValueNode base, ValueNode offset); } @@ -51,10 +47,7 @@ public class AddressLoweringPhase extends Phase { protected void run(StructuredGraph graph) { for (Node node : graph.getNodes()) { AddressNode lowered; - if (node instanceof RawAddressNode) { - RawAddressNode address = (RawAddressNode) node; - lowered = lowering.lower(address.getAddress()); - } else if (node instanceof OffsetAddressNode) { + if (node instanceof OffsetAddressNode) { OffsetAddressNode address = (OffsetAddressNode) node; lowered = lowering.lower(address.getBase(), address.getOffset()); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java index 232fd32c383..957984e2242 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java @@ -23,6 +23,7 @@ package org.graalvm.compiler.phases.common; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; +import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.CounterKey; import org.graalvm.compiler.debug.DebugCloseable; import org.graalvm.compiler.debug.DebugContext; @@ -44,6 +45,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.ControlSinkNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StartNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -153,6 +155,10 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> { new Instance(context, workingSet, newNodesMark).apply(graph, dumpGraph); } + public NodeView getNodeView() { + return NodeView.DEFAULT; + } + private final class Instance extends Phase { private final Mark newNodesMark; @@ -260,9 +266,9 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> { if (node instanceof ValueNode) { ValueNode valueNode = (ValueNode) node; boolean improvedStamp = tryInferStamp(valueNode); - Constant constant = valueNode.stamp().asConstant(); + Constant constant = valueNode.stamp(NodeView.DEFAULT).asConstant(); if (constant != null && !(node instanceof ConstantNode)) { - ConstantNode stampConstant = ConstantNode.forConstant(valueNode.stamp(), constant, context.getMetaAccess(), graph); + ConstantNode stampConstant = ConstantNode.forConstant(valueNode.stamp(NodeView.DEFAULT), constant, context.getMetaAccess(), graph); debug.log("Canonicalizer: constant stamp replaces %1s with %1s", valueNode, stampConstant); valueNode.replaceAtUsages(InputType.Value, stampConstant); GraphUtil.tryKillUnused(valueNode); @@ -442,14 +448,16 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> { return false; } - private final class Tool implements SimplifierTool { + private final class Tool implements SimplifierTool, NodeView { private final Assumptions assumptions; private final OptionValues options; + private NodeView nodeView; Tool(Assumptions assumptions, OptionValues options) { this.assumptions = assumptions; this.options = options; + this.nodeView = getNodeView(); } @Override @@ -513,6 +521,11 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> { public OptionValues getOptions() { return options; } + + @Override + public Stamp stamp(ValueNode node) { + return nodeView.stamp(node); + } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java index 3530b938204..0f1c4206191 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java @@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.LoopExitNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ProxyNode; @@ -414,7 +415,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { Stamp bestPossibleStamp = null; for (int i = 0; i < phi.valueCount(); ++i) { ValueNode valueAt = phi.valueAt(i); - Stamp curBestStamp = valueAt.stamp(); + Stamp curBestStamp = valueAt.stamp(NodeView.DEFAULT); InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i)); if (infoElement != null) { curBestStamp = curBestStamp.join(infoElement.getStamp()); @@ -427,7 +428,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { } } - Stamp oldStamp = phi.stamp(); + Stamp oldStamp = phi.stamp(NodeView.DEFAULT); if (oldStamp.tryImproveWith(bestPossibleStamp) != null) { // Need to be careful to not run into stamp update cycles with the iterative @@ -460,7 +461,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { ValuePhiNode newPhi = graph.addWithoutUnique(new ValuePhiNode(bestPossibleStamp, merge)); for (int i = 0; i < phi.valueCount(); ++i) { ValueNode valueAt = phi.valueAt(i); - if (bestPossibleStamp.meet(valueAt.stamp()).equals(bestPossibleStamp)) { + if (bestPossibleStamp.meet(valueAt.stamp(NodeView.DEFAULT)).equals(bestPossibleStamp)) { // Pi not required here. } else { InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i)); @@ -494,7 +495,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { InfoElement infoElement = this.getInfoElements(valueAt); while (infoElement != null) { Stamp newStamp = infoElement.getStamp(); - if (phi.stamp().tryImproveWith(newStamp) != null) { + if (phi.stamp(NodeView.DEFAULT).tryImproveWith(newStamp) != null) { if (mergeMap == null) { mergeMap = EconomicMap.create(); mergeMaps.put(merge, mergeMap); @@ -547,7 +548,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { * It's equivalent to or'ing in the mask value since those values are * known to be set. */ - BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr(); + BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr(); IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(andX), getOtherSafeStamp(y)); registerNewStamp(andX, newStampX, guard); } @@ -580,7 +581,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { if (y.isConstant()) { InfoElement infoElement = getInfoElements(x); while (infoElement != null) { - Stamp result = binary.foldStamp(infoElement.stamp, y.stamp()); + Stamp result = binary.foldStamp(infoElement.stamp, y.stamp(NodeView.DEFAULT)); if (result != null) { return Pair.create(infoElement, result); } @@ -597,7 +598,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { * registered info elements is in the same chain of pi nodes. */ private static Stamp getSafeStamp(ValueNode x) { - return x.stamp(); + return x.stamp(NodeView.DEFAULT); } /** @@ -610,9 +611,9 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { */ private static Stamp getOtherSafeStamp(ValueNode x) { if (x.isConstant()) { - return x.stamp(); + return x.stamp(NodeView.DEFAULT); } - return x.stamp().unrestricted(); + return x.stamp(NodeView.DEFAULT).unrestricted(); } /** @@ -777,7 +778,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { ValueNode y = binaryOpLogicNode.getY(); infoElement = getInfoElements(x); while (infoElement != null) { - TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp()); + TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp(NodeView.DEFAULT)); if (result.isKnown()) { return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction); } @@ -787,7 +788,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { if (y.isConstant()) { Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(x); if (foldResult != null) { - TriState result = binaryOpLogicNode.tryFold(foldResult.getRight(), y.stamp()); + TriState result = binaryOpLogicNode.tryFold(foldResult.getRight(), y.stamp(NodeView.DEFAULT)); if (result.isKnown()) { return rewireGuards(foldResult.getLeft().getGuard(), result.toBoolean(), foldResult.getLeft().getProxifiedInput(), foldResult.getRight(), rewireGuardFunction); } @@ -795,7 +796,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { } else { infoElement = getInfoElements(y); while (infoElement != null) { - TriState result = binaryOpLogicNode.tryFold(x.stamp(), infoElement.getStamp()); + TriState result = binaryOpLogicNode.tryFold(x.stamp(NodeView.DEFAULT), infoElement.getStamp()); if (result.isKnown()) { return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction); } @@ -815,8 +816,8 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { if (binary.getY().isConstant()) { infoElement = getInfoElements(binary.getX()); while (infoElement != null) { - Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp()); - TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp()); + Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp(NodeView.DEFAULT)); + TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp(NodeView.DEFAULT)); if (result.isKnown()) { return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), newStampX, rewireGuardFunction); } @@ -834,7 +835,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { * It's equivalent to or'ing in the mask value since those values are * known to be set. */ - BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr(); + BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr(); IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(and.getX()), getOtherSafeStamp(y)); if (foldPendingTest(thisGuard, and.getX(), newStampX, rewireGuardFunction)) { return true; @@ -899,7 +900,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> { do { counterStampsRegistered.increment(debug); debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, stamp, guard); - assert value instanceof LogicNode || stamp.isCompatible(value.stamp()) : stamp + " vs. " + value.stamp() + " (" + value + ")"; + assert value instanceof LogicNode || stamp.isCompatible(value.stamp(NodeView.DEFAULT)) : stamp + " vs. " + value.stamp(NodeView.DEFAULT) + " (" + value + ")"; map.setAndGrow(value, new InfoElement(stamp, guard, proxiedValue, map.getAndGrow(value))); undoOperations.push(value); if (value instanceof StampInverter) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java index 81caff4a201..889c11cb67e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ShortCircuitOrNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -68,15 +69,15 @@ public class ExpandLogicPhase extends Phase { StructuredGraph graph = normalize.graph(); ValueNode x = normalize.getX(); ValueNode y = normalize.getY(); - if (x.stamp() instanceof FloatStamp) { - equalComp = graph.addOrUniqueWithInputs(FloatEqualsNode.create(x, y)); - lessComp = graph.addOrUniqueWithInputs(FloatLessThanNode.create(x, y, normalize.isUnorderedLess())); + if (x.stamp(NodeView.DEFAULT) instanceof FloatStamp) { + equalComp = graph.addOrUniqueWithInputs(FloatEqualsNode.create(x, y, NodeView.DEFAULT)); + lessComp = graph.addOrUniqueWithInputs(FloatLessThanNode.create(x, y, normalize.isUnorderedLess(), NodeView.DEFAULT)); } else { - equalComp = graph.addOrUniqueWithInputs(IntegerEqualsNode.create(x, y)); - lessComp = graph.addOrUniqueWithInputs(IntegerLessThanNode.create(x, y)); + equalComp = graph.addOrUniqueWithInputs(IntegerEqualsNode.create(x, y, NodeView.DEFAULT)); + lessComp = graph.addOrUniqueWithInputs(IntegerLessThanNode.create(x, y, NodeView.DEFAULT)); } - Stamp stamp = normalize.stamp(); + Stamp stamp = normalize.stamp(NodeView.DEFAULT); ConditionalNode equalValue = graph.unique( new ConditionalNode(equalComp, ConstantNode.forIntegerStamp(stamp, 0, graph), ConstantNode.forIntegerStamp(stamp, 1, graph))); ConditionalNode value = graph.unique(new ConditionalNode(lessComp, ConstantNode.forIntegerStamp(stamp, -1, graph), equalValue)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java index b1379d9b0ca..0d183bdeb0e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java @@ -42,6 +42,7 @@ import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -115,7 +116,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> { replaceCurrent(fixedAccess); } else if (node instanceof PiNode) { PiNode piNode = (PiNode) node; - if (piNode.stamp().isCompatible(piNode.getOriginalNode().stamp())) { + if (piNode.stamp(NodeView.DEFAULT).isCompatible(piNode.getOriginalNode().stamp(NodeView.DEFAULT))) { // Pi nodes are no longer necessary at this point. piNode.replaceAndDelete(piNode.getOriginalNode()); } @@ -178,7 +179,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> { } counterConstantInputReplacements.increment(node.getDebug()); ConstantNode stampConstant = ConstantNode.forConstant(bestStamp, constant, metaAccess, graph); - assert stampConstant.stamp().isCompatible(valueNode.stamp()); + assert stampConstant.stamp(NodeView.DEFAULT).isCompatible(valueNode.stamp(NodeView.DEFAULT)); replaceInput(p, node, stampConstant); replacements++; } @@ -258,7 +259,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> { bestStamp = bestStamp.meet(currentEndMap.get(phi)); } - if (!bestStamp.equals(phi.stamp())) { + if (!bestStamp.equals(phi.stamp(NodeView.DEFAULT))) { endMap.put(phi, bestStamp); } } @@ -293,7 +294,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> { bestStamp = bestStamp.meet(otherEndsStamp); } - if (nodeWithNewStamp.stamp().tryImproveWith(bestStamp) == null) { + if (nodeWithNewStamp.stamp(NodeView.DEFAULT).tryImproveWith(bestStamp) == null) { // No point in registering the stamp. } else { endMap.put(nodeWithNewStamp, bestStamp); @@ -462,7 +463,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> { ValueNode originalNode = value; StampElement currentStamp = stampMap.getAndGrow(originalNode); if (currentStamp == null) { - return value.stamp(); + return value.stamp(NodeView.DEFAULT); } return currentStamp.getStamp(); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java index a25a2b8ad62..e1094687706 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java @@ -25,6 +25,7 @@ package org.graalvm.compiler.phases.common; import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.phases.Phase; @@ -39,8 +40,8 @@ public class NonNullParametersPhase extends Phase { protected void run(StructuredGraph graph) { Stamp nonNull = StampFactory.objectNonNull(); for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { - if (param.stamp() instanceof ObjectStamp) { - ObjectStamp paramStamp = (ObjectStamp) param.stamp(); + if (param.stamp(NodeView.DEFAULT) instanceof ObjectStamp) { + ObjectStamp paramStamp = (ObjectStamp) param.stamp(NodeView.DEFAULT); param.setStamp(paramStamp.join(nonNull)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java index c395bcca3fc..1f632ea2faf 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java @@ -160,7 +160,7 @@ public class ProfileCompiledMethodsPhase extends Phase { return 10; } else if (node instanceof Access) { return 2; - } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof BinaryNode || node instanceof NotNode) { + } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof NotNode) { return 1; } else if (node instanceof IntegerDivRemNode || node instanceof FloatDivNode || node instanceof RemNode) { return 10; @@ -168,7 +168,7 @@ public class ProfileCompiledMethodsPhase extends Phase { return 3; } else if (node instanceof Invoke) { return 5; - } else if (node instanceof IfNode || node instanceof SafepointNode) { + } else if (node instanceof IfNode || node instanceof SafepointNode || node instanceof BinaryNode) { return 1; } else if (node instanceof SwitchNode) { return node.successors().count(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java index 2711f097b43..f0db2b4df29 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java @@ -69,6 +69,7 @@ import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.KillingBeginNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; @@ -827,7 +828,7 @@ public class InliningUtil extends ValueMergeUtil { if (newReceiver.getStackKind() == JavaKind.Object) { if (invoke.getInvokeKind() == InvokeKind.Special) { - Stamp paramStamp = newReceiver.stamp(); + Stamp paramStamp = newReceiver.stamp(NodeView.DEFAULT); Stamp stamp = paramStamp.join(StampFactory.object(TypeReference.create(graph.getAssumptions(), callTarget.targetMethod().getDeclaringClass()))); if (!stamp.equals(paramStamp)) { // The verifier and previous optimizations guarantee unconditionally that the diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java index f01aec572b2..c117547930f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -191,7 +192,7 @@ public class MultiTypeGuardInlineInfo extends AbstractInlineInfo { PhiNode returnValuePhi = null; if (invoke.asNode().getStackKind() != JavaKind.Void) { - returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge)); + returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp(NodeView.DEFAULT).unrestricted(), returnMerge)); } AbstractMergeNode exceptionMerge = null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java index 969458fedbb..1127d5d76fc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.CompareNode; @@ -111,9 +112,9 @@ public class TypeGuardInlineInfo extends AbstractInlineInfo { private void createGuard(StructuredGraph graph, Providers providers) { ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); LoadHubNode receiverHub = graph.unique(new LoadHubNode(providers.getStampProvider(), nonNullReceiver)); - ConstantNode typeHub = ConstantNode.forConstant(receiverHub.stamp(), providers.getConstantReflection().asObjectHub(type), providers.getMetaAccess(), graph); + ConstantNode typeHub = ConstantNode.forConstant(receiverHub.stamp(NodeView.DEFAULT), providers.getConstantReflection().asObjectHub(type), providers.getMetaAccess(), graph); - LogicNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub, providers.getConstantReflection()); + LogicNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub, providers.getConstantReflection(), NodeView.DEFAULT); FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); assert invoke.predecessor() != null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java index 35d076543a6..317a0556b58 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; @@ -120,8 +121,8 @@ public class InlineableGraph implements Inlineable { } private static Stamp improvedStamp(ValueNode arg, ParameterNode param) { - Stamp joinedStamp = param.stamp().join(arg.stamp()); - if (joinedStamp == null || joinedStamp.equals(param.stamp())) { + Stamp joinedStamp = param.stamp(NodeView.DEFAULT).join(arg.stamp(NodeView.DEFAULT)); + if (joinedStamp == null || joinedStamp.equals(param.stamp(NodeView.DEFAULT))) { return null; } return joinedStamp; @@ -162,7 +163,7 @@ public class InlineableGraph implements Inlineable { parameterUsages = trackParameterUsages(param, parameterUsages); // collect param usages before replacing the param param.replaceAtUsagesAndDelete(graph.unique( - ConstantNode.forConstant(arg.stamp(), constant.getValue(), constant.getStableDimension(), constant.isDefaultStable(), context.getMetaAccess()))); + ConstantNode.forConstant(arg.stamp(NodeView.DEFAULT), constant.getValue(), constant.getStableDimension(), constant.isDefaultStable(), context.getMetaAccess()))); // param-node gone, leaving a gap in the sequence given by param.index() } else { Stamp impro = improvedStamp(arg, param); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java index 56475ba11e9..ed6a1208f27 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java @@ -42,6 +42,7 @@ import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -192,10 +193,10 @@ public class InliningData { assert callTarget.invokeKind().isIndirect(); ResolvedJavaType holder = targetMethod.getDeclaringClass(); - if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { + if (!(callTarget.receiver().stamp(NodeView.DEFAULT) instanceof ObjectStamp)) { return null; } - ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); + ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(NodeView.DEFAULT); if (receiverStamp.alwaysNull()) { // Don't inline if receiver is known to be null return null; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java index 1bd1ad5ea20..85281e1a19c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.phases.graph; import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.ValuePhiNode; @@ -50,9 +51,9 @@ public class InferStamps { for (Node n : graph.getNodes()) { if (n instanceof ValuePhiNode) { ValueNode node = (ValueNode) n; - if (node.stamp() instanceof ObjectStamp) { - assert node.stamp().hasValues() : "We assume all Phi and Proxy stamps are legal before the analysis"; - node.setStamp(node.stamp().empty()); + if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) { + assert node.stamp(NodeView.DEFAULT).hasValues() : "We assume all Phi and Proxy stamps are legal before the analysis"; + node.setStamp(node.stamp(NodeView.DEFAULT).empty()); } } } @@ -71,7 +72,7 @@ public class InferStamps { for (Node n : graph.getNodes()) { if (n instanceof ValueNode) { ValueNode node = (ValueNode) n; - if (node.stamp() instanceof ObjectStamp) { + if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) { stampChanged |= node.inferStamp(); } } @@ -90,7 +91,8 @@ public class InferStamps { for (Node n : graph.getNodes()) { if (n instanceof ValuePhiNode) { ValueNode node = (ValueNode) n; - assert node.stamp().hasValues() : "Stamp is empty after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion)."; + assert node.stamp( + NodeView.DEFAULT).hasValues() : "Stamp is empty after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion)."; } } return true; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java index 7ed4b1821f9..8b4d18381cd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.ControlSinkNode; import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.UnwindNode; @@ -52,7 +53,7 @@ public class ValueMergeUtil { singleResult = result; } else if (phiResult == null) { /* Found a second result value, so create phi node. */ - phiResult = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp().unrestricted(), merge)); + phiResult = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp(NodeView.DEFAULT).unrestricted(), merge)); for (int i = 0; i < merge.forwardEndCount(); i++) { phiResult.addInput(singleResult); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java index c2cefd1cfdf..e195ad1875b 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; @@ -234,7 +235,7 @@ public class VerifyDebugUsage extends VerifyPhase<PhaseContext> { protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, ValueNode arg, ResolvedJavaMethod verifiedCallee, Integer dumpLevel) throws org.graalvm.compiler.phases.VerifyPhase.VerificationError { - ResolvedJavaType argType = ((ObjectStamp) arg.stamp()).type(); + ResolvedJavaType argType = ((ObjectStamp) arg.stamp(NodeView.DEFAULT)).type(); if (metaAccess.lookupJavaType(Graph.class).isAssignableFrom(argType)) { verifyStructuredGraphDumping(callerGraph, debugCallTarget, verifiedCallee, dumpLevel); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java index 30563849c2d..e4e14911d82 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.phases.verify; import org.graalvm.compiler.core.common.type.ObjectStamp; import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -80,7 +81,7 @@ public class VerifyUsageWithEquals extends VerifyPhase<PhaseContext> { * Determines whether the type of {@code node} is assignable to the {@link #restrictedClass}. */ private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider metaAccess) { - if (node.stamp() instanceof ObjectStamp) { + if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) { ResolvedJavaType restrictedType = metaAccess.lookupJavaType(restrictedClass); ResolvedJavaType nodeType = StampTool.typeOrNull(node); if (nodeType == null && node instanceof LoadFieldNode) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java index ace130325ce..1b29868ba06 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeInputList; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; @@ -81,7 +82,7 @@ public class VerifyVirtualizableUsage extends VerifyPhase<PhaseContext> { int i = 0; for (Node arg : arguments) { if (i >= startIdx) { - Stamp argStamp = ((ValueNode) arg).stamp(); + Stamp argStamp = ((ValueNode) arg).stamp(NodeView.DEFAULT); if (argStamp instanceof ObjectStamp) { ObjectStamp objectStamp = (ObjectStamp) argStamp; ResolvedJavaType argStampType = objectStamp.type(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java index f40d78c6fc5..d5dc2508341 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java @@ -92,7 +92,7 @@ public class BinaryGraphPrinter implements @Override public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException { - output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, properties); + output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, DebugContext.addVersionProperties(properties)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java index 9d5463b6b37..6568ecc9198 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -48,7 +49,7 @@ public final class AArch64CountLeadingZerosNode extends UnaryNode implements Ari public static final NodeClass<AArch64CountLeadingZerosNode> TYPE = NodeClass.create(AArch64CountLeadingZerosNode.class); public AArch64CountLeadingZerosNode(ValueNode value) { - super(TYPE, computeStamp(value.stamp(), value), value); + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value); } @Override @@ -57,7 +58,7 @@ public final class AArch64CountLeadingZerosNode extends UnaryNode implements Ari } private static Stamp computeStamp(Stamp newStamp, ValueNode theValue) { - assert newStamp.isCompatible(theValue.stamp()); + assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT)); assert theValue.getStackKind() == JavaKind.Int || theValue.getStackKind() == JavaKind.Long; return StampTool.stampForLeadingZeros((IntegerStamp) newStamp); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java index ac7d674b0e1..54e7e653b6f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -50,7 +51,7 @@ public final class AArch64CountTrailingZerosNode extends UnaryNode implements Ar public static final NodeClass<AArch64CountTrailingZerosNode> TYPE = NodeClass.create(AArch64CountTrailingZerosNode.class); public AArch64CountTrailingZerosNode(ValueNode value) { - super(TYPE, computeStamp(value.stamp(), value), value); + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @@ -60,7 +61,7 @@ public final class AArch64CountTrailingZerosNode extends UnaryNode implements Ar } static Stamp computeStamp(Stamp newStamp, ValueNode value) { - assert newStamp.isCompatible(value.stamp()); + assert newStamp.isCompatible(value.stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; return StampTool.stampForTrailingZeros(valueStamp); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java index 339af5d5dc3..ad5dbccd87f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.graph.Node.NodeIntrinsic; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.RemNode; @@ -63,7 +64,7 @@ public class AArch64FloatArithmeticSnippets extends SnippetTemplate.AbstractTemp } public void lower(RemNode node, LoweringTool tool) { - JavaKind kind = node.stamp().getStackKind(); + JavaKind kind = node.stamp(NodeView.DEFAULT).getStackKind(); assert kind == JavaKind.Float || kind == JavaKind.Double; if (node instanceof SafeNode) { // We already introduced the necessary checks, nothing to do. diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java index b45c2e4272f..cf6ba186a14 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.Node.NodeIntrinsic; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FixedBinaryNode; @@ -84,7 +85,7 @@ public class AArch64IntegerArithmeticSnippets extends AbstractTemplates implemen } public void lower(FixedBinaryNode node, LoweringTool tool) { - JavaKind kind = node.stamp().getStackKind(); + JavaKind kind = node.stamp(NodeView.DEFAULT).getStackKind(); assert kind == JavaKind.Int || kind == JavaKind.Long; SnippetTemplate.SnippetInfo snippet; if (node instanceof SafeNode) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java index a9eec477b0c..1baec57dac2 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.lir.aarch64.AArch64AddressValue; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.FrameState; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.SignExtendNode; @@ -66,7 +67,7 @@ public class AArch64ReadNode extends ReadNode { AArch64LIRGenerator lirgen = (AArch64LIRGenerator) gen.getLIRGeneratorTool(); AArch64ArithmeticLIRGenerator arithgen = (AArch64ArithmeticLIRGenerator) lirgen.getArithmetic(); AArch64Kind readKind = (AArch64Kind) lirgen.getLIRKind(accessStamp).getPlatformKind(); - int resultBits = ((IntegerStamp) stamp()).getBits(); + int resultBits = ((IntegerStamp) stamp(NodeView.DEFAULT)).getBits(); gen.setResult(this, arithgen.emitExtendMemory(isSigned, readKind, resultBits, (AArch64AddressValue) gen.operand(getAddress()), gen.state(this))); } @@ -86,7 +87,7 @@ public class AArch64ReadNode extends ReadNode { AddressNode address = readNode.getAddress(); LocationIdentity location = readNode.getLocationIdentity(); - Stamp stamp = usage.stamp(); + Stamp stamp = usage.stamp(NodeView.DEFAULT); GuardingNode guard = readNode.getGuard(); BarrierType barrierType = readNode.getBarrierType(); boolean nullCheck = readNode.getNullCheck(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java index 56938d2c930..160ba55bba8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -50,7 +51,7 @@ public final class AMD64CountLeadingZerosNode extends UnaryNode implements Arith public static final NodeClass<AMD64CountLeadingZerosNode> TYPE = NodeClass.create(AMD64CountLeadingZerosNode.class); public AMD64CountLeadingZerosNode(ValueNode value) { - super(TYPE, computeStamp(value.stamp(), value), value); + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @@ -60,7 +61,7 @@ public final class AMD64CountLeadingZerosNode extends UnaryNode implements Arith } private static Stamp computeStamp(Stamp newStamp, ValueNode theValue) { - assert newStamp.isCompatible(theValue.stamp()); + assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT)); assert theValue.getStackKind() == JavaKind.Int || theValue.getStackKind() == JavaKind.Long; return StampTool.stampForLeadingZeros((IntegerStamp) newStamp); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java index 3c17918d5bf..d9314d4948f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -50,7 +51,7 @@ public final class AMD64CountTrailingZerosNode extends UnaryNode implements Arit public static final NodeClass<AMD64CountTrailingZerosNode> TYPE = NodeClass.create(AMD64CountTrailingZerosNode.class); public AMD64CountTrailingZerosNode(ValueNode value) { - super(TYPE, computeStamp(value.stamp(), value), value); + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @@ -60,7 +61,7 @@ public final class AMD64CountTrailingZerosNode extends UnaryNode implements Arit } static Stamp computeStamp(Stamp newStamp, ValueNode value) { - assert newStamp.isCompatible(value.stamp()); + assert newStamp.isCompatible(value.stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; return StampTool.stampForTrailingZeros(valueStamp); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java index 4a980b0cbb7..54674b55e53 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMo import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -52,7 +53,7 @@ public final class AMD64RoundNode extends UnaryNode implements ArithmeticLIRLowe private final RoundingMode mode; public AMD64RoundNode(ValueNode value, RoundingMode mode) { - super(TYPE, roundStamp((FloatStamp) value.stamp(), mode), value); + super(TYPE, roundStamp((FloatStamp) value.stamp(NodeView.DEFAULT), mode), value); this.mode = mode; } @@ -83,7 +84,7 @@ public final class AMD64RoundNode extends UnaryNode implements ArithmeticLIRLowe @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); return roundStamp((FloatStamp) newStamp, mode); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java index 2dd8252aeb9..2aeda14b2ed 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java @@ -22,6 +22,7 @@ */ package org.graalvm.compiler.replacements.test; +import org.graalvm.compiler.nodes.NodeView; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; @@ -83,7 +84,7 @@ public class BitOpNodesTest extends GraalCompilerTest { boolean isSparc = arch instanceof SPARC; Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc); ValueNode result = parseAndInline("bitCountIntSnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp(NodeView.DEFAULT)); } public static int bitCountIntEmptySnippet(int v) { @@ -97,7 +98,7 @@ public class BitOpNodesTest extends GraalCompilerTest { boolean isSparc = arch instanceof SPARC; Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc); ValueNode result = parseAndInline("bitCountIntEmptySnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp(NodeView.DEFAULT)); } @Test @@ -117,7 +118,7 @@ public class BitOpNodesTest extends GraalCompilerTest { boolean isSparc = arch instanceof SPARC; Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc); ValueNode result = parseAndInline("bitCountLongSnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp(NodeView.DEFAULT)); } public static int bitCountLongEmptySnippet(long v) { @@ -131,7 +132,7 @@ public class BitOpNodesTest extends GraalCompilerTest { boolean isSparc = arch instanceof SPARC; Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc); ValueNode result = parseAndInline("bitCountLongEmptySnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp(NodeView.DEFAULT)); } /* @@ -155,7 +156,7 @@ public class BitOpNodesTest extends GraalCompilerTest { @Test public void testScanForwardInt() { ValueNode result = parseAndInline("scanForwardIntSnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp(NodeView.DEFAULT)); } public static int scanForwardLongConstantSnippet() { @@ -175,7 +176,7 @@ public class BitOpNodesTest extends GraalCompilerTest { @Test public void testScanForwardLong() { ValueNode result = parseAndInline("scanForwardLongSnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp(NodeView.DEFAULT)); } public static int scanForwardLongEmptySnippet(long v) { @@ -187,7 +188,7 @@ public class BitOpNodesTest extends GraalCompilerTest { @Test public void testScanForwardLongEmpty() { ValueNode result = parseAndInline("scanForwardLongEmptySnippet"); - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT)); } /* @@ -213,7 +214,7 @@ public class BitOpNodesTest extends GraalCompilerTest { /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */ ValueNode result = parseAndInline("scanReverseIntSnippet", BitScanReverseNode.class); if (result != null) { - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp(NodeView.DEFAULT)); } } @@ -238,7 +239,7 @@ public class BitOpNodesTest extends GraalCompilerTest { /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */ ValueNode result = parseAndInline("scanReverseLongSnippet", BitScanReverseNode.class); if (result != null) { - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp(NodeView.DEFAULT)); } } @@ -253,7 +254,7 @@ public class BitOpNodesTest extends GraalCompilerTest { /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */ ValueNode result = parseAndInline("scanReverseLongEmptySnippet", BitScanReverseNode.class); if (result != null) { - Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp()); + Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT)); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java index c833aec2258..1ab1abb7052 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ReturnNode; @@ -95,7 +96,7 @@ public class IntegerExactFoldTest extends GraalCompilerTest { ValueNode node = findNode(graph); boolean overflowExpected = node instanceof IntegerExactArithmeticNode; - IntegerStamp resultStamp = (IntegerStamp) node.stamp(); + IntegerStamp resultStamp = (IntegerStamp) node.stamp(NodeView.DEFAULT); operation.verifyOverflow(lowerBoundA, upperBoundA, lowerBoundB, upperBoundB, bits, overflowExpected, resultStamp); } @@ -120,7 +121,7 @@ public class IntegerExactFoldTest extends GraalCompilerTest { ValueNode node = findNode(graph); boolean overflowExpected = node instanceof IntegerExactArithmeticSplitNode; - IntegerStamp resultStamp = (IntegerStamp) node.stamp(); + IntegerStamp resultStamp = (IntegerStamp) node.stamp(NodeView.DEFAULT); operation.verifyOverflow(lowerBoundA, upperBoundA, lowerBoundB, upperBoundB, bits, overflowExpected, resultStamp); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java index cc02a21f970..f99d7adb323 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; +import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.spi.LoweringTool; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; @@ -50,8 +51,12 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; */ public abstract class MethodSubstitutionTest extends GraalCompilerTest { - @SuppressWarnings("try") protected StructuredGraph testGraph(final String snippet) { + return testGraph(snippet, null); + } + + @SuppressWarnings("try") + protected StructuredGraph testGraph(final String snippet, String name) { DebugContext debug = getDebugContext(); try (DebugContext.Scope s = debug.scope("MethodSubstitutionTest", getResolvedJavaMethod(snippet))) { StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug); @@ -69,7 +74,20 @@ public abstract class MethodSubstitutionTest extends GraalCompilerTest { new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context); } assertNotInGraph(graph, MacroNode.class); - assertNotInGraph(graph, Invoke.class); + if (name != null) { + for (Node node : graph.getNodes()) { + if (node instanceof Invoke) { + Invoke invoke = (Invoke) node; + if (invoke.callTarget() instanceof MethodCallTargetNode) { + MethodCallTargetNode call = (MethodCallTargetNode) invoke.callTarget(); + assertTrue(!call.targetMethod().getName().equals(name), "Unexpected invoke of intrinsic %s", call.targetMethod()); + } + } + + } + } else { + assertNotInGraph(graph, Invoke.class); + } return graph; } catch (Throwable e) { throw debug.handle(e); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java index 090470f0c59..fd20ab92a7d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java @@ -221,7 +221,8 @@ public class MonitorTest extends GraalCompilerTest { * Reproduces issue reported in https://github.com/graalvm/graal-core/issues/201. The stamp in * the PiNode returned by {@link BoxingSnippets#longValueOf} was overwritten when the node was * subsequently canonicalized because {@code PiNode.computeValue()} ignored the - * {@link ValueNode#stamp()} field and used the {@code PiNode.piStamp} field. + * {@link ValueNode#stamp(org.graalvm.compiler.nodes.NodeView)} field and used the + * {@code PiNode.piStamp} field. */ @Test public void test8() { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java index 95e0f45c7e2..7eb29a16c78 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.replacements.test; import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; @@ -93,7 +94,7 @@ public class ObjectAccessTest extends SnippetsTest { private static void assertRead(StructuredGraph graph, JavaKind kind, boolean indexConvert, LocationIdentity locationIdentity) { JavaReadNode read = (JavaReadNode) graph.start().next(); - Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind()); + Assert.assertEquals(kind.getStackKind(), read.stamp(NodeView.DEFAULT).getStackKind()); OffsetAddressNode address = (OffsetAddressNode) read.getAddress(); Assert.assertEquals(graph.getParameter(0), address.getBase()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java index 9ebfb868398..48d6f19f8c3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java @@ -24,6 +24,7 @@ package org.graalvm.compiler.replacements.test; import org.graalvm.compiler.api.replacements.Snippet; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; @@ -105,12 +106,12 @@ public class PointerTest extends SnippetsTest { WordCastNode cast = (WordCastNode) graph.start().next(); JavaReadNode read = (JavaReadNode) cast.next(); - Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind()); + Assert.assertEquals(kind.getStackKind(), read.stamp(NodeView.DEFAULT).getStackKind()); OffsetAddressNode address = (OffsetAddressNode) read.getAddress(); Assert.assertEquals(cast, address.getBase()); Assert.assertEquals(graph.getParameter(0), cast.getInput()); - Assert.assertEquals(target.wordJavaKind, cast.stamp().getStackKind()); + Assert.assertEquals(target.wordJavaKind, cast.stamp(NodeView.DEFAULT).getStackKind()); Assert.assertEquals(locationIdentity, read.getLocationIdentity()); @@ -137,7 +138,7 @@ public class PointerTest extends SnippetsTest { OffsetAddressNode address = (OffsetAddressNode) write.getAddress(); Assert.assertEquals(cast, address.getBase()); Assert.assertEquals(graph.getParameter(0), cast.getInput()); - Assert.assertEquals(target.wordJavaKind, cast.stamp().getStackKind()); + Assert.assertEquals(target.wordJavaKind, cast.stamp(NodeView.DEFAULT).getStackKind()); Assert.assertEquals(locationIdentity, write.getLocationIdentity()); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java new file mode 100644 index 00000000000..ee74c619b04 --- /dev/null +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, 2014, 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. + */ +package org.graalvm.compiler.replacements.test; + +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.graalvm.compiler.core.test.GraalCompilerTest; +import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.options.OptionValues; +import org.graalvm.compiler.phases.PhaseSuite; +import org.graalvm.compiler.phases.tiers.HighTierContext; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.Collection; + +import static java.lang.reflect.Modifier.isStatic; + +@RunWith(Parameterized.class) +public class SystemArrayCopyTest extends GraalCompilerTest { + + @Parameter(0) public Object src; + @Parameter(1) public Object dst; + @Parameter(2) public int len; + @Parameter(3) public String name; + + @Parameters(name = "{3}") + public static Collection<Object[]> data() { + Object[] srcs = {new int[4], new double[4], new Integer[4], new Number[4], new String[4], new Object[]{"Graal", 0, 0, 0}, new Object()}; + Object[] dsts = {new int[4], new Number[4]}; + int[] lens = {-1, 0, 2, 8}; + + ArrayList<Object[]> ret = new ArrayList<>(srcs.length * dsts.length * lens.length); + for (Object src : srcs) { + for (Object dst : dsts) { + for (int length : lens) { + ret.add(new Object[]{src, dst, length, src.getClass().getSimpleName() + ", 0, " + dst.getClass().getSimpleName() + ", 0, " + length}); + } + } + } + return ret; + } + + public static void testArrayCopySnippet(Object src, Object dst, int length) { + System.arraycopy(src, 0, dst, 0, length); + } + + private static final int PARAMETER_LENGTH = 3; + private Object[] argsToBind; + + @Test + public void testArrayCopy() { + ResolvedJavaMethod method = getResolvedJavaMethod("testArrayCopySnippet"); + Object receiver = method.isStatic() ? null : this; + Object[] args = {src, dst, len}; + + Result expect = executeExpected(method, receiver, args); + testAgainstExpected(method, expect, receiver, args); + + // test composition of constant binding + for (int i = 1; i < (1 << PARAMETER_LENGTH); i++) { + argsToBind = new Object[PARAMETER_LENGTH]; + for (int j = 0; j < PARAMETER_LENGTH; j++) { + if ((i & (1 << j)) != 0) { + argsToBind[j] = args[j]; + } + } + testAgainstExpected(method, expect, receiver, args); + } + } + + @Override + protected StructuredGraph parse(StructuredGraph.Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) { + StructuredGraph graph = super.parse(builder, graphBuilderSuite); + if (argsToBind != null) { + ResolvedJavaMethod m = graph.method(); + Object receiver = isStatic(m.getModifiers()) ? null : this; + Object[] args = argsWithReceiver(receiver, argsToBind); + JavaType[] parameterTypes = m.toParameterTypes(); + assert parameterTypes.length == args.length; + for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) { + int index = param.index(); + if (args[index] != null) { + JavaConstant c = getSnippetReflection().forBoxed(parameterTypes[index].getJavaKind(), args[index]); + ConstantNode replacement = ConstantNode.forConstant(c, getMetaAccess(), graph); + param.replaceAtUsages(replacement); + } + } + } + return graph; + } + + @Override + protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) { + return super.getCode(method, graph, true, installAsDefault, options); + } + +} diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java index dd0746320f3..4bfcadde273 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java @@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.FieldLocationIdentity; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -113,7 +114,6 @@ import org.graalvm.compiler.nodes.memory.ReadNode; import org.graalvm.compiler.nodes.memory.WriteNode; import org.graalvm.compiler.nodes.memory.address.AddressNode; import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode; -import org.graalvm.compiler.nodes.memory.address.RawAddressNode; import org.graalvm.compiler.nodes.spi.Lowerable; import org.graalvm.compiler.nodes.spi.LoweringProvider; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -247,7 +247,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { indexOfSnippets.lower(node, tool); } }; - SnippetLowerableMemoryNode snippetLower = new SnippetLowerableMemoryNode(lowering, NamedLocationIdentity.getArrayLocation(JavaKind.Char), n.stamp(), n.toArgumentArray()); + SnippetLowerableMemoryNode snippetLower = new SnippetLowerableMemoryNode(lowering, NamedLocationIdentity.getArrayLocation(JavaKind.Char), n.stamp(NodeView.DEFAULT), n.toArgumentArray()); n.graph().add(snippetLower); n.graph().replaceFixedWithFixed(n, snippetLower); } @@ -347,7 +347,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { ResolvedJavaField field = loadField.field(); ValueNode object = loadField.isStatic() ? staticFieldBase(graph, field) : loadField.object(); object = createNullCheckedValue(object, loadField, tool); - Stamp loadStamp = loadStamp(loadField.stamp(), getStorageKind(field)); + Stamp loadStamp = loadStamp(loadField.stamp(NodeView.DEFAULT), getStorageKind(field)); AddressNode address = createFieldAddress(graph, object, field); assert address != null : "Field that is loaded must not be eliminated: " + field.getDeclaringClass().toJavaName(true) + "." + field.getName(); @@ -419,7 +419,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { ValueNode array = loadIndexed.array(); array = createNullCheckedValue(array, loadIndexed, tool); JavaKind elementKind = loadIndexed.elementKind(); - Stamp loadStamp = loadStamp(loadIndexed.stamp(), elementKind); + Stamp loadStamp = loadStamp(loadIndexed.stamp(NodeView.DEFAULT), elementKind); GuardingNode boundsCheck = getBoundsCheck(loadIndexed, array, tool); AddressNode address = createArrayIndexAddress(graph, array, elementKind, loadIndexed.index(), boundsCheck); @@ -575,7 +575,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { protected AddressNode createUnsafeAddress(StructuredGraph graph, ValueNode object, ValueNode offset) { if (object.isConstant() && object.asConstant().isDefaultForKind()) { - return graph.unique(new RawAddressNode(offset)); + return graph.addOrUniqueWithInputs(OffsetAddressNode.create(offset)); } else { return graph.unique(new OffsetAddressNode(object, offset)); } @@ -584,7 +584,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { protected ReadNode createUnsafeRead(StructuredGraph graph, RawLoadNode load, GuardingNode guard) { boolean compressible = load.accessKind() == JavaKind.Object; JavaKind readKind = load.accessKind(); - Stamp loadStamp = loadStamp(load.stamp(), readKind, compressible); + Stamp loadStamp = loadStamp(load.stamp(NodeView.DEFAULT), readKind, compressible); AddressNode address = createUnsafeAddress(graph, load.object(), load.offset()); ReadNode memoryRead = graph.add(new ReadNode(address, load.getLocationIdentity(), loadStamp, BarrierType.NONE)); if (guard == null) { @@ -603,8 +603,8 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { StructuredGraph graph = load.graph(); JavaKind readKind = load.getKind(); assert readKind != JavaKind.Object; - Stamp loadStamp = loadStamp(load.stamp(), readKind, false); - AddressNode address = graph.unique(new RawAddressNode(load.getAddress())); + Stamp loadStamp = loadStamp(load.stamp(NodeView.DEFAULT), readKind, false); + AddressNode address = graph.addOrUniqueWithInputs(OffsetAddressNode.create(load.getAddress())); ReadNode memoryRead = graph.add(new ReadNode(address, load.getLocationIdentity(), loadStamp, BarrierType.NONE)); // An unsafe read must not float otherwise it may float above // a test guaranteeing the read is safe. @@ -639,7 +639,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { assert store.getValue().getStackKind() != JavaKind.Object; JavaKind valueKind = store.getKind(); ValueNode value = implicitStoreConvert(graph, valueKind, store.getValue(), false); - AddressNode address = graph.unique(new RawAddressNode(store.getAddress())); + AddressNode address = graph.addOrUniqueWithInputs(OffsetAddressNode.create(store.getAddress())); WriteNode write = graph.add(new WriteNode(address, store.getLocationIdentity(), value, BarrierType.NONE)); write.setStateAfter(store.stateAfter()); graph.replaceFixedWithFixed(store, write); @@ -648,7 +648,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { protected void lowerJavaReadNode(JavaReadNode read) { StructuredGraph graph = read.graph(); JavaKind valueKind = read.getReadKind(); - Stamp loadStamp = loadStamp(read.stamp(), valueKind, read.isCompressible()); + Stamp loadStamp = loadStamp(read.stamp(NodeView.DEFAULT), valueKind, read.isCompressible()); ReadNode memoryRead = graph.add(new ReadNode(read.getAddress(), read.getLocationIdentity(), loadStamp, read.getBarrierType())); GuardingNode guard = read.getGuard(); @@ -1041,7 +1041,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { arrayLength = arrayLength.isAlive() ? arrayLength : graph.addOrUniqueWithInputs(arrayLength); } - LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength); + LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength, NodeView.DEFAULT); if (boundsCheck.isTautology()) { return null; } @@ -1060,7 +1060,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { if (nullCheck == null) { return object; } - return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck)); + return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp(NodeView.DEFAULT)).join(StampFactory.objectNonNull()), (ValueNode) nullCheck)); } @Override @@ -1069,10 +1069,10 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider { ValueNode offset = ((OffsetAddressNode) address).getOffset(); int base = arrayBaseOffset(elementKind); - ValueNode scaledIndex = graph.unique(new SubNode(offset, ConstantNode.forIntegerStamp(offset.stamp(), base, graph))); + ValueNode scaledIndex = graph.unique(new SubNode(offset, ConstantNode.forIntegerStamp(offset.stamp(NodeView.DEFAULT), base, graph))); int shift = CodeUtil.log2(arrayScalingFactor(elementKind)); ValueNode ret = graph.unique(new RightShiftNode(scaledIndex, ConstantNode.forInt(shift, graph))); - return IntegerConvertNode.convert(ret, StampFactory.forKind(JavaKind.Int), graph); + return IntegerConvertNode.convert(ret, StampFactory.forKind(JavaKind.Int), graph, NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java index 99d6ff7d413..6ccd8d4f91d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java @@ -50,6 +50,7 @@ import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.KillingBeginNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; @@ -271,12 +272,12 @@ public class GraphKit implements GraphBuilderTool { int argIndex = 0; if (!isStatic) { JavaKind expected = asKind(method.getDeclaringClass()); - JavaKind actual = args[argIndex++].stamp().getStackKind(); + JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind(); assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]"; } for (int i = 0; i != signature.getParameterCount(false); i++) { JavaKind expected = asKind(signature.getParameterType(i, method.getDeclaringClass())).getStackKind(); - JavaKind actual = args[argIndex++].stamp().getStackKind(); + JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind(); if (expected != actual) { throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]"); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java index bea1cd94df3..0f0440ddaf0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ShortCircuitOrNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -183,7 +184,7 @@ public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates { } if (condition == null || (!(condition instanceof CompareNode)) || ((CompareNode) condition).getY() != testValue) { // Re-use previously generated condition if the trueValue for the test is the same - condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null); + condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null, NodeView.DEFAULT); } return condition; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java index b7027451717..48ebe3e562e 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java @@ -83,6 +83,10 @@ public class MethodHandlePlugin implements NodePlugin { // As such, it needs to recursively inline everything. inlineEverything = args.length != argumentsList.size(); } + if (inlineEverything && !callTarget.targetMethod().hasBytecodes()) { + // we need to force-inline but we can not, leave the invoke as-is + return false; + } b.handleReplacedInvoke(invoke.getInvokeKind(), callTarget.targetMethod(), argumentsList.toArray(new ValueNode[argumentsList.size()]), inlineEverything); } return true; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java index 1509b364545..aa52ee525d8 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java @@ -63,6 +63,7 @@ import org.graalvm.compiler.nodes.IfNode; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.ReturnNode; import org.graalvm.compiler.nodes.SimplifyingGraphDecoder; @@ -583,7 +584,7 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder { return callTarget.targetMethod(); } - SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp()); + SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp(NodeView.DEFAULT)); Object specialCallTarget = specialCallTargetCache.get(key); if (specialCallTarget == null) { specialCallTarget = MethodCallTargetNode.devirtualizeCall(key.invokeKind, key.targetMethod, key.contextType, graph.getAssumptions(), @@ -1066,7 +1067,7 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder { assert !methodScope.isInlinedMethod(); GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null); Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(), - StampPair.create(param.stamp(), param.uncheckedStamp())); + StampPair.create(param.stamp(NodeView.DEFAULT), param.uncheckedStamp())); if (result != null) { return result; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java index 067fde637bd..45258eac938 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java @@ -83,6 +83,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.MergeNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ParameterNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode.Placeholder; @@ -750,7 +751,7 @@ public class SnippetTemplate { nodeReplacements.put(parameter, placeholder); placeholders[i] = placeholder; } else if (args.info.isNonNullParameter(i)) { - parameter.setStamp(parameter.stamp().join(StampFactory.objectNonNull())); + parameter.setStamp(parameter.stamp(NodeView.DEFAULT).join(StampFactory.objectNonNull())); } } } @@ -785,7 +786,8 @@ public class SnippetTemplate { if (usage instanceof LoadIndexedNode) { LoadIndexedNode loadIndexed = (LoadIndexedNode) usage; debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed); - LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp())); + LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add( + new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp(NodeView.DEFAULT))); snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter); debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed); } else if (usage instanceof StoreIndexedNode) { @@ -829,7 +831,7 @@ public class SnippetTemplate { for (Node node : snippetCopy.getNodes()) { if (node instanceof ValueNode) { ValueNode valueNode = (ValueNode) node; - if (valueNode.stamp() == PlaceholderStamp.singleton()) { + if (valueNode.stamp(NodeView.DEFAULT) == PlaceholderStamp.singleton()) { curPlaceholderStampedNodes.add(valueNode); } } @@ -1502,7 +1504,7 @@ public class SnippetTemplate { private void updateStamps(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) { for (ValueNode node : placeholderStampedNodes) { ValueNode dup = (ValueNode) duplicates.get(node); - Stamp replaceeStamp = replacee.stamp(); + Stamp replaceeStamp = replacee.stamp(NodeView.DEFAULT); if (node instanceof Placeholder) { Placeholder placeholderDup = (Placeholder) dup; placeholderDup.makeReplacement(replaceeStamp); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java index 9bd73d2d322..ba074f3a9c4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java @@ -54,6 +54,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.FixedGuardNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AbsNode; @@ -296,14 +297,14 @@ public class StandardGraphBuilderPlugins { r.register2("divideUnsigned", type, type, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) { - b.push(kind, b.append(new UnsignedDivNode(dividend, divisor).canonical(null))); + b.push(kind, b.append(UnsignedDivNode.create(dividend, divisor, NodeView.DEFAULT))); return true; } }); r.register2("remainderUnsigned", type, type, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) { - b.push(kind, b.append(new UnsignedRemNode(dividend, divisor).canonical(null))); + b.push(kind, b.append(UnsignedRemNode.create(dividend, divisor, NodeView.DEFAULT))); return true; } }); @@ -344,14 +345,14 @@ public class StandardGraphBuilderPlugins { r.register1("floatToRawIntBits", float.class, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - b.push(JavaKind.Int, b.append(new ReinterpretNode(JavaKind.Int, value).canonical(null))); + b.push(JavaKind.Int, b.append(ReinterpretNode.create(JavaKind.Int, value, NodeView.DEFAULT))); return true; } }); r.register1("intBitsToFloat", int.class, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - b.push(JavaKind.Float, b.append(new ReinterpretNode(JavaKind.Float, value).canonical(null))); + b.push(JavaKind.Float, b.append(ReinterpretNode.create(JavaKind.Float, value, NodeView.DEFAULT))); return true; } }); @@ -362,14 +363,14 @@ public class StandardGraphBuilderPlugins { r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - b.push(JavaKind.Long, b.append(new ReinterpretNode(JavaKind.Long, value).canonical(null))); + b.push(JavaKind.Long, b.append(ReinterpretNode.create(JavaKind.Long, value, NodeView.DEFAULT))); return true; } }); r.register1("longBitsToDouble", long.class, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - b.push(JavaKind.Double, b.append(new ReinterpretNode(JavaKind.Double, value).canonical(null))); + b.push(JavaKind.Double, b.append(ReinterpretNode.create(JavaKind.Double, value, NodeView.DEFAULT))); return true; } }); @@ -421,7 +422,7 @@ public class StandardGraphBuilderPlugins { r.register1("sqrt", Double.TYPE, new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) { - b.push(JavaKind.Double, b.append(new SqrtNode(value).canonical(null))); + b.push(JavaKind.Double, b.append(SqrtNode.create(value, NodeView.DEFAULT))); return true; } }); @@ -470,7 +471,7 @@ public class StandardGraphBuilderPlugins { cond = cond.negate(); } - LogicNode compare = CompareNode.createCompareNode(graph, b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, cond, lhs, rhs); + LogicNode compare = CompareNode.createCompareNode(graph, b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, cond, lhs, rhs, NodeView.DEFAULT); b.addPush(JavaKind.Boolean, new ConditionalNode(compare, trueValue, falseValue)); return true; } @@ -521,7 +522,7 @@ public class StandardGraphBuilderPlugins { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { ValueNode object = receiver.get(); - ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), GraphUtil.originalValue(object)); + ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), NodeView.DEFAULT, GraphUtil.originalValue(object)); if (folded != null) { b.addPush(JavaKind.Object, folded); } else { @@ -892,7 +893,8 @@ public class StandardGraphBuilderPlugins { } else if (falseCount == 0 || trueCount == 0) { boolean expected = falseCount == 0; LogicNode condition = b.addWithInputs( - IntegerEqualsNode.create(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, result, b.add(ConstantNode.forBoolean(!expected)))); + IntegerEqualsNode.create(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, result, b.add(ConstantNode.forBoolean(!expected)), + NodeView.DEFAULT)); b.append(new FixedGuardNode(condition, DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, true)); newResult = b.add(ConstantNode.forBoolean(expected)); } else { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java index a08ff3182cc..9f62f4d66bb 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.nodeinfo.NodeSize; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.ValueNodeUtil; import org.graalvm.compiler.nodes.memory.MemoryAccess; @@ -173,7 +174,7 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower allEqual = false; } } - if (entry1.stamp().alwaysDistinct(entry2.stamp())) { + if (entry1.stamp(NodeView.DEFAULT).alwaysDistinct(entry2.stamp(NodeView.DEFAULT))) { // the contents are different tool.replaceWithValue(ConstantNode.forBoolean(false, graph())); return; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java index 0c5e6a17724..a8c9eca51f7 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java @@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.DeoptimizingNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.LoadIndexedNode; import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint; @@ -164,8 +165,8 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt * Returns true if this copy doesn't require store checks. Trivially true for primitive arrays. */ public boolean isExact() { - ResolvedJavaType srcType = StampTool.typeOrNull(getSource().stamp()); - ResolvedJavaType destType = StampTool.typeOrNull(getDestination().stamp()); + ResolvedJavaType srcType = StampTool.typeOrNull(getSource().stamp(NodeView.DEFAULT)); + ResolvedJavaType destType = StampTool.typeOrNull(getDestination().stamp(NodeView.DEFAULT)); if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) { return false; } @@ -173,7 +174,7 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt return true; } - if (StampTool.isExactType(getDestination().stamp())) { + if (StampTool.isExactType(getDestination().stamp(NodeView.DEFAULT))) { if (destType != null && destType.isAssignableFrom(srcType)) { return true; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java index 9261266c0eb..a053795a24f 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeCycles; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.java.LoadFieldNode; import org.graalvm.compiler.nodes.java.MonitorIdNode; @@ -65,7 +66,7 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement } protected Stamp computeStamp(ValueNode object) { - Stamp objectStamp = object.stamp(); + Stamp objectStamp = object.stamp(NodeView.DEFAULT); if (objectStamp instanceof ObjectStamp) { objectStamp = objectStamp.join(StampFactory.objectNonNull()); } @@ -116,7 +117,7 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement tool.replaceWithVirtual(newVirtual); } } else { - ResolvedJavaType type = getConcreteType(originalAlias.stamp()); + ResolvedJavaType type = getConcreteType(originalAlias.stamp(NodeView.DEFAULT)); if (type != null && !type.isArray()) { VirtualInstanceNode newVirtual = createVirtualInstanceNode(type, true); ResolvedJavaField[] fields = newVirtual.getFields(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java index 2e68bc7e6b8..e2fad1dfccc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.BinaryNode; import org.graalvm.compiler.nodes.calc.FloatDivNode; @@ -86,13 +87,13 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme @Override public Stamp foldStamp(Stamp stampX, Stamp stampY) { - return stamp(); + return stamp(NodeView.DEFAULT); } protected BinaryMathIntrinsicNode(ValueNode forX, ValueNode forY, BinaryOperation op) { super(TYPE, StampFactory.forKind(JavaKind.Double), forX, forY); - assert forX.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(forX.stamp()) == 64; - assert forY.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(forY.stamp()) == 64; + assert forX.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(forX.stamp(NodeView.DEFAULT)) == 64; + assert forY.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(forY.stamp(NodeView.DEFAULT)) == 64; this.operation = op; } @@ -118,6 +119,7 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { + NodeView view = NodeView.from(tool); ValueNode c = tryConstantFold(forX, forY, getOperation()); if (c != null) { return c; @@ -150,8 +152,8 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme } // x**0.5 = sqrt(x) - if (yValue == 0.5D && x.stamp() instanceof FloatStamp && ((FloatStamp) x.stamp()).lowerBound() >= 0.0D) { - return new SqrtNode(x); + if (yValue == 0.5D && x.stamp(view) instanceof FloatStamp && ((FloatStamp) x.stamp(view)).lowerBound() >= 0.0D) { + return SqrtNode.create(x, view); } } return this; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java index 864e9b569f7..a1868016324 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -48,7 +49,7 @@ public final class BitCountNode extends UnaryNode implements ArithmeticLIRLowera public static final NodeClass<BitCountNode> TYPE = NodeClass.create(BitCountNode.class); public BitCountNode(ValueNode value) { - super(TYPE, computeStamp(value.stamp(), value), value); + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @@ -59,7 +60,7 @@ public final class BitCountNode extends UnaryNode implements ArithmeticLIRLowera } static Stamp computeStamp(Stamp newStamp, ValueNode theValue) { - assert newStamp.isCompatible(theValue.stamp()); + assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; assert (valueStamp.downMask() & CodeUtil.mask(valueStamp.getBits())) == valueStamp.downMask(); assert (valueStamp.upMask() & CodeUtil.mask(valueStamp.getBits())) == valueStamp.upMask(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java index 80d3d8d61a6..b9d1f50056a 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -53,13 +54,13 @@ public final class BitScanForwardNode extends UnaryNode implements ArithmeticLIR public static final NodeClass<BitScanForwardNode> TYPE = NodeClass.create(BitScanForwardNode.class); public BitScanForwardNode(ValueNode value) { - super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value); + super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp(NodeView.DEFAULT)).getBits()), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; int min; int max; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java index bf9be6929fb..9dc9ac98ca4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -53,13 +54,13 @@ public final class BitScanReverseNode extends UnaryNode implements ArithmeticLIR public static final NodeClass<BitScanReverseNode> TYPE = NodeClass.create(BitScanReverseNode.class); public BitScanReverseNode(ValueNode value) { - super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value); + super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp(NodeView.DEFAULT)).getBits()), value); assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long; } @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; int min; int max; diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java index 604a9987ea8..bc78260f0c4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.GuardNode; import org.graalvm.compiler.nodes.InvokeNode; import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; @@ -263,7 +264,7 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl // Try to get the most accurate receiver type if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) { ValueNode receiver = getReceiver(originalArguments); - TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp()); + TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp(NodeView.DEFAULT)); if (receiverType != null) { concreteMethod = receiverType.getType().findUniqueConcreteMethod(target); } @@ -317,7 +318,7 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl * type information anyway. */ if (targetType != null && !targetType.getType().isPrimitive() && !argument.getStackKind().isPrimitive()) { - ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp()); + ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp(NodeView.DEFAULT)); if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) { LogicNode inst = InstanceOfNode.createAllowNull(targetType, argument, null, null); assert !inst.isAlive(); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java index ff5d7658706..e46bf3af591 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodeinfo.Verbosity; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -82,7 +83,7 @@ public final class ReadRegisterNode extends FixedWithNextNode implements LIRLowe @Override public void generate(NodeLIRBuilderTool generator) { - LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp()); + LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); Value result = register.asValue(kind); if (incoming) { generator.getLIRGeneratorTool().emitIncomingValues(new Value[]{result}); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java index 414a7632938..5ad14522c51 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java @@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; @@ -54,7 +55,7 @@ public final class ReverseBytesNode extends UnaryNode implements LIRLowerable { @Override public Stamp foldStamp(Stamp newStamp) { - assert newStamp.isCompatible(getValue().stamp()); + assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT)); IntegerStamp valueStamp = (IntegerStamp) newStamp; if (getStackKind() == JavaKind.Int) { long mask = CodeUtil.mask(JavaKind.Int.getBitCount()); @@ -62,7 +63,7 @@ public final class ReverseBytesNode extends UnaryNode implements LIRLowerable { } else if (getStackKind() == JavaKind.Long) { return IntegerStamp.stampForMask(valueStamp.getBits(), Long.reverse(valueStamp.downMask()), Long.reverse(valueStamp.upMask())); } else { - return stamp(); + return stamp(NodeView.DEFAULT); } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java index 390c96af042..77d2c6d94dc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.UnaryNode; import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable; @@ -106,8 +107,8 @@ public final class UnaryMathIntrinsicNode extends UnaryNode implements Arithmeti } protected UnaryMathIntrinsicNode(ValueNode value, UnaryOperation op) { - super(TYPE, computeStamp(value.stamp(), op), value); - assert value.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(value.stamp()) == 64; + super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), op), value); + assert value.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(value.stamp(NodeView.DEFAULT)) == 64; this.operation = op; } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java index bc6ae830e95..f22225b1e7d 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java @@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -54,8 +55,8 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr public IntegerAddExactNode(ValueNode x, ValueNode y) { super(TYPE, x, y); - setStamp(x.stamp().unrestricted()); - assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp; + setStamp(x.stamp(NodeView.DEFAULT).unrestricted()); + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp; } @Override @@ -132,7 +133,7 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr return forX; } } - if (!IntegerStamp.addCanOverflow((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) { + if (!IntegerStamp.addCanOverflow((IntegerStamp) forX.stamp(NodeView.DEFAULT), (IntegerStamp) forY.stamp(NodeView.DEFAULT))) { return new AddNode(forX, forY).canonical(tool); } return this; @@ -159,7 +160,7 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr @Override public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) { - return graph().add(new IntegerAddExactSplitNode(stamp(), getX(), getY(), next, deopt)); + return graph().add(new IntegerAddExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java index 43bb3993c3c..5e909f8e165 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.AddNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -49,7 +50,8 @@ public final class IntegerAddExactSplitNode extends IntegerExactArithmeticSplitN @Override public void simplify(SimplifierTool tool) { - if (!IntegerStamp.addCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) { + NodeView view = NodeView.from(tool); + if (!IntegerStamp.addCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) { tool.deleteBranch(overflowSuccessor); tool.addToWorkList(next); AddNode replacement = graph().unique(new AddNode(x, y)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java index f4781e44123..f3656b3b5f0 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.MulNode; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -48,8 +49,8 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr public IntegerMulExactNode(ValueNode x, ValueNode y) { super(TYPE, x, y); - setStamp(x.stamp().unrestricted()); - assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp; + setStamp(x.stamp(NodeView.DEFAULT).unrestricted()); + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp; } @Override @@ -76,10 +77,10 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr return forX; } if (c == 0) { - return ConstantNode.forIntegerStamp(stamp(), 0); + return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0); } } - if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) { + if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(NodeView.DEFAULT), (IntegerStamp) y.stamp(NodeView.DEFAULT))) { return new MulNode(x, y).canonical(tool); } return this; @@ -104,7 +105,7 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr @Override public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) { - return graph().add(new IntegerMulExactSplitNode(stamp(), getX(), getY(), next, deopt)); + return graph().add(new IntegerMulExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java index c7d4492e53d..aff76b1d4e4 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java @@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.MulNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -51,7 +52,8 @@ public final class IntegerMulExactSplitNode extends IntegerExactArithmeticSplitN @Override public void simplify(SimplifierTool tool) { - if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) { + NodeView view = NodeView.from(tool); + if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) { tool.deleteBranch(overflowSuccessor); tool.addToWorkList(next); MulNode replacement = graph().unique(new MulNode(x, y)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java index 2a7107ef435..13c381dc6fc 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -81,7 +82,7 @@ public final class IntegerMulHighNode extends BinaryArithmeticNode<MulHigh> impl if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { long i = ((PrimitiveConstant) c).asLong(); if (i == 0 || i == 1) { - return ConstantNode.forIntegerStamp(self.stamp(), 0); + return ConstantNode.forIntegerStamp(self.stamp(NodeView.DEFAULT), 0); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java index 625dbf6f7d0..d9a4ede5b95 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.SubNode; import org.graalvm.compiler.nodes.spi.LoweringTool; @@ -49,8 +50,8 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr public IntegerSubExactNode(ValueNode x, ValueNode y) { super(TYPE, x, y); - setStamp(x.stamp().unrestricted()); - assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp; + setStamp(x.stamp(NodeView.DEFAULT).unrestricted()); + assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp; } @Override @@ -67,7 +68,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr @Override public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) { if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) { - return ConstantNode.forIntegerStamp(stamp(), 0); + return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0); } if (forX.isConstant() && forY.isConstant()) { return canonicalXYconstant(forX, forY); @@ -77,7 +78,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr return forX; } } - if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) { + if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(NodeView.DEFAULT), (IntegerStamp) y.stamp(NodeView.DEFAULT))) { return new SubNode(x, y).canonical(tool); } return this; @@ -102,7 +103,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr @Override public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) { - return graph().add(new IntegerSubExactSplitNode(stamp(), getX(), getY(), next, deopt)); + return graph().add(new IntegerSubExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt)); } @Override diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java index ad6ad0f808e..acd86ca9995 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.SimplifierTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.SubNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -49,7 +50,8 @@ public final class IntegerSubExactSplitNode extends IntegerExactArithmeticSplitN @Override public void simplify(SimplifierTool tool) { - if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) { + NodeView view = NodeView.from(tool); + if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) { tool.deleteBranch(overflowSuccessor); tool.addToWorkList(next); SubNode replacement = graph().unique(new SubNode(x, y)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java index ff0d7d1ceca..c0554681a7c 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -81,7 +82,7 @@ public final class UnsignedMulHighNode extends BinaryArithmeticNode<UMulHigh> im if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) { long i = ((PrimitiveConstant) c).asLong(); if (i == 0 || i == 1) { - return ConstantNode.forIntegerStamp(self.stamp(), 0); + return ConstantNode.forIntegerStamp(self.stamp(NodeView.DEFAULT), 0); } } } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java index d880f8dd5cb..9dae16eef14 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java @@ -31,6 +31,7 @@ import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ProxyNode; @@ -228,7 +229,8 @@ public final class GraphEffectList extends EffectList { */ public void replaceAtUsages(ValueNode node, ValueNode replacement, FixedNode insertBefore) { assert node != null && replacement != null : node + " " + replacement; - assert node.stamp().isCompatible(replacement.stamp()) : "Replacement node stamp not compatible " + node.stamp() + " vs " + replacement.stamp(); + assert node.stamp(NodeView.DEFAULT).isCompatible(replacement.stamp(NodeView.DEFAULT)) : "Replacement node stamp not compatible " + node.stamp(NodeView.DEFAULT) + " vs " + + replacement.stamp(NodeView.DEFAULT); add("replace at usages", (graph, obsoleteNodes) -> { assert node.isAlive(); ValueNode replacementNode = graph.addOrUniqueWithInputs(replacement); @@ -244,8 +246,8 @@ public final class GraphEffectList extends EffectList { * to improve the stamp information of the read. Such a read might later be replaced * with a read with a less precise stamp. */ - if (!node.stamp().equals(replacementNode.stamp())) { - replacementNode = graph.unique(new PiNode(replacementNode, node.stamp())); + if (!node.stamp(NodeView.DEFAULT).equals(replacementNode.stamp(NodeView.DEFAULT))) { + replacementNode = graph.unique(new PiNode(replacementNode, node.stamp(NodeView.DEFAULT))); } node.replaceAtUsages(replacementNode); if (node instanceof FixedWithNextNode) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java index b4722d8f9b5..5403efad0a3 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java @@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.nodes.FieldLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; @@ -127,7 +128,7 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P VirtualInstanceNode instance = (VirtualInstanceNode) virtual; for (int i = 0; i < instance.entryCount(); i++) { JavaKind declaredKind = instance.field(i).getJavaKind(); - if (declaredKind == stampToJavaKind(values.get(i).stamp())) { + if (declaredKind == stampToJavaKind(values.get(i).stamp(NodeView.DEFAULT))) { // We won't cache unaligned field writes upon instantiation unless we add // support for non-array objects in PEReadEliminationClosure.processUnsafeLoad. readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1, declaredKind, false), values.get(i)); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java index b667a19c72f..59ec8053046 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java @@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopExitNode; import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; @@ -189,7 +190,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE ValueNode object = GraphUtil.unproxify(load.object()); LocationIdentity location = NamedLocationIdentity.getArrayLocation(componentKind); ValueNode cachedValue = state.getReadCache(object, location, index, accessKind, this); - assert cachedValue == null || load.stamp().isCompatible(cachedValue.stamp()) : "The RawLoadNode's stamp is not compatible with the cached value."; + assert cachedValue == null || load.stamp(NodeView.DEFAULT).isCompatible(cachedValue.stamp(NodeView.DEFAULT)) : "The RawLoadNode's stamp is not compatible with the cached value."; if (cachedValue != null) { effects.replaceAtUsages(load, cachedValue, load); addScalarAlias(load, cachedValue); @@ -399,7 +400,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE // e.g. unsafe loads / stores with different access kinds have different stamps // although location, object and offset are the same, in this case we cannot // create a phi nor can we set a common value - if (otherValue == null || !value.stamp().isCompatible(otherValue.stamp())) { + if (otherValue == null || !value.stamp(NodeView.DEFAULT).isCompatible(otherValue.stamp(NodeView.DEFAULT))) { value = null; phi = false; break; @@ -409,11 +410,11 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE } } if (phi) { - PhiNode phiNode = getPhi(key, value.stamp().unrestricted()); + PhiNode phiNode = getPhi(key, value.stamp(NodeView.DEFAULT).unrestricted()); mergeEffects.addFloatingNode(phiNode, "mergeReadCache"); for (int i = 0; i < states.size(); i++) { ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, key.kind, PEReadEliminationClosure.this); - assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps."; + assert phiNode.stamp(NodeView.DEFAULT).isCompatible(v.stamp(NodeView.DEFAULT)) : "Cannot create read elimination phi for inputs with incompatible stamps."; setPhiInput(phiNode, i, v); } newState.readCache.put(key, phiNode); @@ -444,13 +445,13 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, kind, PEReadEliminationClosure.this); // e.g. unsafe loads / stores with same identity and different access kinds see // mergeReadCache(states) - if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) { + if (value == null || !values[i - 1].stamp(NodeView.DEFAULT).isCompatible(value.stamp(NodeView.DEFAULT))) { return; } values[i] = value; } - PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind, overflowAccess), values[0].stamp().unrestricted()); + PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind, overflowAccess), values[0].stamp(NodeView.DEFAULT).unrestricted()); mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi"); for (int i = 0; i < values.length; i++) { setPhiInput(phiNode, i, values[i]); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java index f88b86b763b..57ec34493f5 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java @@ -49,6 +49,7 @@ import org.graalvm.compiler.nodes.FrameState; import org.graalvm.compiler.nodes.Invoke; import org.graalvm.compiler.nodes.LoopBeginNode; import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.StructuredGraph; @@ -877,12 +878,12 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat if (phis[valueIndex] == null) { ValueNode field = states[i].getObjectState(getObject.applyAsInt(i)).getEntry(valueIndex); if (values[valueIndex] != field) { - phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted()); + phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted()); } } } - if (phis[valueIndex] != null && !phis[valueIndex].stamp().isCompatible(values[valueIndex].stamp())) { - phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted()); + if (phis[valueIndex] != null && !phis[valueIndex].stamp(NodeView.DEFAULT).isCompatible(values[valueIndex].stamp(NodeView.DEFAULT))) { + phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted()); } if (twoSlotKinds != null && twoSlotKinds[valueIndex] != null) { // skip an entry after a long/double value that occupies two int slots diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java index 26b8d935fc3..6e05a283695 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.FieldLocationIdentity; import org.graalvm.compiler.nodes.FixedWithNextNode; import org.graalvm.compiler.nodes.LoopExitNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.ValueNode; @@ -97,7 +98,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination LoadCacheEntry identifier = new LoadCacheEntry(object, new FieldLocationIdentity(access.field())); ValueNode cachedValue = state.getCacheEntry(identifier); if (node instanceof LoadFieldNode) { - if (cachedValue != null && access.stamp().isCompatible(cachedValue.stamp())) { + if (cachedValue != null && access.stamp(NodeView.DEFAULT).isCompatible(cachedValue.stamp(NodeView.DEFAULT))) { effects.replaceAtUsages(access, cachedValue, access); addScalarAlias(access, cachedValue); deleted = true; @@ -196,7 +197,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination } private static boolean areValuesReplaceable(ValueNode originalValue, ValueNode replacementValue, boolean considerGuards) { - return originalValue.stamp().isCompatible(replacementValue.stamp()) && + return originalValue.stamp(NodeView.DEFAULT).isCompatible(replacementValue.stamp(NodeView.DEFAULT)) && (!considerGuards || (getGuard(originalValue) == null || getGuard(originalValue) == getGuard(replacementValue))); } @@ -269,7 +270,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination // E.g. unsafe loads / stores with different access kinds have different stamps // although location, object and offset are the same. In this case we cannot // create a phi nor can we set a common value. - if (otherValue == null || !value.stamp().isCompatible(otherValue.stamp())) { + if (otherValue == null || !value.stamp(NodeView.DEFAULT).isCompatible(otherValue.stamp(NodeView.DEFAULT))) { value = null; phi = false; break; @@ -279,11 +280,11 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination } } if (phi) { - PhiNode phiNode = getCachedPhi(key, value.stamp().unrestricted()); + PhiNode phiNode = getCachedPhi(key, value.stamp(NodeView.DEFAULT).unrestricted()); mergeEffects.addFloatingNode(phiNode, "mergeReadCache"); for (int i = 0; i < states.size(); i++) { ValueNode v = states.get(i).getCacheEntry(key); - assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps."; + assert phiNode.stamp(NodeView.DEFAULT).isCompatible(v.stamp(NodeView.DEFAULT)) : "Cannot create read elimination phi for inputs with incompatible stamps."; setPhiInput(phiNode, i, v); } newState.addCacheEntry(key, phiNode); @@ -315,14 +316,14 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination ValueNode value = states.get(i).getCacheEntry(identifier.duplicateWithObject(getPhiValueAt(phi, i))); // e.g. unsafe loads / stores with same identity and different access kinds see // mergeReadCache(states) - if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) { + if (value == null || !values[i - 1].stamp(NodeView.DEFAULT).isCompatible(value.stamp(NodeView.DEFAULT))) { return; } values[i] = value; } CacheEntry<?> newIdentifier = identifier.duplicateWithObject(phi); - PhiNode phiNode = getCachedPhi(newIdentifier, values[0].stamp().unrestricted()); + PhiNode phiNode = getCachedPhi(newIdentifier, values[0].stamp(NodeView.DEFAULT).unrestricted()); mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi"); for (int i = 0; i < values.length; i++) { setPhiInput(phiNode, i, values[i]); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java index bae52ff242c..314d2d97dd1 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java @@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode; @@ -179,7 +180,7 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool { } else if (oldValue.getStackKind() == JavaKind.Double || oldValue.getStackKind() == JavaKind.Long) { // Splitting double word constant by storing over it with an int getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing second half of double word value %s", current, oldValue); - ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false); + ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false, NodeView.DEFAULT); addNode(secondHalf); state.setEntry(virtual.getObjectId(), index + 1, secondHalf); } @@ -188,7 +189,7 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool { // Storing into second half of double, so replace previous value ValueNode previous = getEntry(virtual, index - 1); getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing first half of double word value %s", current, previous); - ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true); + ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true, NodeView.DEFAULT); addNode(firstHalf); state.setEntry(virtual.getObjectId(), index - 1, firstHalf); } diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java index 9b43b013a6c..41abd8cc3e9 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java @@ -38,6 +38,7 @@ import org.graalvm.compiler.lir.ConstantValue; import org.graalvm.compiler.nodeinfo.NodeInfo; import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; @@ -73,17 +74,17 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl } public static WordCastNode addressToWord(ValueNode input, JavaKind wordKind) { - assert input.stamp() instanceof AbstractPointerStamp; + assert input.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp; return new WordCastNode(StampFactory.forKind(wordKind), input); } public static WordCastNode objectToTrackedPointer(ValueNode input, JavaKind wordKind) { - assert input.stamp() instanceof ObjectStamp; + assert input.stamp(NodeView.DEFAULT) instanceof ObjectStamp; return new WordCastNode(StampFactory.forKind(wordKind), input, true); } public static WordCastNode objectToUntrackedPointer(ValueNode input, JavaKind wordKind) { - assert input.stamp() instanceof ObjectStamp; + assert input.stamp(NodeView.DEFAULT) instanceof ObjectStamp; return new WordCastNode(StampFactory.forKind(wordKind), input, false); } @@ -108,13 +109,13 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl return input; } - assert !stamp().isCompatible(input.stamp()); + assert !stamp(NodeView.DEFAULT).isCompatible(input.stamp(NodeView.DEFAULT)); if (input.isConstant()) { /* Null pointers are uncritical for GC, so they can be constant folded. */ if (input.asJavaConstant().isNull()) { - return ConstantNode.forIntegerStamp(stamp(), 0); + return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0); } else if (input.asJavaConstant().getJavaKind().isNumericInteger() && input.asJavaConstant().asLong() == 0) { - return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, tool.getMetaAccess()); + return ConstantNode.forConstant(stamp(NodeView.DEFAULT), JavaConstant.NULL_POINTER, tool.getMetaAccess()); } } @@ -124,7 +125,7 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl @Override public void generate(NodeLIRBuilderTool generator) { Value value = generator.operand(input); - ValueKind<?> kind = generator.getLIRGeneratorTool().getLIRKind(stamp()); + ValueKind<?> kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); assert kind.getPlatformKind().getSizeInBytes() == value.getPlatformKind().getSizeInBytes(); if (trackedPointer && LIRKind.isValue(kind) && !LIRKind.isValue(value)) { From d4fab56c4fafc84937e724595335cb48f087d6c5 Mon Sep 17 00:00:00 2001 From: Ioi Lam <iklam@openjdk.org> Date: Fri, 1 Dec 2017 15:53:15 -0800 Subject: [PATCH 116/165] 8190809: JVM crashes while generating appcds for classpath with empty directory entry Reviewed-by: ccheung, jiangli, lmesnik --- .../share/classfile/sharedClassUtil.cpp | 37 ++++++------ .../runtime/appcds/DirClasspathTest.java | 58 +++++++++++++++++++ 2 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java diff --git a/src/hotspot/share/classfile/sharedClassUtil.cpp b/src/hotspot/share/classfile/sharedClassUtil.cpp index 4d713cae863..2080472e2f1 100644 --- a/src/hotspot/share/classfile/sharedClassUtil.cpp +++ b/src/hotspot/share/classfile/sharedClassUtil.cpp @@ -141,23 +141,26 @@ void SharedClassUtil::update_shared_classpath(ClassPathEntry *cpe, SharedClassPa ResourceMark rm(THREAD); jint manifest_size; bool isSigned; - char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK); - if (manifest != NULL) { - ManifestStream* stream = new ManifestStream((u1*)manifest, - manifest_size); - isSigned = stream->check_is_signed(); - if (isSigned) { - ent->_is_signed = true; - } else { - // Copy the manifest into the shared archive - manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK); - Array<u1>* buf = MetadataFactory::new_array<u1>(loader_data, - manifest_size, - THREAD); - char* p = (char*)(buf->data()); - memcpy(p, manifest, manifest_size); - ent->set_manifest(buf); - ent->_is_signed = false; + + if (cpe->is_jar_file()) { + char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK); + if (manifest != NULL) { + ManifestStream* stream = new ManifestStream((u1*)manifest, + manifest_size); + isSigned = stream->check_is_signed(); + if (isSigned) { + ent->_is_signed = true; + } else { + // Copy the manifest into the shared archive + manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK); + Array<u1>* buf = MetadataFactory::new_array<u1>(loader_data, + manifest_size, + THREAD); + char* p = (char*)(buf->data()); + memcpy(p, manifest, manifest_size); + ent->set_manifest(buf); + ent->_is_signed = false; + } } } } diff --git a/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java b/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java new file mode 100644 index 00000000000..047f2d00080 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 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 + * @summary AppCDS handling of directories in -cp + * AppCDS does not support uncompressed oops + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @library /test/lib + * @run main DirClasspathTest + */ + +import jdk.test.lib.Platform; +import jdk.test.lib.process.OutputAnalyzer; +import java.io.File; + +public class DirClasspathTest { + public static void main(String[] args) throws Exception { + File dir = new File(System.getProperty("user.dir")); + File emptydir = new File(dir, "emptydir"); + emptydir.mkdir(); + + // Empty dir in -cp: should be OK + OutputAnalyzer output; + if (!Platform.isWindows()) { + // This block fails on Windows because of JDK-8192927 + output = TestCommon.dump(emptydir.getPath(), TestCommon.list("DoesntMatter"), "-Xlog:class+path=info"); + TestCommon.checkDump(output); + } + + // Non-empty dir in -cp: should fail + // <dir> is not empty because it has at least one subdirectory, i.e., <emptydir> + output = TestCommon.dump(dir.getPath(), TestCommon.list("DoesntMatter"), "-Xlog:class+path=info"); + output.shouldNotHaveExitValue(0); + output.shouldContain("CDS allows only empty directories in archived classpaths"); + } +} From c647d80e3819d5450675c69ce32a88a8420a9981 Mon Sep 17 00:00:00 2001 From: Igor Ignatyev <iignatyev@openjdk.org> Date: Fri, 1 Dec 2017 15:58:39 -0800 Subject: [PATCH 117/165] 8191273: applications/ctw/modules tests fail intermittently Reviewed-by: kvn, dlong --- .../ctw/src/sun/hotspot/tools/ctw/CtwRunner.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java index 453c3c0c612..cdc8efaedfb 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java @@ -123,14 +123,15 @@ public class CtwRunner { .collect(Collectors.joining(" ")); String phase = phaseName(classStart); Path out = Paths.get(".", phase + ".out"); + Path err = Paths.get(".", phase + ".err"); System.out.printf("%s %dms START : [%s]%n" + "cout/cerr are redirected to %s%n", phase, TimeUnit.NANOSECONDS.toMillis(System.nanoTime()), - commandLine, out); - int exitCode = pb.redirectErrorStream(true) - .redirectOutput(out.toFile()) - .start() - .waitFor(); + commandLine, phase); + int exitCode = pb.redirectOutput(out.toFile()) + .redirectError(err.toFile()) + .start() + .waitFor(); System.out.printf("%s %dms END : exit code = %d%n", phase, TimeUnit.NANOSECONDS.toMillis(System.nanoTime()), exitCode); From f570e1bc2778b4e32447b1a93a0a6f9a0e55ded8 Mon Sep 17 00:00:00 2001 From: Roland Westrelin <roland@openjdk.org> Date: Fri, 1 Dec 2017 16:20:18 -0800 Subject: [PATCH 118/165] 8192762: LoopNode::verify_strip_mined() fails with "assert failed: only phis" Don't clone Phi to uncommon calls Reviewed-by: kvn --- src/hotspot/share/opto/compile.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index efb7000e37b..8e5da8a4028 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2751,27 +2751,28 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { case Op_CallRuntime: case Op_CallLeaf: case Op_CallLeafNoFP: { - assert( n->is_Call(), "" ); + assert (n->is_Call(), ""); CallNode *call = n->as_Call(); // Count call sites where the FP mode bit would have to be flipped. // Do not count uncommon runtime calls: // uncommon_trap, _complete_monitor_locking, _complete_monitor_unlocking, // _new_Java, _new_typeArray, _new_objArray, _rethrow_Java, ... - if( !call->is_CallStaticJava() || !call->as_CallStaticJava()->_name ) { + if (!call->is_CallStaticJava() || !call->as_CallStaticJava()->_name) { frc.inc_call_count(); // Count the call site } else { // See if uncommon argument is shared Node *n = call->in(TypeFunc::Parms); int nop = n->Opcode(); // Clone shared simple arguments to uncommon calls, item (1). - if( n->outcnt() > 1 && + if (n->outcnt() > 1 && !n->is_Proj() && nop != Op_CreateEx && nop != Op_CheckCastPP && nop != Op_DecodeN && nop != Op_DecodeNKlass && - !n->is_Mem() ) { + !n->is_Mem() && + !n->is_Phi()) { Node *x = n->clone(); - call->set_req( TypeFunc::Parms, x ); + call->set_req(TypeFunc::Parms, x); } } break; From b4ef56fd28cebc515c99d5f26b1fa80692f688a3 Mon Sep 17 00:00:00 2001 From: Vivek Deshpande <vdeshpande@openjdk.org> Date: Fri, 1 Dec 2017 16:23:17 -0800 Subject: [PATCH 119/165] 8190494: Different results with UseAVX=3 when calling AVX-512 native function via JNI Reset mask register after JNI calls Reviewed-by: kvn, vlivanov --- src/hotspot/cpu/x86/assembler_x86.cpp | 164 ++++++++++----------- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 7 + 2 files changed, 89 insertions(+), 82 deletions(-) diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 1156969b193..961ebb531be 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -1256,7 +1256,7 @@ void Assembler::addr_nop_8() { void Assembler::addsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x58); @@ -1266,7 +1266,7 @@ void Assembler::addsd(XMMRegister dst, XMMRegister src) { void Assembler::addsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -1276,7 +1276,7 @@ void Assembler::addsd(XMMRegister dst, Address src) { void Assembler::addss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x58); emit_int8((unsigned char)(0xC0 | encode)); @@ -1285,7 +1285,7 @@ void Assembler::addss(XMMRegister dst, XMMRegister src) { void Assembler::addss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x58); @@ -1295,7 +1295,7 @@ void Assembler::addss(XMMRegister dst, Address src) { void Assembler::aesdec(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDE); emit_operand(dst, src); @@ -1303,7 +1303,7 @@ void Assembler::aesdec(XMMRegister dst, Address src) { void Assembler::aesdec(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDE); emit_int8(0xC0 | encode); @@ -1312,7 +1312,7 @@ void Assembler::aesdec(XMMRegister dst, XMMRegister src) { void Assembler::aesdeclast(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDF); emit_operand(dst, src); @@ -1320,7 +1320,7 @@ void Assembler::aesdeclast(XMMRegister dst, Address src) { void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDF); emit_int8((unsigned char)(0xC0 | encode)); @@ -1329,7 +1329,7 @@ void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) { void Assembler::aesenc(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDC); emit_operand(dst, src); @@ -1337,7 +1337,7 @@ void Assembler::aesenc(XMMRegister dst, Address src) { void Assembler::aesenc(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDC); emit_int8(0xC0 | encode); @@ -1346,7 +1346,7 @@ void Assembler::aesenc(XMMRegister dst, XMMRegister src) { void Assembler::aesenclast(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDD); emit_operand(dst, src); @@ -1354,7 +1354,7 @@ void Assembler::aesenclast(XMMRegister dst, Address src) { void Assembler::aesenclast(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDD); emit_int8((unsigned char)(0xC0 | encode)); @@ -1387,7 +1387,7 @@ void Assembler::andl(Register dst, Register src) { void Assembler::andnl(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_int8((unsigned char)(0xC0 | encode)); @@ -1396,7 +1396,7 @@ void Assembler::andnl(Register dst, Register src1, Register src2) { void Assembler::andnl(Register dst, Register src1, Address src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_operand(dst, src2); @@ -1424,7 +1424,7 @@ void Assembler::bswapl(Register reg) { // bswap void Assembler::blsil(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -1433,7 +1433,7 @@ void Assembler::blsil(Register dst, Register src) { void Assembler::blsil(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rbx, src); @@ -1441,7 +1441,7 @@ void Assembler::blsil(Register dst, Address src) { void Assembler::blsmskl(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -1450,7 +1450,7 @@ void Assembler::blsmskl(Register dst, Register src) { void Assembler::blsmskl(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rdx, src); @@ -1458,7 +1458,7 @@ void Assembler::blsmskl(Register dst, Address src) { void Assembler::blsrl(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -1467,7 +1467,7 @@ void Assembler::blsrl(Register dst, Register src) { void Assembler::blsrl(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rcx, src); @@ -1753,7 +1753,7 @@ void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) { void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x5A); @@ -1763,7 +1763,7 @@ void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { void Assembler::cvtsd2ss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -1817,7 +1817,7 @@ void Assembler::cvtsi2ssq(XMMRegister dst, Register src) { void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5A); emit_int8((unsigned char)(0xC0 | encode)); @@ -1826,7 +1826,7 @@ void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) { void Assembler::cvtss2sd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5A); @@ -1870,7 +1870,7 @@ void Assembler::decl(Address dst) { void Assembler::divsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -1880,7 +1880,7 @@ void Assembler::divsd(XMMRegister dst, Address src) { void Assembler::divsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); @@ -1890,7 +1890,7 @@ void Assembler::divsd(XMMRegister dst, XMMRegister src) { void Assembler::divss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); @@ -1899,7 +1899,7 @@ void Assembler::divss(XMMRegister dst, Address src) { void Assembler::divss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); emit_int8((unsigned char)(0xC0 | encode)); @@ -2105,7 +2105,7 @@ void Assembler::jmpb(Label& L) { void Assembler::ldmxcsr( Address src) { if (UseAVX > 0 ) { InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, 0, 0, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xAE); emit_operand(as_Register(2), src); @@ -2784,7 +2784,7 @@ void Assembler::movsbl(Register dst, Register src) { // movsxb void Assembler::movsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x10); @@ -2794,7 +2794,7 @@ void Assembler::movsd(XMMRegister dst, XMMRegister src) { void Assembler::movsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -2805,7 +2805,7 @@ void Assembler::movsd(XMMRegister dst, Address src) { void Assembler::movsd(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.reset_is_clear_context(); attributes.set_rex_vex_w_reverted(); @@ -2816,7 +2816,7 @@ void Assembler::movsd(Address dst, XMMRegister src) { void Assembler::movss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x10); emit_int8((unsigned char)(0xC0 | encode)); @@ -2825,7 +2825,7 @@ void Assembler::movss(XMMRegister dst, XMMRegister src) { void Assembler::movss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x10); @@ -2835,7 +2835,7 @@ void Assembler::movss(XMMRegister dst, Address src) { void Assembler::movss(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); attributes.reset_is_clear_context(); simd_prefix(src, xnoreg, dst, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); @@ -2931,7 +2931,7 @@ void Assembler::mull(Register src) { void Assembler::mulsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -2941,7 +2941,7 @@ void Assembler::mulsd(XMMRegister dst, Address src) { void Assembler::mulsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x59); @@ -2951,7 +2951,7 @@ void Assembler::mulsd(XMMRegister dst, XMMRegister src) { void Assembler::mulss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x59); @@ -2960,7 +2960,7 @@ void Assembler::mulss(XMMRegister dst, Address src) { void Assembler::mulss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x59); emit_int8((unsigned char)(0xC0 | encode)); @@ -4289,7 +4289,7 @@ void Assembler::vpalignr(XMMRegister dst, XMMRegister nds, XMMRegister src, int void Assembler::pblendw(XMMRegister dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0x0E); emit_int8((unsigned char)(0xC0 | encode)); @@ -4388,7 +4388,7 @@ void Assembler::smovl() { void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x51); @@ -4398,7 +4398,7 @@ void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) { void Assembler::sqrtsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4408,7 +4408,7 @@ void Assembler::sqrtsd(XMMRegister dst, Address src) { void Assembler::sqrtss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x51); emit_int8((unsigned char)(0xC0 | encode)); @@ -4421,7 +4421,7 @@ void Assembler::std() { void Assembler::sqrtss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x51); @@ -4484,7 +4484,7 @@ void Assembler::subl(Register dst, Register src) { void Assembler::subsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); @@ -4494,7 +4494,7 @@ void Assembler::subsd(XMMRegister dst, XMMRegister src) { void Assembler::subsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4504,7 +4504,7 @@ void Assembler::subsd(XMMRegister dst, Address src) { void Assembler::subss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false , /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true , /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); emit_int8((unsigned char)(0xC0 | encode)); @@ -4513,7 +4513,7 @@ void Assembler::subss(XMMRegister dst, XMMRegister src) { void Assembler::subss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); @@ -4735,7 +4735,7 @@ void Assembler::xorb(Register dst, Address src) { void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4745,7 +4745,7 @@ void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x58); @@ -4755,7 +4755,7 @@ void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x58); @@ -4764,7 +4764,7 @@ void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x58); emit_int8((unsigned char)(0xC0 | encode)); @@ -4773,7 +4773,7 @@ void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4783,7 +4783,7 @@ void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); @@ -4793,7 +4793,7 @@ void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); @@ -4802,7 +4802,7 @@ void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5E); emit_int8((unsigned char)(0xC0 | encode)); @@ -4810,7 +4810,7 @@ void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vfmadd231sd(XMMRegister dst, XMMRegister src1, XMMRegister src2) { assert(VM_Version::supports_fma(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xB9); emit_int8((unsigned char)(0xC0 | encode)); @@ -4818,7 +4818,7 @@ void Assembler::vfmadd231sd(XMMRegister dst, XMMRegister src1, XMMRegister src2) void Assembler::vfmadd231ss(XMMRegister dst, XMMRegister src1, XMMRegister src2) { assert(VM_Version::supports_fma(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xB9); emit_int8((unsigned char)(0xC0 | encode)); @@ -4827,7 +4827,7 @@ void Assembler::vfmadd231ss(XMMRegister dst, XMMRegister src1, XMMRegister src2) void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4837,7 +4837,7 @@ void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x59); @@ -4847,7 +4847,7 @@ void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x59); @@ -4856,7 +4856,7 @@ void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x59); emit_int8((unsigned char)(0xC0 | encode)); @@ -4865,7 +4865,7 @@ void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); attributes.set_rex_vex_w_reverted(); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); @@ -4875,7 +4875,7 @@ void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_rex_vex_w_reverted(); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); @@ -4885,7 +4885,7 @@ void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); @@ -4894,7 +4894,7 @@ void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) { void Assembler::vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x5C); emit_int8((unsigned char)(0xC0 | encode)); @@ -5395,7 +5395,7 @@ void Assembler::vxorps(XMMRegister dst, XMMRegister nds, Address src, int vector void Assembler::vphaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx() && (vector_len == 0) || VM_Version::supports_avx2(), "256 bit integer vectors requires AVX2"); - InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x01); emit_int8((unsigned char)(0xC0 | encode)); @@ -5454,7 +5454,7 @@ void Assembler::paddq(XMMRegister dst, XMMRegister src) { void Assembler::phaddw(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_sse3(), ""); - InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x01); emit_int8((unsigned char)(0xC0 | encode)); @@ -6697,7 +6697,7 @@ void Assembler::vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, in void Assembler::vzeroupper() { if (VM_Version::supports_vzeroupper()) { - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); (void)vex_prefix_and_encode(0, 0, 0, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8(0x77); } @@ -7460,7 +7460,7 @@ void Assembler::vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src, int void Assembler::shlxl(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF7); emit_int8((unsigned char)(0xC0 | encode)); @@ -7468,7 +7468,7 @@ void Assembler::shlxl(Register dst, Register src1, Register src2) { void Assembler::shlxq(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF7); emit_int8((unsigned char)(0xC0 | encode)); @@ -8003,7 +8003,7 @@ void Assembler::andq(Register dst, Register src) { void Assembler::andnq(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_int8((unsigned char)(0xC0 | encode)); @@ -8012,7 +8012,7 @@ void Assembler::andnq(Register dst, Register src1, Register src2) { void Assembler::andnq(Register dst, Register src1, Address src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_operand(dst, src2); @@ -8040,7 +8040,7 @@ void Assembler::bswapq(Register reg) { void Assembler::blsiq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -8049,7 +8049,7 @@ void Assembler::blsiq(Register dst, Register src) { void Assembler::blsiq(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rbx, src); @@ -8057,7 +8057,7 @@ void Assembler::blsiq(Register dst, Address src) { void Assembler::blsmskq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -8066,7 +8066,7 @@ void Assembler::blsmskq(Register dst, Register src) { void Assembler::blsmskq(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rdx, src); @@ -8074,7 +8074,7 @@ void Assembler::blsmskq(Register dst, Address src) { void Assembler::blsrq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); @@ -8083,7 +8083,7 @@ void Assembler::blsrq(Register dst, Register src) { void Assembler::blsrq(Register dst, Address src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); InstructionMark im(this); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rcx, src); @@ -8522,7 +8522,7 @@ void Assembler::mulq(Register src) { void Assembler::mulxq(Register dst1, Register dst2, Register src) { assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst1->encoding(), dst2->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF6); emit_int8((unsigned char)(0xC0 | encode)); @@ -8685,7 +8685,7 @@ void Assembler::rorq(Register dst, int imm8) { void Assembler::rorxq(Register dst, Register src, int imm8) { assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0xF0); emit_int8((unsigned char)(0xC0 | encode)); @@ -8694,7 +8694,7 @@ void Assembler::rorxq(Register dst, Register src, int imm8) { void Assembler::rorxd(Register dst, Register src, int imm8) { assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0xF0); emit_int8((unsigned char)(0xC0 | encode)); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 3ac35d752e4..112321cce78 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -6630,6 +6630,13 @@ void MacroAssembler::restore_cpu_control_state_after_jni() { } // Clear upper bits of YMM registers to avoid SSE <-> AVX transition penalty. vzeroupper(); + // Reset k1 to 0xffff. + if (VM_Version::supports_evex()) { + push(rcx); + movl(rcx, 0xffff); + kmovwl(k1, rcx); + pop(rcx); + } #ifndef _LP64 // Either restore the x87 floating pointer control word after returning From 1172328edfc2727a1c060746ae7d5629c6a96137 Mon Sep 17 00:00:00 2001 From: Vivek Deshpande <vdeshpande@openjdk.org> Date: Fri, 1 Dec 2017 16:58:30 -0800 Subject: [PATCH 120/165] 8170244: Update UseAVX after cpu feature detection to use more default mapping Reviewed-by: kvn --- src/hotspot/cpu/x86/vm_version_x86.cpp | 51 ++++++++++++++++++-------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index f322d60a6ed..563ff1f111b 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -629,18 +629,26 @@ void VM_Version::get_processor_features() { _features &= ~CPU_SSE; // first try initial setting and detect what we can support + int use_avx_limit = 0; if (UseAVX > 0) { if (UseAVX > 2 && supports_evex()) { - UseAVX = 3; + use_avx_limit = 3; } else if (UseAVX > 1 && supports_avx2()) { - UseAVX = 2; + use_avx_limit = 2; } else if (UseAVX > 0 && supports_avx()) { - UseAVX = 1; + use_avx_limit = 1; } else { - UseAVX = 0; + use_avx_limit = 0; } + } + if (FLAG_IS_DEFAULT(UseAVX)) { + FLAG_SET_DEFAULT(UseAVX, use_avx_limit); + } else if (UseAVX > use_avx_limit) { + warning("UseAVX=%d is not supported on this CPU, setting it to UseAVX=%d", (int) UseAVX, use_avx_limit); + FLAG_SET_DEFAULT(UseAVX, use_avx_limit); } else if (UseAVX < 0) { - UseAVX = 0; + warning("UseAVX=%d is not valid, setting it to UseAVX=0", (int) UseAVX); + FLAG_SET_DEFAULT(UseAVX, 0); } if (UseAVX < 3) { @@ -710,16 +718,29 @@ void VM_Version::get_processor_features() { // UseSSE is set to the smaller of what hardware supports and what // the command line requires. I.e., you cannot set UseSSE to 2 on // older Pentiums which do not support it. - if (UseSSE > 4) UseSSE=4; - if (UseSSE < 0) UseSSE=0; - if (!supports_sse4_1()) // Drop to 3 if no SSE4 support - UseSSE = MIN2((intx)3,UseSSE); - if (!supports_sse3()) // Drop to 2 if no SSE3 support - UseSSE = MIN2((intx)2,UseSSE); - if (!supports_sse2()) // Drop to 1 if no SSE2 support - UseSSE = MIN2((intx)1,UseSSE); - if (!supports_sse ()) // Drop to 0 if no SSE support - UseSSE = 0; + int use_sse_limit = 0; + if (UseSSE > 0) { + if (UseSSE > 3 && supports_sse4_1()) { + use_sse_limit = 4; + } else if (UseSSE > 2 && supports_sse3()) { + use_sse_limit = 3; + } else if (UseSSE > 1 && supports_sse2()) { + use_sse_limit = 2; + } else if (UseSSE > 0 && supports_sse()) { + use_sse_limit = 1; + } else { + use_sse_limit = 0; + } + } + if (FLAG_IS_DEFAULT(UseSSE)) { + FLAG_SET_DEFAULT(UseSSE, use_sse_limit); + } else if (UseSSE > use_sse_limit) { + warning("UseSSE=%d is not supported on this CPU, setting it to UseSSE=%d", (int) UseSSE, use_sse_limit); + FLAG_SET_DEFAULT(UseSSE, use_sse_limit); + } else if (UseSSE < 0) { + warning("UseSSE=%d is not valid, setting it to UseSSE=0", (int) UseSSE); + FLAG_SET_DEFAULT(UseSSE, 0); + } // Use AES instructions if available. if (supports_aes()) { From 37811b7460012b503e9f6c52867942077674661f Mon Sep 17 00:00:00 2001 From: Chris Hegarty <chegar@openjdk.org> Date: Wed, 6 Dec 2017 11:11:59 -0800 Subject: [PATCH 121/165] 8191494: Refresh incubating HTTP Client Co-authored-by: Daniel Fuchs <daniel.fuchs@oracle.com> Co-authored-by: Michael McMahon <michael.x.mcmahon@oracle.com> Co-authored-by: Pavel Rappo <pavel.rappo@oracle.com> Reviewed-by: chegar, dfuchs, michaelm --- .../sun/net/httpserver/ServerImpl.java | 10 +- .../http/AbstractAsyncSSLConnection.java | 133 ++- ...Handler.java => AbstractSubscription.java} | 21 +- .../jdk/incubator/http/AsyncConnection.java | 112 -- .../jdk/incubator/http/AsyncEvent.java | 24 +- .../incubator/http/AsyncSSLConnection.java | 138 +-- .../jdk/incubator/http/AsyncSSLDelegate.java | 692 ------------- .../http/AsyncSSLTunnelConnection.java | 146 +-- .../jdk/incubator/http/AsyncTriggerEvent.java | 59 ++ .../incubator/http/AuthenticationFilter.java | 8 +- .../incubator/http/BlockingPushPublisher.java | 126 --- .../incubator/http/BufferingSubscriber.java | 313 ++++++ .../jdk/incubator/http/ConnectionPool.java | 485 ++++++--- .../jdk/incubator/http/CookieFilter.java | 20 +- .../jdk/incubator/http/DefaultPublisher.java | 143 --- .../classes/jdk/incubator/http/Exchange.java | 416 ++++---- .../jdk/incubator/http/ExchangeImpl.java | 166 +-- .../jdk/incubator/http/ExecutorWrapper.java | 105 -- .../jdk/incubator/http/FilterFactory.java | 6 +- .../jdk/incubator/http/HeaderFilter.java | 2 +- .../jdk/incubator/http/HeaderParser.java | 60 +- .../incubator/http/Http1AsyncReceiver.java | 651 ++++++++++++ .../jdk/incubator/http/Http1Exchange.java | 607 ++++++++--- .../jdk/incubator/http/Http1HeaderParser.java | 257 +++++ .../jdk/incubator/http/Http1Request.java | 390 +++---- .../jdk/incubator/http/Http1Response.java | 510 +++++++--- .../jdk/incubator/http/Http2ClientImpl.java | 133 ++- .../jdk/incubator/http/Http2Connection.java | 673 ++++++++---- .../jdk/incubator/http/HttpClient.java | 285 ++++-- .../incubator/http/HttpClientBuilderImpl.java | 15 +- .../jdk/incubator/http/HttpClientFacade.java | 141 +++ .../jdk/incubator/http/HttpClientImpl.java | 705 ++++++++++--- .../jdk/incubator/http/HttpConnection.java | 508 ++++++---- .../jdk/incubator/http/HttpHeaders.java | 115 ++- .../jdk/incubator/http/HttpRequest.java | 458 +++++---- .../http/HttpRequestBuilderImpl.java | 161 +-- .../jdk/incubator/http/HttpRequestImpl.java | 183 ++-- .../jdk/incubator/http/HttpResponse.java | 835 +++++++++------ .../jdk/incubator/http/HttpResponseImpl.java | 92 +- .../incubator/http/HttpTimeoutException.java | 2 +- .../jdk/incubator/http/ImmutableHeaders.java | 26 +- .../jdk/incubator/http/MultiExchange.java | 287 +++--- .../jdk/incubator/http/MultiMapResult.java | 11 +- .../incubator/http/PlainHttpConnection.java | 465 +++------ .../incubator/http/PlainProxyConnection.java | 2 +- .../http/PlainTunnelingConnection.java | 165 +-- .../incubator/http/PrivilegedExecutor.java | 69 ++ .../jdk/incubator/http/PseudoPublisher.java | 80 -- .../jdk/incubator/http/PullPublisher.java | 105 +- .../classes/jdk/incubator/http/PushGroup.java | 76 +- .../jdk/incubator/http/PushPublisher.java | 102 -- .../jdk/incubator/http/RawChannelImpl.java | 52 +- .../jdk/incubator/http/RedirectFilter.java | 8 +- ...Processors.java => RequestPublishers.java} | 125 ++- .../classes/jdk/incubator/http/Response.java | 25 +- .../jdk/incubator/http/ResponseContent.java | 553 ++++++---- .../jdk/incubator/http/ResponseHeaders.java | 191 ---- .../incubator/http/ResponseProcessors.java | 331 ------ .../incubator/http/ResponseSubscribers.java | 561 ++++++++++ .../jdk/incubator/http/SSLConnection.java | 205 ---- .../jdk/incubator/http/SSLDelegate.java | 137 ++- .../incubator/http/SSLTunnelConnection.java | 207 ---- .../jdk/incubator/http/SocketTube.java | 956 ++++++++++++++++++ .../classes/jdk/incubator/http/Stream.java | 647 +++++++----- .../classes/jdk/incubator/http/WebSocket.java | 776 ++++++-------- .../http/WebSocketHandshakeException.java | 2 +- .../jdk/incubator/http/WindowController.java | 163 ++- .../incubator/http/WindowUpdateSender.java | 12 +- .../internal/common/AsyncDataReadQueue.java | 212 ---- .../http/internal/common/AsyncWriteQueue.java | 235 ----- .../http/internal/common/ByteBufferPool.java | 2 +- .../internal/common/ByteBufferReference.java | 4 +- .../common/ConnectionExpiredException.java} | 42 +- .../http/internal/common/DebugLogger.java | 251 +++++ .../http/internal/common/Demand.java | 116 +++ .../http/internal/common/FlowTube.java | 185 ++++ .../http/internal/common/HttpHeadersImpl.java | 39 +- .../incubator/http/internal/common/Log.java | 61 +- .../http/internal/common/MinimalFuture.java | 18 +- .../incubator/http/internal/common/Pair.java | 2 +- .../http/internal/common/SSLFlowDelegate.java | 903 +++++++++++++++++ .../http/internal/common/SSLTube.java | 586 +++++++++++ .../internal/common/SequentialScheduler.java | 364 +++++++ .../internal/common/SubscriberWrapper.java | 460 +++++++++ .../internal/common/SubscriptionBase.java | 88 ++ .../incubator/http/internal/common/Utils.java | 518 ++++++---- .../internal/frame/ContinuationFrame.java | 12 +- .../http/internal/frame/DataFrame.java | 20 +- .../http/internal/frame/ErrorFrame.java | 6 +- .../http/internal/frame/FramesDecoder.java | 158 ++- .../http/internal/frame/FramesEncoder.java | 150 ++- .../http/internal/frame/GoAwayFrame.java | 2 +- .../http/internal/frame/HeaderFrame.java | 16 +- .../http/internal/frame/HeadersFrame.java | 14 +- .../http/internal/frame/Http2Frame.java | 14 +- .../http/internal/frame/MalformedFrame.java | 25 + .../http/internal/frame/OutgoingHeaders.java | 2 +- .../http/internal/frame/PingFrame.java | 2 +- .../http/internal/frame/PriorityFrame.java | 2 +- .../http/internal/frame/PushPromiseFrame.java | 8 +- .../http/internal/frame/ResetFrame.java | 2 +- .../http/internal/frame/SettingsFrame.java | 2 +- .../internal/frame/WindowUpdateFrame.java | 2 +- .../hpack/BinaryRepresentationWriter.java | 2 +- .../internal/hpack/BulkSizeUpdateWriter.java | 2 +- .../http/internal/hpack/Decoder.java | 161 ++- .../http/internal/hpack/DecodingCallback.java | 43 +- .../http/internal/hpack/Encoder.java | 154 ++- .../incubator/http/internal/hpack/HPACK.java | 174 ++++ .../http/internal/hpack/HeaderTable.java | 52 +- .../http/internal/hpack/Huffman.java | 41 +- .../http/internal/hpack/ISO_8859_1.java | 10 +- .../internal/hpack/IndexNameValueWriter.java | 5 +- .../http/internal/hpack/IndexedWriter.java | 2 +- .../http/internal/hpack/IntegerReader.java | 7 +- .../http/internal/hpack/IntegerWriter.java | 2 +- .../hpack/LiteralNeverIndexedWriter.java | 2 +- .../hpack/LiteralWithIndexingWriter.java | 2 +- .../http/internal/hpack/LiteralWriter.java | 2 +- .../http/internal/hpack/SizeUpdateWriter.java | 2 +- .../http/internal/hpack/StringReader.java | 5 +- .../http/internal/hpack/StringWriter.java | 6 +- .../http/internal/hpack/package-info.java | 2 +- .../http/internal/websocket/BuilderImpl.java | 66 +- .../websocket/CheckFailedException.java | 2 +- .../websocket/CooperativeHandler.java | 208 ---- .../websocket/FailWebSocketException.java | 2 +- .../internal/websocket/FrameConsumer.java | 4 +- .../websocket/MessageStreamConsumer.java | 10 +- .../internal/websocket/OpeningHandshake.java | 152 ++- .../internal/websocket/OutgoingMessage.java | 19 +- .../http/internal/websocket/RawChannel.java | 2 +- .../http/internal/websocket/Receiver.java | 138 +-- .../http/internal/websocket/Transmitter.java | 14 +- .../internal/websocket/TransportSupplier.java | 109 ++ .../websocket/UTF8AccumulatingDecoder.java | 2 +- .../internal/websocket/WebSocketImpl.java | 748 ++++++++------ .../internal/websocket/WebSocketRequest.java | 9 +- .../jdk/incubator/http/package-info.java | 2 +- .../share/classes/module-info.java | 3 +- test/jdk/ProblemList.txt | 2 - .../com/sun/net/httpserver/EchoHandler.java | 18 +- test/jdk/java/net/httpclient/APIErrors.java | 193 ---- .../java/net/httpclient/AbstractNoBody.java | 215 ++++ .../java/net/httpclient/BasicAuthTest.java | 4 +- .../BodyProcessorInputStreamTest.java | 161 +++ .../BufferingSubscriberCancelTest.java | 217 ++++ .../BufferingSubscriberErrorCompleteTest.java | 226 +++++ .../httpclient/BufferingSubscriberTest.java | 472 +++++++++ .../net/httpclient/CancelledResponse.java | 341 +++++++ .../httpclient/CustomRequestPublisher.java | 349 +++++++ .../httpclient/CustomResponseSubscriber.java | 283 ++++++ test/jdk/java/net/httpclient/EchoHandler.java | 2 +- .../net/httpclient/HandshakeFailureTest.java | 288 ++++++ test/jdk/java/net/httpclient/HeadersTest.java | 2 +- .../jdk/java/net/httpclient/HeadersTest1.java | 20 +- .../jdk/java/net/httpclient/HeadersTest2.java | 121 +++ .../net/httpclient/HttpClientBuilderTest.java | 244 +++++ .../net/httpclient/HttpInputStreamTest.java | 126 ++- .../httpclient/HttpRequestBuilderTest.java | 253 ++++- .../HttpResponseInputStreamTest.java | 246 +++++ .../java/net/httpclient/ImmutableHeaders.java | 2 +- .../httpclient/InterruptedBlockingSend.java | 69 ++ .../net/httpclient/LightWeightHttpServer.java | 7 +- .../jdk/java/net/httpclient/ManyRequests.java | 22 +- .../java/net/httpclient/ManyRequests2.java | 2 +- .../net/httpclient/ManyRequestsLegacy.java | 377 +++++++ .../net/httpclient/MessageHeadersTest.java | 2 +- .../{Server.java => MockServer.java} | 133 ++- .../java/net/httpclient/MultiAuthTest.java | 4 +- .../java/net/httpclient/NoBodyPartOne.java | 115 +++ .../java/net/httpclient/NoBodyPartTwo.java | 137 +++ .../java/net/httpclient/ProxyAuthTest.java | 43 +- test/jdk/java/net/httpclient/ProxyServer.java | 4 +- test/jdk/java/net/httpclient/ProxyTest.java | 38 +- .../java/net/httpclient/RequestBodyTest.java | 125 ++- .../net/httpclient/RequestBuilderTest.java | 378 +++++++ .../RequestProcessorExceptions.java | 91 ++ .../java/net/httpclient/ShortRequestBody.java | 159 +-- .../jdk/java/net/httpclient/SmallTimeout.java | 47 +- test/jdk/java/net/httpclient/SmokeTest.java | 373 ++++--- .../java/net/httpclient/SplitResponse.java | 175 +++- ...ggingHelper.java => SplitResponseSSL.java} | 24 +- test/jdk/java/net/httpclient/TestKit.java | 2 +- test/jdk/java/net/httpclient/TestKitTest.java | 2 +- .../jdk/java/net/httpclient/TimeoutBasic.java | 161 ++- .../java/net/httpclient/TimeoutOrdering.java | 2 - test/jdk/java/net/httpclient/VersionTest.java | 3 +- .../java/net/httpclient/ZeroRedirects.java | 117 +++ .../httpclient/docs/files/notsobigfile.txt | 2 +- .../httpclient/examples/WebSocketExample.java | 14 +- .../java/net/httpclient/http2/BasicTest.java | 100 +- .../http2/ContinuationFrameTest.java | 249 +++++ .../java/net/httpclient/http2/ErrorTest.java | 5 +- .../httpclient/http2/FixedThreadPoolTest.java | 16 +- ...Driver.java => HpackBinaryTestDriver.java} | 9 +- .../http2/HpackCircularBufferDriver.java | 34 + .../httpclient/http2/HpackDecoderDriver.java | 34 + .../httpclient/http2/HpackEncoderDriver.java | 34 + ...Table.java => HpackHeaderTableDriver.java} | 4 +- .../httpclient/http2/HpackHuffmanDriver.java | 34 + .../net/httpclient/http2/HpackTestHelper.java | 34 + .../jdk/java/net/httpclient/http2/NoBody.java | 93 -- .../java/net/httpclient/http2/ProxyTest2.java | 3 +- .../net/httpclient/http2/RedirectTest.java | 149 ++- .../java/net/httpclient/http2/ServerPush.java | 13 +- .../net/httpclient/http2/TLSConnection.java | 10 +- .../java/net/httpclient/http2/Timeout.java | 5 +- .../internal/hpack/BinaryPrimitivesTest.java | 38 +- .../internal/hpack/BuffersTestingKit.java | 2 +- .../internal/hpack/CircularBufferTest.java | 2 +- .../http/internal/hpack/DecoderTest.java | 76 +- .../http/internal/hpack/EncoderTest.java | 13 +- .../http/internal/hpack/HeaderTableTest.java | 79 +- .../http/internal/hpack/HuffmanTest.java | 12 +- .../http/internal/hpack/SpecHelper.java | 2 +- .../http/internal/hpack/TestHelper.java | 2 +- .../http2/server/BodyInputStream.java | 14 +- .../http2/server/BodyOutputStream.java | 15 +- .../httpclient/http2/server/EchoHandler.java | 2 +- .../http2/server}/ExceptionallyCloseable.java | 8 +- .../http2/server/Http2EchoHandler.java | 2 +- .../httpclient/http2/server/Http2Handler.java | 2 +- ...Handler.java => Http2RedirectHandler.java} | 46 +- .../http2/server/Http2TestExchange.java | 148 +-- .../http2/server/Http2TestExchangeImpl.java | 192 ++++ .../server/Http2TestExchangeSupplier.java | 54 + .../http2/server/Http2TestServer.java | 31 +- .../server/Http2TestServerConnection.java | 367 +++++-- .../http2/server/NoBodyHandler.java | 2 +- .../http2/server/OutgoingPushPromise.java | 2 +- .../httpclient/http2/server/PushHandler.java | 2 +- .../net/httpclient/http2/server}/Queue.java | 101 +- .../net/httpclient/http2/server/TestUtil.java | 2 +- .../jdk/java/net/httpclient/security/0.policy | 28 +- .../jdk/java/net/httpclient/security/1.policy | 28 +- .../java/net/httpclient/security/10.policy | 28 +- .../java/net/httpclient/security/11.policy | 28 +- .../java/net/httpclient/security/12.policy | 28 +- .../java/net/httpclient/security/14.policy | 28 +- .../java/net/httpclient/security/15.policy | 28 +- .../jdk/java/net/httpclient/security/2.policy | 28 +- .../jdk/java/net/httpclient/security/3.policy | 28 +- .../jdk/java/net/httpclient/security/4.policy | 28 +- .../jdk/java/net/httpclient/security/5.policy | 28 +- .../jdk/java/net/httpclient/security/6.policy | 28 +- .../jdk/java/net/httpclient/security/7.policy | 28 +- .../jdk/java/net/httpclient/security/8.policy | 28 +- .../jdk/java/net/httpclient/security/9.policy | 28 +- .../java/net/httpclient/security/Driver.java | 12 +- .../net/httpclient/security/Security.java | 22 +- .../FileProcessorPermissionTest.java | 138 +++ .../security/filePerms/httpclient.policy | 68 ++ ...iver.java => BuildingWebSocketDriver.java} | 14 +- .../websocket/ConnectionHandover.java | 22 +- .../websocket/DummyWebSocketServer.java | 52 +- .../websocket/HeaderWriterDriver.java | 30 + .../httpclient/websocket/MaskerDriver.java | 30 + .../httpclient/websocket/ReaderDriver.java | 30 + .../websocket/ReceivingTestDriver.java | 29 + .../websocket/SendingTestDriver.java | 29 + .../websocket/WSHandshakeException.java | 131 +++ .../websocket/BuildingWebSocketTest.java | 124 ++- .../http/internal/websocket/CloseTest.java | 246 ----- .../internal/websocket/DataProviders.java | 107 -- .../internal/websocket/HeaderWriterTest.java | 2 +- .../http/internal/websocket/MaskerTest.java | 2 +- .../http/internal/websocket/MockChannel.java | 415 -------- .../internal/websocket/MockChannelTest.java | 102 -- .../http/internal/websocket/MockListener.java | 395 ++++++-- .../internal/websocket/MockListenerTest.java | 99 -- .../http/internal/websocket/MockReceiver.java | 87 ++ .../internal/websocket/MockTransmitter.java | 71 ++ .../internal/websocket/MockTransport.java | 68 ++ .../http/internal/websocket/PingTest.java | 195 ---- .../http/internal/websocket/ReaderTest.java | 2 +- .../internal/websocket/ReceivingTest.java | 206 ++++ .../http/internal/websocket/SendingTest.java | 164 +++ .../http/internal/websocket/TestSupport.java | 223 +--- .../security/WSURLPermissionTest.java | 579 +++++++++++ .../websocket/security/httpclient.policy | 68 ++ .../whitebox/ConnectionPoolTestDriver.java | 32 + .../httpclient/whitebox/DemandTestDriver.java | 28 + .../java/net/httpclient/whitebox/Driver.java | 6 +- .../httpclient/whitebox/FlowTestDriver.java | 28 + .../whitebox/Http1HeaderParserTestDriver.java | 28 + .../whitebox/SSLEchoTubeTestDriver.java | 28 + .../whitebox/SSLTubeTestDriver.java | 28 + .../whitebox/WrapperTestDriver.java | 28 + .../incubator/http/AbstractRandomTest.java | 61 ++ .../incubator/http/AbstractSSLTubeTest.java | 320 ++++++ .../incubator/http/ConnectionPoolTest.java | 240 +++-- .../jdk/incubator/http/FlowTest.java | 547 ++++++++++ .../incubator/http/Http1HeaderParserTest.java | 372 +++++++ .../jdk/incubator/http/RawChannelTest.java | 20 +- .../incubator/http/ResponseHeadersTest.java | 223 ---- .../jdk/incubator/http/SSLEchoTubeTest.java | 420 ++++++++ .../jdk/incubator/http/SSLTubeTest.java | 275 +++++ .../jdk/incubator/http/SelectorTest.java | 99 +- .../jdk/incubator/http/WrapperTest.java | 263 +++++ .../http/internal/common/DemandTest.java | 202 ++++ 301 files changed, 27018 insertions(+), 11801 deletions(-) rename src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/{internal/common/BufferHandler.java => AbstractSubscription.java} (68%) delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java rename src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/{RequestProcessors.java => RequestPublishers.java} (67%) delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java rename src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/{AbstractPushPublisher.java => internal/common/ConnectionExpiredException.java} (58%) create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java delete mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java create mode 100644 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java delete mode 100644 test/jdk/java/net/httpclient/APIErrors.java create mode 100644 test/jdk/java/net/httpclient/AbstractNoBody.java create mode 100644 test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java create mode 100644 test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java create mode 100644 test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java create mode 100644 test/jdk/java/net/httpclient/BufferingSubscriberTest.java create mode 100644 test/jdk/java/net/httpclient/CancelledResponse.java create mode 100644 test/jdk/java/net/httpclient/CustomRequestPublisher.java create mode 100644 test/jdk/java/net/httpclient/CustomResponseSubscriber.java create mode 100644 test/jdk/java/net/httpclient/HandshakeFailureTest.java create mode 100644 test/jdk/java/net/httpclient/HeadersTest2.java create mode 100644 test/jdk/java/net/httpclient/HttpClientBuilderTest.java create mode 100644 test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java create mode 100644 test/jdk/java/net/httpclient/InterruptedBlockingSend.java create mode 100644 test/jdk/java/net/httpclient/ManyRequestsLegacy.java rename test/jdk/java/net/httpclient/{Server.java => MockServer.java} (64%) create mode 100644 test/jdk/java/net/httpclient/NoBodyPartOne.java create mode 100644 test/jdk/java/net/httpclient/NoBodyPartTwo.java create mode 100644 test/jdk/java/net/httpclient/RequestBuilderTest.java create mode 100644 test/jdk/java/net/httpclient/RequestProcessorExceptions.java rename test/jdk/java/net/httpclient/{websocket/LoggingHelper.java => SplitResponseSSL.java} (69%) create mode 100644 test/jdk/java/net/httpclient/ZeroRedirects.java create mode 100644 test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java rename test/jdk/java/net/httpclient/http2/{HpackDriver.java => HpackBinaryTestDriver.java} (72%) create mode 100644 test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java create mode 100644 test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java create mode 100644 test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java rename test/jdk/java/net/httpclient/http2/{HpackDriverHeaderTable.java => HpackHeaderTableDriver.java} (92%) create mode 100644 test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java create mode 100644 test/jdk/java/net/httpclient/http2/HpackTestHelper.java delete mode 100644 test/jdk/java/net/httpclient/http2/NoBody.java rename {src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common => test/jdk/java/net/httpclient/http2/server}/ExceptionallyCloseable.java (86%) rename test/jdk/java/net/httpclient/http2/server/{RedirectHandler.java => Http2RedirectHandler.java} (61%) create mode 100644 test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java create mode 100644 test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java rename {src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common => test/jdk/java/net/httpclient/http2/server}/Queue.java (63%) create mode 100644 test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java create mode 100644 test/jdk/java/net/httpclient/security/filePerms/httpclient.policy rename test/jdk/java/net/httpclient/websocket/{WSDriver.java => BuildingWebSocketDriver.java} (52%) create mode 100644 test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java create mode 100644 test/jdk/java/net/httpclient/websocket/MaskerDriver.java create mode 100644 test/jdk/java/net/httpclient/websocket/ReaderDriver.java create mode 100644 test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java create mode 100644 test/jdk/java/net/httpclient/websocket/SendingTestDriver.java create mode 100644 test/jdk/java/net/httpclient/websocket/WSHandshakeException.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java create mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java create mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java create mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java delete mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java create mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java create mode 100644 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java create mode 100644 test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java create mode 100644 test/jdk/java/net/httpclient/websocket/security/httpclient.policy create mode 100644 test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java delete mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java create mode 100644 test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java 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 10c3539b84b..ee758e4da94 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -551,9 +551,11 @@ class ServerImpl implements TimeSource { requestLine = req.requestLine(); if (requestLine == null) { /* connection closed */ + logger.log(Level.DEBUG, "no request line: closing"); closeConnection(connection); return; } + logger.log(Level.DEBUG, "Exchange request line: {0}", requestLine); int space = requestLine.indexOf (' '); if (space == -1) { reject (Code.HTTP_BAD_REQUEST, @@ -797,7 +799,8 @@ class ServerImpl implements TimeSource { // fashion. void requestCompleted (HttpConnection c) { - assert c.getState() == State.REQUEST; + State s = c.getState(); + assert s == State.REQUEST : "State is not REQUEST ("+s+")"; reqConnections.remove (c); c.rspStartedTime = getTime(); rspConnections.add (c); @@ -806,7 +809,8 @@ class ServerImpl implements TimeSource { // called after response has been sent void responseCompleted (HttpConnection c) { - assert c.getState() == State.RESPONSE; + State s = c.getState(); + assert s == State.RESPONSE : "State is not RESPONSE ("+s+")"; rspConnections.remove (c); c.setState (State.IDLE); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java index 07223e61f25..67dd2aa5023 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java @@ -28,9 +28,19 @@ package jdk.incubator.http; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.CompletableFuture; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import jdk.incubator.http.internal.common.ExceptionallyCloseable; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLParameters; + +import jdk.incubator.http.internal.common.SSLTube; +import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.Utils; /** @@ -52,34 +62,127 @@ import jdk.incubator.http.internal.common.ExceptionallyCloseable; * */ abstract class AbstractAsyncSSLConnection extends HttpConnection - implements AsyncConnection, ExceptionallyCloseable { +{ + protected final SSLEngine engine; + protected final String serverName; + protected final SSLParameters sslParameters; - - AbstractAsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client) { + AbstractAsyncSSLConnection(InetSocketAddress addr, + HttpClientImpl client, + String serverName, + String[] alpn) { super(addr, client); + this.serverName = serverName; + SSLContext context = client.theSSLContext(); + sslParameters = createSSLParameters(client, serverName, alpn); + Log.logParams(sslParameters); + engine = createEngine(context, sslParameters); } - abstract SSLEngine getEngine(); - abstract AsyncSSLDelegate sslDelegate(); abstract HttpConnection plainConnection(); - abstract HttpConnection downgrade(); + abstract SSLTube getConnectionFlow(); + + final CompletableFuture<String> getALPN() { + assert connected(); + return getConnectionFlow().getALPN(); + } + + final SSLEngine getEngine() { return engine; } + + private static SSLParameters createSSLParameters(HttpClientImpl client, + String serverName, + String[] alpn) { + SSLParameters sslp = client.sslParameters(); + SSLParameters sslParameters = Utils.copySSLParameters(sslp); + if (alpn != null) { + Log.logSSL("AbstractAsyncSSLConnection: Setting application protocols: {0}", + Arrays.toString(alpn)); + sslParameters.setApplicationProtocols(alpn); + } else { + Log.logSSL("AbstractAsyncSSLConnection: no applications set!"); + } + if (serverName != null) { + sslParameters.setServerNames(List.of(new SNIHostName(serverName))); + } + return sslParameters; + } + + private static SSLEngine createEngine(SSLContext context, + SSLParameters sslParameters) { + SSLEngine engine = context.createSSLEngine(); + engine.setUseClientMode(true); + engine.setSSLParameters(sslParameters); + return engine; + } @Override final boolean isSecure() { return true; } - // Blocking read functions not used here - @Override - protected final ByteBuffer readImpl() throws IOException { - throw new UnsupportedOperationException("Not supported."); + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. + static final class SSLConnectionChannel extends DetachedConnectionChannel { + final DetachedConnectionChannel delegate; + final SSLDelegate sslDelegate; + SSLConnectionChannel(DetachedConnectionChannel delegate, SSLDelegate sslDelegate) { + this.delegate = delegate; + this.sslDelegate = sslDelegate; + } + + SocketChannel channel() { + return delegate.channel(); + } + + @Override + ByteBuffer read() throws IOException { + SSLDelegate.WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192)); + // TODO: check for closure + int n = r.result.bytesProduced(); + if (n > 0) { + return r.buf; + } else if (n == 0) { + return Utils.EMPTY_BYTEBUFFER; + } else { + return null; + } + } + @Override + long write(ByteBuffer[] buffers, int start, int number) throws IOException { + long l = SSLDelegate.countBytes(buffers, start, number); + SSLDelegate.WrapperResult r = sslDelegate.sendData(buffers, start, number); + if (r.result.getStatus() == SSLEngineResult.Status.CLOSED) { + if (l > 0) { + throw new IOException("SSLHttpConnection closed"); + } + } + return l; + } + @Override + public void shutdownInput() throws IOException { + delegate.shutdownInput(); + } + @Override + public void shutdownOutput() throws IOException { + delegate.shutdownOutput(); + } + @Override + public void close() { + delegate.close(); + } } - // whenReceivedResponse only used in HTTP/1.1 (Http1Exchange) - // AbstractAsyncSSLConnection is only used with HTTP/2 + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. @Override - final CompletableFuture<Void> whenReceivingResponse() { - throw new UnsupportedOperationException("Not supported."); + DetachedConnectionChannel detachChannel() { + assert client() != null; + DetachedConnectionChannel detachedChannel = plainConnection().detachChannel(); + SSLDelegate sslDelegate = new SSLDelegate(engine, + detachedChannel.channel()); + return new SSLConnectionChannel(detachedChannel, sslDelegate); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java similarity index 68% rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java index 73ae0462799..859146c1db4 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -23,16 +23,23 @@ * questions. */ -package jdk.incubator.http.internal.common; +package jdk.incubator.http; -import java.nio.ByteBuffer; +import java.util.concurrent.Flow; +import jdk.incubator.http.internal.common.Demand; /** - * Implemented by buffer pools. + * A {@link Flow.Subscription} wrapping a {@link Demand} instance. + * */ -public interface BufferHandler { +abstract class AbstractSubscription implements Flow.Subscription { - ByteBuffer getBuffer(); + private final Demand demand = new Demand(); + + /** + * Returns the subscription's demand. + * @return the subscription's demand. + */ + protected Demand demand() { return demand; } - void returnBuffer(ByteBuffer buffer); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java deleted file mode 100644 index 39514653c34..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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. 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. - */ - -package jdk.incubator.http; - -import jdk.incubator.http.internal.common.ByteBufferReference; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -/** - * Implemented by classes that offer an asynchronous interface. - * - * PlainHttpConnection, AsyncSSLConnection AsyncSSLDelegate. - * - * setAsyncCallbacks() is called to set the callback for reading - * and error notification. Reads all happen on the selector thread, which - * must not block. - * - * Writing uses the same write() methods as used in blocking mode. - * Queues are employed on the writing side to buffer data while it is waiting - * to be sent. This strategy relies on HTTP/2 protocol flow control to stop - * outgoing queue from continually growing. Writes can be initiated by the - * calling thread, but if socket becomes full then the queue is emptied by - * the selector thread - */ -interface AsyncConnection { - - /** - * Enables asynchronous sending and receiving mode. The given async - * receiver will receive all incoming data. asyncInput() will be called - * to trigger reads. asyncOutput() will be called to drive writes. - * - * The errorReceiver callback must be called when any fatal exception - * occurs. Connection is assumed to be closed afterwards. - */ - void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier); - - - - /** - * Does whatever is required to start reading. Usually registers - * an event with the selector thread. - */ - void startReading(); - - /** - * Cancel asynchronous reading. Used to downgrade a HTTP/2 connection to HTTP/1 - */ - void stopAsyncReading(); - - /** - * In async mode, this method puts buffers at the end of the send queue. - * When in async mode, calling this method should later be followed by - * subsequent flushAsync invocation. - * That allows multiple threads to put buffers into the queue while some other - * thread is writing. - */ - void writeAsync(ByteBufferReference[] buffers) throws IOException; - - /** - * Re-enable asynchronous reads through the callback - */ - void enableCallback(); - - /** - * In async mode, this method may put buffers at the beginning of send queue, - * breaking frames sequence and allowing to write these buffers before other - * buffers in the queue. - * When in async mode, calling this method should later be followed by - * subsequent flushAsync invocation. - * That allows multiple threads to put buffers into the queue while some other - * thread is writing. - */ - void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException; - - /** - * This method should be called after any writeAsync/writeAsyncUnordered - * invocation. - * If there is a race to flushAsync from several threads one thread - * (race winner) capture flush operation and write the whole queue content. - * Other threads (race losers) exits from the method (not blocking) - * and continue execution. - */ - void flushAsync() throws IOException; -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java index e45aacfdfcf..655439934ea 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,23 +25,24 @@ package jdk.incubator.http; +import java.io.IOException; import java.nio.channels.SelectableChannel; /** * Event handling interface from HttpClientImpl's selector. * - * If BLOCKING is set, then the channel will be put in blocking - * mode prior to handle() being called. If false, then it remains non-blocking. - * * If REPEATING is set then the event is not cancelled after being posted. */ abstract class AsyncEvent { - public static final int BLOCKING = 0x1; // non blocking if not set public static final int REPEATING = 0x2; // one off event if not set protected final int flags; + AsyncEvent() { + this(0); + } + AsyncEvent(int flags) { this.flags = flags; } @@ -55,12 +56,13 @@ abstract class AsyncEvent { /** Called when event occurs */ public abstract void handle(); - /** Called when selector is shutting down. Abort all exchanges. */ - public abstract void abort(); - - public boolean blocking() { - return (flags & BLOCKING) != 0; - } + /** + * Called when an error occurs during registration, or when the selector has + * been shut down. Aborts all exchanges. + * + * @param ioe the IOException, or null + */ + public abstract void abort(IOException ioe); public boolean repeating() { return (flags & REPEATING) != 0; diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java index f6c095526ee..cc77383d1a5 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java @@ -26,37 +26,29 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; -import javax.net.ssl.SSLEngine; - -import jdk.incubator.http.internal.common.ByteBufferReference; +import jdk.incubator.http.internal.common.SSLTube; import jdk.incubator.http.internal.common.Utils; + /** * Asynchronous version of SSLConnection. */ class AsyncSSLConnection extends AbstractAsyncSSLConnection { - final AsyncSSLDelegate sslDelegate; final PlainHttpConnection plainConnection; - final String serverName; + final PlainHttpPublisher writePublisher; + private volatile SSLTube flow; - AsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) { - super(addr, client); + AsyncSSLConnection(InetSocketAddress addr, + HttpClientImpl client, + String[] alpn) { + super(addr, client, Utils.getServerName(addr), alpn); plainConnection = new PlainHttpConnection(addr, client); - serverName = Utils.getServerName(addr); - sslDelegate = new AsyncSSLDelegate(plainConnection, client, ap, serverName); - } - - @Override - synchronized void configureMode(Mode mode) throws IOException { - super.configureMode(mode); - plainConnection.configureMode(mode); + writePublisher = new PlainHttpPublisher(); } @Override @@ -64,30 +56,26 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection { return plainConnection; } - @Override - AsyncSSLDelegate sslDelegate() { - return sslDelegate; - } - - @Override - public void connect() throws IOException, InterruptedException { - plainConnection.connect(); - configureMode(Mode.ASYNC); - startReading(); - sslDelegate.connect(); - } - @Override public CompletableFuture<Void> connectAsync() { - // not used currently - throw new InternalError(); + return plainConnection + .connectAsync() + .thenApply( unused -> { + // create the SSLTube wrapping the SocketTube, with the given engine + flow = new SSLTube(engine, + client().theExecutor(), + plainConnection.getConnectionFlow()); + return null; } ); } @Override boolean connected() { - return plainConnection.connected() && sslDelegate.connected(); + return plainConnection.connected(); } + @Override + HttpPublisher publisher() { return writePublisher; } + @Override boolean isProxied() { return false; @@ -98,98 +86,30 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection { return plainConnection.channel(); } - @Override - public void enableCallback() { - sslDelegate.enableCallback(); - } - @Override ConnectionPool.CacheKey cacheKey() { return ConnectionPool.cacheKey(address, null); } - @Override - long write(ByteBuffer[] buffers, int start, int number) - throws IOException - { - ByteBuffer[] bufs = Utils.reduce(buffers, start, number); - long n = Utils.remaining(bufs); - sslDelegate.writeAsync(ByteBufferReference.toReferences(bufs)); - sslDelegate.flushAsync(); - return n; - } - - @Override - long write(ByteBuffer buffer) throws IOException { - long n = buffer.remaining(); - sslDelegate.writeAsync(ByteBufferReference.toReferences(buffer)); - sslDelegate.flushAsync(); - return n; - } - - @Override - public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - assert getMode() == Mode.ASYNC; - sslDelegate.writeAsyncUnordered(buffers); - } - - @Override - public void writeAsync(ByteBufferReference[] buffers) throws IOException { - assert getMode() == Mode.ASYNC; - sslDelegate.writeAsync(buffers); - } - - @Override - public void flushAsync() throws IOException { - sslDelegate.flushAsync(); - } - - @Override - public void closeExceptionally(Throwable cause) { - Utils.close(cause, sslDelegate, plainConnection.channel()); - } - @Override public void close() { - Utils.close(sslDelegate, plainConnection.channel()); + plainConnection.close(); } @Override void shutdownInput() throws IOException { + debug.log(Level.DEBUG, "plainConnection.channel().shutdownInput()"); plainConnection.channel().shutdownInput(); } @Override void shutdownOutput() throws IOException { + debug.log(Level.DEBUG, "plainConnection.channel().shutdownOutput()"); plainConnection.channel().shutdownOutput(); } - @Override - SSLEngine getEngine() { - return sslDelegate.getEngine(); - } - - @Override - public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier) { - sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); - plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer); - } - - @Override - public void startReading() { - plainConnection.startReading(); - sslDelegate.startReading(); - } - - @Override - public void stopAsyncReading() { - plainConnection.stopAsyncReading(); - } - - @Override - SSLConnection downgrade() { - return new SSLConnection(this); - } + @Override + SSLTube getConnectionFlow() { + return flow; + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java deleted file mode 100644 index e3341c148c2..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java +++ /dev/null @@ -1,692 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Semaphore; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import static javax.net.ssl.SSLEngineResult.Status.*; -import javax.net.ssl.*; - -import jdk.incubator.http.internal.common.AsyncWriteQueue; -import jdk.incubator.http.internal.common.ByteBufferPool; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Log; -import jdk.incubator.http.internal.common.Queue; -import jdk.incubator.http.internal.common.Utils; -import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; -import jdk.incubator.http.internal.common.ExceptionallyCloseable; - -/** - * Asynchronous wrapper around SSLEngine. send and receive is fully non - * blocking. When handshaking is required, a thread is created to perform - * the handshake and application level sends do not take place during this time. - * - * Is implemented using queues and functions operating on the receiving end - * of each queue. - * - * Application writes to: - * || - * \/ - * appOutputQ - * || - * \/ - * appOutputQ read by "upperWrite" method which does SSLEngine.wrap - * and does async write to PlainHttpConnection - * - * Reading side is as follows - * -------------------------- - * - * "upperRead" method reads off channelInputQ and calls SSLEngine.unwrap and - * when decrypted data is returned, it is passed to the user's Consumer<ByteBuffer> - * /\ - * || - * channelInputQ - * /\ - * || - * "asyncReceive" method puts buffers into channelInputQ. It is invoked from - * OP_READ events from the selector. - * - * Whenever handshaking is required, the doHandshaking() method is called - * which creates a thread to complete the handshake. It takes over the - * channelInputQ from upperRead, and puts outgoing packets on channelOutputQ. - * Selector events are delivered to asyncReceive and lowerWrite as normal. - * - * Errors - * - * Any exception thrown by the engine or channel, causes all Queues to be closed - * the channel to be closed, and the error is reported to the user's - * Consumer<Throwable> - */ -class AsyncSSLDelegate implements ExceptionallyCloseable, AsyncConnection { - - // outgoing buffers put in this queue first and may remain here - // while SSL handshaking happening. - final AsyncWriteQueue appOutputQ = new AsyncWriteQueue(this::upperWrite); - - // Bytes read into this queue before being unwrapped. Backup on this - // Q should only happen when the engine is stalled due to delegated tasks - final Queue<ByteBufferReference> channelInputQ; - - // input occurs through the read() method which is expected to be called - // when the selector signals some data is waiting to be read. All incoming - // handshake data is handled in this method, which means some calls to - // read() may return zero bytes of user data. This is not a sign of spinning, - // just that the handshake mechanics are being executed. - - final SSLEngine engine; - final SSLParameters sslParameters; - final HttpConnection lowerOutput; - final HttpClientImpl client; - final String serverName; - // should be volatile to provide proper synchronization(visibility) action - volatile Consumer<ByteBufferReference> asyncReceiver; - volatile Consumer<Throwable> errorHandler; - volatile boolean connected = false; - - // Locks. - final Object reader = new Object(); - // synchronizing handshake state - final Semaphore handshaker = new Semaphore(1); - final String[] alpn; - - // alpn[] may be null. upcall is callback which receives incoming decoded bytes off socket - - AsyncSSLDelegate(HttpConnection lowerOutput, HttpClientImpl client, String[] alpn, String sname) - { - SSLContext context = client.sslContext(); - this.serverName = sname; - engine = context.createSSLEngine(); - engine.setUseClientMode(true); - SSLParameters sslp = client.sslParameters() - .orElseGet(context::getSupportedSSLParameters); - sslParameters = Utils.copySSLParameters(sslp); - if (alpn != null) { - Log.logSSL("AsyncSSLDelegate: Setting application protocols: " + Arrays.toString(alpn)); - sslParameters.setApplicationProtocols(alpn); - } else { - Log.logSSL("AsyncSSLDelegate: no applications set!"); - } - if (serverName != null) { - SNIHostName sn = new SNIHostName(serverName); - sslParameters.setServerNames(List.of(sn)); - } - logParams(sslParameters); - engine.setSSLParameters(sslParameters); - this.lowerOutput = lowerOutput; - this.client = client; - this.channelInputQ = new Queue<>(); - this.channelInputQ.registerPutCallback(this::upperRead); - this.alpn = alpn; - } - - @Override - public void writeAsync(ByteBufferReference[] src) throws IOException { - appOutputQ.put(src); - } - - @Override - public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - appOutputQ.putFirst(buffers); - } - - @Override - public void flushAsync() throws IOException { - if (appOutputQ.flush()) { - lowerOutput.flushAsync(); - } - } - - SSLEngine getEngine() { - return engine; - } - - @Override - public void closeExceptionally(Throwable t) { - Utils.close(t, appOutputQ, channelInputQ, lowerOutput); - } - - @Override - public void close() { - Utils.close(appOutputQ, channelInputQ, lowerOutput); - } - - // The code below can be uncommented to shake out - // the implementation by inserting random delays and trigger - // handshake in the SelectorManager thread (upperRead) - // static final java.util.Random random = - // new java.util.Random(System.currentTimeMillis()); - - /** - * Attempts to wrap buffers from appOutputQ and place them on the - * channelOutputQ for writing. If handshaking is happening, then the - * process stalls and last buffers taken off the appOutputQ are put back - * into it until handshaking completes. - * - * This same method is called to try and resume output after a blocking - * handshaking operation has completed. - */ - private boolean upperWrite(ByteBufferReference[] refs, AsyncWriteQueue delayCallback) { - // currently delayCallback is not used. Use it when it's needed to execute handshake in another thread. - try { - ByteBuffer[] buffers = ByteBufferReference.toBuffers(refs); - int bytes = Utils.remaining(buffers); - while (bytes > 0) { - EngineResult r = wrapBuffers(buffers); - int bytesProduced = r.bytesProduced(); - int bytesConsumed = r.bytesConsumed(); - bytes -= bytesConsumed; - if (bytesProduced > 0) { - lowerOutput.writeAsync(new ByteBufferReference[]{r.destBuffer}); - } - - // The code below can be uncommented to shake out - // the implementation by inserting random delays and trigger - // handshake in the SelectorManager thread (upperRead) - - // int sleep = random.nextInt(100); - // if (sleep > 20) { - // Thread.sleep(sleep); - // } - - // handshaking is happening or is needed - if (r.handshaking()) { - Log.logTrace("Write: needs handshake"); - doHandshakeNow("Write"); - } - } - ByteBufferReference.clear(refs); - } catch (Throwable t) { - closeExceptionally(t); - errorHandler.accept(t); - } - // We always return true: either all the data was sent, or - // an exception happened and we have closed the queue. - return true; - } - - // Connecting at this level means the initial handshake has completed. - // This means that the initial SSL parameters are available including - // ALPN result. - void connect() throws IOException, InterruptedException { - doHandshakeNow("Init"); - connected = true; - } - - boolean connected() { - return connected; - } - - private void startHandshake(String tag) { - Runnable run = () -> { - try { - doHandshakeNow(tag); - } catch (Throwable t) { - Log.logTrace("{0}: handshake failed: {1}", tag, t); - closeExceptionally(t); - errorHandler.accept(t); - } - }; - client.executor().execute(run); - } - - private void doHandshakeNow(String tag) - throws IOException, InterruptedException - { - handshaker.acquire(); - try { - channelInputQ.disableCallback(); - lowerOutput.flushAsync(); - Log.logTrace("{0}: Starting handshake...", tag); - doHandshakeImpl(); - Log.logTrace("{0}: Handshake completed", tag); - // don't unblock the channel here, as we aren't sure yet, whether ALPN - // negotiation succeeded. Caller will call enableCallback() externally - } finally { - handshaker.release(); - } - } - - public void enableCallback() { - channelInputQ.enableCallback(); - } - - /** - * Executes entire handshake in calling thread. - * Returns after handshake is completed or error occurs - */ - private void doHandshakeImpl() throws IOException { - engine.beginHandshake(); - while (true) { - SSLEngineResult.HandshakeStatus status = engine.getHandshakeStatus(); - switch(status) { - case NEED_TASK: { - List<Runnable> tasks = obtainTasks(); - for (Runnable task : tasks) { - task.run(); - } - } break; - case NEED_WRAP: - handshakeWrapAndSend(); - break; - case NEED_UNWRAP: case NEED_UNWRAP_AGAIN: - handshakeReceiveAndUnWrap(); - break; - case FINISHED: - return; - case NOT_HANDSHAKING: - return; - default: - throw new InternalError("Unexpected Handshake Status: " - + status); - } - } - } - - // acknowledge a received CLOSE request from peer - void doClosure() throws IOException { - //while (!wrapAndSend(emptyArray)) - //; - } - - List<Runnable> obtainTasks() { - List<Runnable> l = new ArrayList<>(); - Runnable r; - while ((r = engine.getDelegatedTask()) != null) { - l.add(r); - } - return l; - } - - @Override - public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier) { - this.asyncReceiver = asyncReceiver; - this.errorHandler = errorReceiver; - // readBufferSupplier is not used, - // because of AsyncSSLDelegate has its own appBufferPool - } - - @Override - public void startReading() { - // maybe this class does not need to implement AsyncConnection - } - - @Override - public void stopAsyncReading() { - // maybe this class does not need to implement AsyncConnection - } - - - static class EngineResult { - final SSLEngineResult result; - final ByteBufferReference destBuffer; - - - // normal result - EngineResult(SSLEngineResult result) { - this(result, null); - } - - EngineResult(SSLEngineResult result, ByteBufferReference destBuffer) { - this.result = result; - this.destBuffer = destBuffer; - } - - boolean handshaking() { - SSLEngineResult.HandshakeStatus s = result.getHandshakeStatus(); - return s != FINISHED && s != NOT_HANDSHAKING; - } - - int bytesConsumed() { - return result.bytesConsumed(); - } - - int bytesProduced() { - return result.bytesProduced(); - } - - SSLEngineResult.HandshakeStatus handshakeStatus() { - return result.getHandshakeStatus(); - } - - SSLEngineResult.Status status() { - return result.getStatus(); - } - } - - EngineResult handshakeWrapAndSend() throws IOException { - EngineResult r = wrapBuffer(Utils.EMPTY_BYTEBUFFER); - if (r.bytesProduced() > 0) { - lowerOutput.writeAsync(new ByteBufferReference[]{r.destBuffer}); - lowerOutput.flushAsync(); - } - return r; - } - - // called during handshaking. It blocks until a complete packet - // is available, unwraps it and returns. - void handshakeReceiveAndUnWrap() throws IOException { - ByteBufferReference ref = channelInputQ.take(); - while (true) { - // block waiting for input - EngineResult r = unwrapBuffer(ref.get()); - SSLEngineResult.Status status = r.status(); - if (status == BUFFER_UNDERFLOW) { - // wait for another buffer to arrive - ByteBufferReference ref1 = channelInputQ.take(); - ref = combine (ref, ref1); - continue; - } - // OK - // theoretically possible we could receive some user data - if (r.bytesProduced() > 0) { - asyncReceiver.accept(r.destBuffer); - } else { - r.destBuffer.clear(); - } - // it is also possible that a delegated task could be needed - // even though they are handled in the calling function - if (r.handshakeStatus() == NEED_TASK) { - obtainTasks().stream().forEach((task) -> task.run()); - } - - if (!ref.get().hasRemaining()) { - ref.clear(); - return; - } - } - } - - EngineResult wrapBuffer(ByteBuffer src) throws SSLException { - ByteBuffer[] bufs = new ByteBuffer[1]; - bufs[0] = src; - return wrapBuffers(bufs); - } - - private final ByteBufferPool netBufferPool = new ByteBufferPool(); - private final ByteBufferPool appBufferPool = new ByteBufferPool(); - - /** - * provides buffer of sslEngine@getPacketBufferSize(). - * used for encrypted buffers after wrap or before unwrap. - * @return ByteBufferReference - */ - public ByteBufferReference getNetBuffer() { - return netBufferPool.get(engine.getSession().getPacketBufferSize()); - } - - /** - * provides buffer of sslEngine@getApplicationBufferSize(). - * @return ByteBufferReference - */ - private ByteBufferReference getAppBuffer() { - return appBufferPool.get(engine.getSession().getApplicationBufferSize()); - } - - EngineResult wrapBuffers(ByteBuffer[] src) throws SSLException { - ByteBufferReference dst = getNetBuffer(); - while (true) { - SSLEngineResult sslResult = engine.wrap(src, dst.get()); - switch (sslResult.getStatus()) { - case BUFFER_OVERFLOW: - // Shouldn't happen. We allocated buffer with packet size - // get it again if net buffer size was changed - dst = getNetBuffer(); - break; - case CLOSED: - case OK: - dst.get().flip(); - return new EngineResult(sslResult, dst); - case BUFFER_UNDERFLOW: - // Shouldn't happen. Doesn't returns when wrap() - // underflow handled externally - return new EngineResult(sslResult); - default: - assert false; - } - } - } - - EngineResult unwrapBuffer(ByteBuffer srcbuf) throws IOException { - ByteBufferReference dst = getAppBuffer(); - while (true) { - SSLEngineResult sslResult = engine.unwrap(srcbuf, dst.get()); - switch (sslResult.getStatus()) { - case BUFFER_OVERFLOW: - // may happen only if app size buffer was changed. - // get it again if app buffer size changed - dst = getAppBuffer(); - break; - case CLOSED: - doClosure(); - throw new IOException("Engine closed"); - case BUFFER_UNDERFLOW: - dst.clear(); - return new EngineResult(sslResult); - case OK: - dst.get().flip(); - return new EngineResult(sslResult, dst); - } - } - } - - /** - * Asynchronous read input. Call this when selector fires. - * Unwrap done in upperRead because it also happens in - * doHandshake() when handshake taking place - */ - public void asyncReceive(ByteBufferReference buffer) { - try { - channelInputQ.put(buffer); - } catch (Throwable t) { - closeExceptionally(t); - errorHandler.accept(t); - } - } - - private ByteBufferReference pollInput() throws IOException { - return channelInputQ.poll(); - } - - private ByteBufferReference pollInput(ByteBufferReference next) throws IOException { - return next == null ? channelInputQ.poll() : next; - } - - public void upperRead() { - ByteBufferReference src; - ByteBufferReference next = null; - synchronized (reader) { - try { - src = pollInput(); - if (src == null) { - return; - } - while (true) { - EngineResult r = unwrapBuffer(src.get()); - switch (r.result.getStatus()) { - case BUFFER_UNDERFLOW: - // Buffer too small. Need to combine with next buf - next = pollInput(next); - if (next == null) { - // no data available. - // push buffer back until more data available - channelInputQ.pushback(src); - return; - } else { - src = shift(src, next); - if (!next.get().hasRemaining()) { - next.clear(); - next = null; - } - } - break; - case OK: - // check for any handshaking work - if (r.handshaking()) { - // handshaking is happening or is needed - // so we put the buffer back on Q to process again - // later. - Log.logTrace("Read: needs handshake"); - channelInputQ.pushback(src); - startHandshake("Read"); - return; - } - asyncReceiver.accept(r.destBuffer); - } - if (src.get().hasRemaining()) { - continue; - } - src.clear(); - src = pollInput(next); - next = null; - if (src == null) { - return; - } - } - } catch (Throwable t) { - closeExceptionally(t); - errorHandler.accept(t); - } - } - } - - ByteBufferReference shift(ByteBufferReference ref1, ByteBufferReference ref2) { - ByteBuffer buf1 = ref1.get(); - if (buf1.capacity() < engine.getSession().getPacketBufferSize()) { - ByteBufferReference newRef = getNetBuffer(); - ByteBuffer newBuf = newRef.get(); - newBuf.put(buf1); - buf1 = newBuf; - ref1.clear(); - ref1 = newRef; - } else { - buf1.compact(); - } - ByteBuffer buf2 = ref2.get(); - Utils.copy(buf2, buf1, Math.min(buf1.remaining(), buf2.remaining())); - buf1.flip(); - return ref1; - } - - - ByteBufferReference combine(ByteBufferReference ref1, ByteBufferReference ref2) { - ByteBuffer buf1 = ref1.get(); - ByteBuffer buf2 = ref2.get(); - int avail1 = buf1.capacity() - buf1.remaining(); - if (buf2.remaining() < avail1) { - buf1.compact(); - buf1.put(buf2); - buf1.flip(); - ref2.clear(); - return ref1; - } - int newsize = buf1.remaining() + buf2.remaining(); - ByteBuffer newbuf = ByteBuffer.allocate(newsize); // getting rid of buffer pools - newbuf.put(buf1); - newbuf.put(buf2); - newbuf.flip(); - ref1.clear(); - ref2.clear(); - return ByteBufferReference.of(newbuf); - } - - SSLParameters getSSLParameters() { - return sslParameters; - } - - static void logParams(SSLParameters p) { - if (!Log.ssl()) { - return; - } - - if (p == null) { - Log.logSSL("SSLParameters: Null params"); - return; - } - - final StringBuilder sb = new StringBuilder("SSLParameters:"); - final List<Object> params = new ArrayList<>(); - if (p.getCipherSuites() != null) { - for (String cipher : p.getCipherSuites()) { - sb.append("\n cipher: {") - .append(params.size()).append("}"); - params.add(cipher); - } - } - - // SSLParameters.getApplicationProtocols() can't return null - // JDK 8 EXCL START - for (String approto : p.getApplicationProtocols()) { - sb.append("\n application protocol: {") - .append(params.size()).append("}"); - params.add(approto); - } - // JDK 8 EXCL END - - if (p.getProtocols() != null) { - for (String protocol : p.getProtocols()) { - sb.append("\n protocol: {") - .append(params.size()).append("}"); - params.add(protocol); - } - } - - if (p.getServerNames() != null) { - for (SNIServerName sname : p.getServerNames()) { - sb.append("\n server name: {") - .append(params.size()).append("}"); - params.add(sname.toString()); - } - } - sb.append('\n'); - - Log.logSSL(sb.toString(), params.toArray()); - } - - String getSessionInfo() { - StringBuilder sb = new StringBuilder(); - String application = engine.getApplicationProtocol(); - SSLSession sess = engine.getSession(); - String cipher = sess.getCipherSuite(); - String protocol = sess.getProtocol(); - sb.append("Handshake complete alpn: ") - .append(application) - .append(", Cipher: ") - .append(cipher) - .append(", Protocol: ") - .append(protocol); - return sb.toString(); - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java index 7afb87f5191..1497db8c050 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java @@ -26,15 +26,11 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import jdk.incubator.http.internal.common.ByteBufferReference; +import jdk.incubator.http.internal.common.SSLTube; import jdk.incubator.http.internal.common.Utils; /** @@ -43,48 +39,43 @@ import jdk.incubator.http.internal.common.Utils; class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { final PlainTunnelingConnection plainConnection; - final AsyncSSLDelegate sslDelegate; - final String serverName; + final PlainHttpPublisher writePublisher; + volatile SSLTube flow; - @Override - public void connect() throws IOException, InterruptedException { - plainConnection.connect(); - configureMode(Mode.ASYNC); - startReading(); - sslDelegate.connect(); - } - - @Override - boolean connected() { - return plainConnection.connected() && sslDelegate.connected(); + AsyncSSLTunnelConnection(InetSocketAddress addr, + HttpClientImpl client, + String[] alpn, + InetSocketAddress proxy) + { + super(addr, client, Utils.getServerName(addr), alpn); + this.plainConnection = new PlainTunnelingConnection(addr, proxy, client); + this.writePublisher = new PlainHttpPublisher(); } @Override public CompletableFuture<Void> connectAsync() { - throw new InternalError(); - } - - AsyncSSLTunnelConnection(InetSocketAddress addr, - HttpClientImpl client, - String[] alpn, - InetSocketAddress proxy) - { - super(addr, client); - this.serverName = Utils.getServerName(addr); - this.plainConnection = new PlainTunnelingConnection(addr, proxy, client); - this.sslDelegate = new AsyncSSLDelegate(plainConnection, client, alpn, serverName); + debug.log(Level.DEBUG, "Connecting plain tunnel connection"); + // This will connect the PlainHttpConnection flow, so that + // its HttpSubscriber and HttpPublisher are subscribed to the + // SocketTube + return plainConnection + .connectAsync() + .thenApply( unused -> { + debug.log(Level.DEBUG, "creating SSLTube"); + // create the SSLTube wrapping the SocketTube, with the given engine + flow = new SSLTube(engine, + client().theExecutor(), + plainConnection.getConnectionFlow()); + return null;} ); } @Override - synchronized void configureMode(Mode mode) throws IOException { - super.configureMode(mode); - plainConnection.configureMode(mode); + boolean connected() { + return plainConnection.connected(); // && sslDelegate.connected(); } @Override - SSLParameters sslParameters() { - return sslDelegate.getSSLParameters(); - } + HttpPublisher publisher() { return writePublisher; } @Override public String toString() { @@ -96,53 +87,14 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { return plainConnection; } - @Override - AsyncSSLDelegate sslDelegate() { - return sslDelegate; - } - @Override ConnectionPool.CacheKey cacheKey() { return ConnectionPool.cacheKey(address, plainConnection.proxyAddr); } - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - //debugPrint("Send", buffers, start, number); - ByteBuffer[] bufs = Utils.reduce(buffers, start, number); - long n = Utils.remaining(bufs); - sslDelegate.writeAsync(ByteBufferReference.toReferences(bufs)); - sslDelegate.flushAsync(); - return n; - } - - @Override - long write(ByteBuffer buffer) throws IOException { - //debugPrint("Send", buffer); - long n = buffer.remaining(); - sslDelegate.writeAsync(ByteBufferReference.toReferences(buffer)); - sslDelegate.flushAsync(); - return n; - } - - @Override - public void writeAsync(ByteBufferReference[] buffers) throws IOException { - sslDelegate.writeAsync(buffers); - } - - @Override - public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - sslDelegate.writeAsyncUnordered(buffers); - } - - @Override - public void flushAsync() throws IOException { - sslDelegate.flushAsync(); - } - @Override public void close() { - Utils.close(sslDelegate, plainConnection.channel()); + plainConnection.close(); } @Override @@ -166,41 +118,7 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { } @Override - public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier) { - sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); - plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer); - } - - @Override - public void startReading() { - plainConnection.startReading(); - sslDelegate.startReading(); - } - - @Override - public void stopAsyncReading() { - plainConnection.stopAsyncReading(); - } - - @Override - public void enableCallback() { - sslDelegate.enableCallback(); - } - - @Override - public void closeExceptionally(Throwable cause) throws IOException { - Utils.close(cause, sslDelegate, plainConnection.channel()); - } - - @Override - SSLEngine getEngine() { - return sslDelegate.getEngine(); - } - - @Override - SSLTunnelConnection downgrade() { - return new SSLTunnelConnection(this); - } + SSLTube getConnectionFlow() { + return flow; + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java new file mode 100644 index 00000000000..5becb011232 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * An asynchronous event which is triggered only once from the selector manager + * thread as soon as event registration are handled. + */ +final class AsyncTriggerEvent extends AsyncEvent{ + + private final Runnable trigger; + private final Consumer<? super IOException> errorHandler; + AsyncTriggerEvent(Consumer<? super IOException> errorHandler, + Runnable trigger) { + super(0); + this.trigger = Objects.requireNonNull(trigger); + this.errorHandler = Objects.requireNonNull(errorHandler); + } + /** Returns null */ + @Override + public SelectableChannel channel() { return null; } + /** Returns 0 */ + @Override + public int interestOps() { return 0; } + @Override + public void handle() { trigger.run(); } + @Override + public void abort(IOException ioe) { errorHandler.accept(ioe); } + @Override + public boolean repeating() { return false; } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java index a6ff07d884e..9659ce43022 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -54,6 +54,9 @@ class AuthenticationFilter implements HeaderFilter { static final int UNAUTHORIZED = 401; static final int PROXY_UNAUTHORIZED = 407; + // A public no-arg constructor is required by FilterFactory + public AuthenticationFilter() {} + private PasswordAuthentication getCredentials(String header, boolean proxy, HttpRequestImpl req) @@ -83,7 +86,7 @@ class AuthenticationFilter implements HeaderFilter { } private URI getProxyURI(HttpRequestImpl r) { - InetSocketAddress proxy = r.proxy(exchange.client()); + InetSocketAddress proxy = r.proxy(); if (proxy == null) { return null; } @@ -216,7 +219,6 @@ class AuthenticationFilter implements HeaderFilter { return null; // error gets returned to app } - String realm = parser.findValue("realm"); AuthInfo au = proxy ? exchange.proxyauth : exchange.serverauth; if (au == null) { PasswordAuthentication pw = getCredentials(authval, proxy, req); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java deleted file mode 100644 index fa49fe5eb33..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.util.Optional; -import java.util.concurrent.Flow; -import jdk.incubator.http.internal.common.Log; - -/** - * A Publisher which is assumed to run in its own thread. - * - * acceptData() may therefore block while waiting for subscriber demand - */ -class BlockingPushPublisher<T> extends AbstractPushPublisher<T> { - volatile Subscription subscription; - volatile Flow.Subscriber<? super T> subscriber; - volatile SubscriptionState state; - long demand; - - @Override - public void subscribe(Flow.Subscriber<? super T> subscriber) { - state = SubscriptionState.OPENED; - subscription = new Subscription(subscriber); - subscriber.onSubscribe(subscription); - } - - /** - * Entry point for supplying items to publisher. This call will block - * when no demand available. - */ - @Override - public void acceptData(Optional<T> item) throws InterruptedException { - SubscriptionState s = this.state; - - // do not use switch(this.state): this.state could be null. - if (s == SubscriptionState.CANCELLED) return; - if (s == SubscriptionState.DONE) { - throw new IllegalStateException("subscription complete"); - } - - if (!item.isPresent()) { - subscriber.onComplete(); - this.state = SubscriptionState.DONE; - } else { - obtainPermit(); - if (this.state == SubscriptionState.CANCELLED) return; - subscriber.onNext(item.get()); - } - } - - /** - * Terminates the publisher with given exception. - */ - @Override - public void acceptError(Throwable t) { - if (this.state != SubscriptionState.OPENED) { - Log.logError(t); - return; - } - subscriber.onError(t); - cancel(); - } - - private synchronized void obtainPermit() throws InterruptedException { - while (demand == 0) { - wait(); - } - if (this.state == SubscriptionState.DONE) { - throw new IllegalStateException("subscription complete"); - } - demand --; - } - - synchronized void addPermits(long n) { - long old = demand; - demand += n; - if (old == 0) { - notifyAll(); - } - } - - synchronized void cancel() { - this.state = SubscriptionState.CANCELLED; - notifyAll(); - } - - private class Subscription implements Flow.Subscription { - - Subscription(Flow.Subscriber<? super T> subscriber) { - BlockingPushPublisher.this.subscriber = subscriber; - } - - @Override - public void request(long n) { - addPermits(n); - } - - @Override - public void cancel() { - BlockingPushPublisher.this.cancel(); - } - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java new file mode 100644 index 00000000000..270da1688cc --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.Utils; + +/** + * A buffering BodySubscriber. When subscribed, accumulates ( buffers ) a given + * amount ( in bytes ) of a publisher's data before pushing it to a downstream + * subscriber. + */ +class BufferingSubscriber<T> implements HttpResponse.BodySubscriber<T> +{ + /** The downstream consumer of the data. */ + private final HttpResponse.BodySubscriber<T> downstreamSubscriber; + /** The amount of data to be accumulate before pushing downstream. */ + private final int bufferSize; + + /** The subscription, created lazily. */ + private volatile Flow.Subscription subscription; + /** The downstream subscription, created lazily. */ + private volatile DownstreamSubscription downstreamSubscription; + + /** Must be held when accessing the internal buffers. */ + private final Object buffersLock = new Object(); + /** The internal buffers holding the buffered data. */ + private ArrayList<ByteBuffer> internalBuffers; + /** The actual accumulated remaining bytes in internalBuffers. */ + private int accumulatedBytes; + + /** Holds the Throwable from upstream's onError. */ + private volatile Throwable throwable; + + /** State of the buffering subscriber: + * 1) [UNSUBSCRIBED] when initially created + * 2) [ACTIVE] when subscribed and can receive data + * 3) [ERROR | CANCELLED | COMPLETE] (terminal state) + */ + static final int UNSUBSCRIBED = 0x01; + static final int ACTIVE = 0x02; + static final int ERROR = 0x04; + static final int CANCELLED = 0x08; + static final int COMPLETE = 0x10; + + private volatile int state; + + BufferingSubscriber(HttpResponse.BodySubscriber<T> downstreamSubscriber, + int bufferSize) { + this.downstreamSubscriber = downstreamSubscriber; + this.bufferSize = bufferSize; + synchronized (buffersLock) { + internalBuffers = new ArrayList<>(); + } + state = UNSUBSCRIBED; + } + + /** Returns the number of bytes remaining in the given buffers. */ + private static final long remaining(List<ByteBuffer> buffers) { + return buffers.stream().mapToLong(ByteBuffer::remaining).sum(); + } + + /** + * Tells whether, or not, there is at least a sufficient number of bytes + * accumulated in the internal buffers. If the subscriber is COMPLETE, and + * has some buffered data, then there is always enough ( to pass downstream ). + */ + private final boolean hasEnoughAccumulatedBytes() { + assert Thread.holdsLock(buffersLock); + return accumulatedBytes >= bufferSize + || (state == COMPLETE && accumulatedBytes > 0); + } + + /** + * Returns a new, unmodifiable, List<ByteBuffer> containing exactly the + * amount of data as required before pushing downstream. The amount of data + * may be less than required ( bufferSize ), in the case where the subscriber + * is COMPLETE. + */ + private List<ByteBuffer> fromInternalBuffers() { + assert Thread.holdsLock(buffersLock); + int leftToFill = bufferSize; + int state = this.state; + assert (state == ACTIVE || state == CANCELLED) + ? accumulatedBytes >= leftToFill : true; + List<ByteBuffer> dsts = new ArrayList<>(); + + ListIterator<ByteBuffer> itr = internalBuffers.listIterator(); + while (itr.hasNext()) { + ByteBuffer b = itr.next(); + if (b.remaining() <= leftToFill) { + itr.remove(); + if (b.position() != 0) + b = b.slice(); // ensure position = 0 when propagated + dsts.add(b); + leftToFill -= b.remaining(); + accumulatedBytes -= b.remaining(); + if (leftToFill == 0) + break; + } else { + int prevLimit = b.limit(); + b.limit(b.position() + leftToFill); + ByteBuffer slice = b.slice(); + dsts.add(slice); + b.limit(prevLimit); + b.position(b.position() + leftToFill); + accumulatedBytes -= leftToFill; + leftToFill = 0; + break; + } + } + assert (state == ACTIVE || state == CANCELLED) + ? leftToFill == 0 : state == COMPLETE; + assert (state == ACTIVE || state == CANCELLED) + ? remaining(dsts) == bufferSize : state == COMPLETE; + assert accumulatedBytes >= 0; + assert dsts.stream().noneMatch(b -> b.position() != 0); + return Collections.unmodifiableList(dsts); + } + + /** Subscription that is passed to the downstream subscriber. */ + private class DownstreamSubscription implements Flow.Subscription { + private final AtomicBoolean cancelled = new AtomicBoolean(); // false + private final Demand demand = new Demand(); + private volatile boolean illegalArg; + + @Override + public void request(long n) { + if (cancelled.get() || illegalArg) { + return; + } + if (n <= 0L) { + // pass the "bad" value upstream so the Publisher can deal with + // it appropriately, i.e. invoke onError + illegalArg = true; + subscription.request(n); + return; + } + + demand.increase(n); + + pushDemanded(); + } + + private final SequentialScheduler pushDemandedScheduler = + new SequentialScheduler(new PushDemandedTask()); + + void pushDemanded() { + if (cancelled.get()) + return; + pushDemandedScheduler.runOrSchedule(); + } + + class PushDemandedTask extends SequentialScheduler.CompleteRestartableTask { + @Override + public void run() { + try { + Throwable t = throwable; + if (t != null) { + pushDemandedScheduler.stop(); // stop the demand scheduler + downstreamSubscriber.onError(t); + return; + } + + while (true) { + List<ByteBuffer> item; + synchronized (buffersLock) { + if (cancelled.get()) + return; + if (!hasEnoughAccumulatedBytes()) + break; + if (!demand.tryDecrement()) + break; + item = fromInternalBuffers(); + } + assert item != null; + + downstreamSubscriber.onNext(item); + } + if (cancelled.get()) + return; + + // complete only if all data consumed + boolean complete; + synchronized (buffersLock) { + complete = state == COMPLETE && internalBuffers.isEmpty(); + } + if (complete) { + assert internalBuffers.isEmpty(); + pushDemandedScheduler.stop(); // stop the demand scheduler + downstreamSubscriber.onComplete(); + return; + } + } catch (Throwable t) { + cancel(); // cancel if there is any error + throw t; + } + + boolean requestMore = false; + synchronized (buffersLock) { + if (!hasEnoughAccumulatedBytes() && !demand.isFulfilled()) { + // request more upstream data + requestMore = true; + } + } + if (requestMore) + subscription.request(1); + } + } + + @Override + public void cancel() { + if (cancelled.compareAndExchange(false, true)) + return; // already cancelled + + state = CANCELLED; // set CANCELLED state of upstream subscriber + subscription.cancel(); // cancel upstream subscription + pushDemandedScheduler.stop(); // stop the demand scheduler + } + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + Objects.requireNonNull(subscription); + if (this.subscription != null) { + subscription.cancel(); + return; + } + + int s = this.state; + assert s == UNSUBSCRIBED; + state = ACTIVE; + this.subscription = subscription; + downstreamSubscription = new DownstreamSubscription(); + downstreamSubscriber.onSubscribe(downstreamSubscription); + } + + @Override + public void onNext(List<ByteBuffer> item) { + Objects.requireNonNull(item); + + int s = state; + if (s == CANCELLED) + return; + + if (s != ACTIVE) + throw new InternalError("onNext on inactive subscriber"); + + synchronized (buffersLock) { + accumulatedBytes += Utils.accumulateBuffers(internalBuffers, item); + } + + downstreamSubscription.pushDemanded(); + } + + @Override + public void onError(Throwable incomingThrowable) { + Objects.requireNonNull(incomingThrowable); + int s = state; + assert s == ACTIVE : "Expected ACTIVE, got:" + s; + state = ERROR; + Throwable t = this.throwable; + assert t == null : "Expected null, got:" + t; + this.throwable = incomingThrowable; + downstreamSubscription.pushDemanded(); + } + + @Override + public void onComplete() { + int s = state; + assert s == ACTIVE : "Expected ACTIVE, got:" + s; + state = COMPLETE; + downstreamSubscription.pushDemanded(); + } + + @Override + public CompletionStage<T> getBody() { + return downstreamSubscriber.getBody(); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java index df627809111..38f9b3d2546 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,14 +25,24 @@ package jdk.incubator.http; -import java.lang.ref.WeakReference; +import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.ListIterator; import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Optional; +import java.util.concurrent.Flow; +import java.util.stream.Collectors; +import jdk.incubator.http.internal.common.FlowTube; import jdk.incubator.http.internal.common.Utils; /** @@ -40,34 +50,18 @@ import jdk.incubator.http.internal.common.Utils; */ final class ConnectionPool { - // These counters are used to distribute ids for debugging - // The ACTIVE_CLEANER_COUNTER will tell how many CacheCleaner - // are active at a given time. It will increase when a new - // CacheCleaner is started and decrease when it exits. - static final AtomicLong ACTIVE_CLEANER_COUNTER = new AtomicLong(); - // The POOL_IDS_COUNTER increases each time a new ConnectionPool - // is created. It may wrap and become negative but will never be - // decremented. - static final AtomicLong POOL_IDS_COUNTER = new AtomicLong(); - // The cleanerCounter is used to name cleaner threads within a - // a connection pool, and increments monotically. - // It may wrap and become negative but will never be - // decremented. - final AtomicLong cleanerCounter = new AtomicLong(); - static final long KEEP_ALIVE = Utils.getIntegerNetProperty( "jdk.httpclient.keepalive.timeout", 1200); // seconds + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); // Pools of idle connections - final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool; - final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool; - // A monotically increasing id for this connection pool. - // It may be negative (that's OK) - // Mostly used for debugging purposes when looking at thread dumps. - // Global scope. - final long poolID = POOL_IDS_COUNTER.incrementAndGet(); - final AtomicReference<CacheCleaner> cleanerRef; + private final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool; + private final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool; + private final ExpiryList expiryList; + private final String dbgTag; // used for debug + boolean stopped; /** * Entries in connection pool are keyed by destination address and/or @@ -110,28 +104,26 @@ final class ConnectionPool { } } - static class ExpiryEntry { - final HttpConnection connection; - final long expiry; // absolute time in seconds of expiry time - ExpiryEntry(HttpConnection connection, long expiry) { - this.connection = connection; - this.expiry = expiry; - } + ConnectionPool(long clientId) { + this("ConnectionPool("+clientId+")"); } - final LinkedList<ExpiryEntry> expiryList; - /** * There should be one of these per HttpClient. */ - ConnectionPool() { + private ConnectionPool(String tag) { + dbgTag = tag; plainPool = new HashMap<>(); sslPool = new HashMap<>(); - expiryList = new LinkedList<>(); - cleanerRef = new AtomicReference<>(); + expiryList = new ExpiryList(); + } + + final String dbgString() { + return dbgTag; } void start() { + assert !stopped : "Already stopped"; } static CacheKey cacheKey(InetSocketAddress destination, @@ -143,6 +135,7 @@ final class ConnectionPool { synchronized HttpConnection getConnection(boolean secure, InetSocketAddress addr, InetSocketAddress proxy) { + if (stopped) return null; CacheKey key = new CacheKey(addr, proxy); HttpConnection c = secure ? findConnection(key, sslPool) : findConnection(key, plainPool); @@ -153,16 +146,49 @@ final class ConnectionPool { /** * Returns the connection to the pool. */ - synchronized void returnToPool(HttpConnection conn) { - if (conn instanceof PlainHttpConnection) { - putConnection(conn, plainPool); - } else { - putConnection(conn, sslPool); + void returnToPool(HttpConnection conn) { + returnToPool(conn, Instant.now(), KEEP_ALIVE); + } + + // Called also by whitebox tests + void returnToPool(HttpConnection conn, Instant now, long keepAlive) { + + // Don't call registerCleanupTrigger while holding a lock, + // but register it before the connection is added to the pool, + // since we don't want to trigger the cleanup if the connection + // is not in the pool. + CleanupTrigger cleanup = registerCleanupTrigger(conn); + + // it's possible that cleanup may have been called. + synchronized(this) { + if (cleanup.isDone()) { + return; + } else if (stopped) { + conn.close(); + return; + } + if (conn instanceof PlainHttpConnection) { + putConnection(conn, plainPool); + } else { + assert conn.isSecure(); + putConnection(conn, sslPool); + } + expiryList.add(conn, now, keepAlive); } - addToExpiryList(conn); //System.out.println("Return to pool: " + conn); } + private CleanupTrigger registerCleanupTrigger(HttpConnection conn) { + // Connect the connection flow to a pub/sub pair that will take the + // connection out of the pool and close it if anything happens + // while the connection is sitting in the pool. + CleanupTrigger cleanup = new CleanupTrigger(conn); + FlowTube flow = conn.getConnectionFlow(); + debug.log(Level.DEBUG, "registering %s", cleanup); + flow.connectFlows(cleanup, cleanup); + return cleanup; + } + private HttpConnection findConnection(CacheKey key, HashMap<CacheKey,LinkedList<HttpConnection>> pool) { @@ -171,20 +197,24 @@ final class ConnectionPool { return null; } else { HttpConnection c = l.removeFirst(); - removeFromExpiryList(c); + expiryList.remove(c); return c; } } /* called from cache cleaner only */ - private void + private boolean removeFromPool(HttpConnection c, HashMap<CacheKey,LinkedList<HttpConnection>> pool) { //System.out.println("cacheCleaner removing: " + c); - LinkedList<HttpConnection> l = pool.get(c.cacheKey()); - assert l != null; - boolean wasPresent = l.remove(c); - assert wasPresent; + assert Thread.holdsLock(this); + CacheKey k = c.cacheKey(); + List<HttpConnection> l = pool.get(k); + if (l == null || l.isEmpty()) { + pool.remove(k); + return false; + } + return l.remove(c); } private void @@ -199,135 +229,262 @@ final class ConnectionPool { l.add(c); } - static String makeCleanerName(long poolId, long cleanerId) { - return "HTTP-Cache-cleaner-" + poolId + "-" + cleanerId; + /** + * Purge expired connection and return the number of milliseconds + * in which the next connection is scheduled to expire. + * If no connections are scheduled to be purged return 0. + * @return the delay in milliseconds in which the next connection will + * expire. + */ + long purgeExpiredConnectionsAndReturnNextDeadline() { + if (!expiryList.purgeMaybeRequired()) return 0; + return purgeExpiredConnectionsAndReturnNextDeadline(Instant.now()); } - // only runs while entries exist in cache - final static class CacheCleaner extends Thread { + // Used for whitebox testing + long purgeExpiredConnectionsAndReturnNextDeadline(Instant now) { + long nextPurge = 0; - volatile boolean stopping; - // A monotically increasing id. May wrap and become negative (that's OK) - // Mostly used for debugging purposes when looking at thread dumps. - // Scoped per connection pool. - final long cleanerID; - // A reference to the owning ConnectionPool. - // This reference's referent may become null if the HttpClientImpl - // that owns this pool is GC'ed. - final WeakReference<ConnectionPool> ownerRef; - - CacheCleaner(ConnectionPool owner) { - this(owner, owner.cleanerCounter.incrementAndGet()); - } - - CacheCleaner(ConnectionPool owner, long cleanerID) { - super(null, null, makeCleanerName(owner.poolID, cleanerID), 0, false); - this.cleanerID = cleanerID; - this.ownerRef = new WeakReference<>(owner); - setDaemon(true); - } - - synchronized boolean stopping() { - return stopping || ownerRef.get() == null; - } - - synchronized void stopCleaner() { - stopping = true; - } - - @Override - public void run() { - ACTIVE_CLEANER_COUNTER.incrementAndGet(); - try { - while (!stopping()) { - try { - Thread.sleep(3000); - } catch (InterruptedException e) {} - ConnectionPool owner = ownerRef.get(); - if (owner == null) return; - owner.cleanCache(this); - owner = null; - } - } finally { - ACTIVE_CLEANER_COUNTER.decrementAndGet(); - } - } - } - - synchronized void removeFromExpiryList(HttpConnection c) { - if (c == null) { - return; - } - ListIterator<ExpiryEntry> li = expiryList.listIterator(); - while (li.hasNext()) { - ExpiryEntry e = li.next(); - if (e.connection.equals(c)) { - li.remove(); - return; - } - } - CacheCleaner cleaner = this.cleanerRef.get(); - if (expiryList.isEmpty() && cleaner != null) { - this.cleanerRef.compareAndSet(cleaner, null); - cleaner.stopCleaner(); - cleaner.interrupt(); - } - } - - private void cleanCache(CacheCleaner cleaner) { - long now = System.currentTimeMillis() / 1000; - LinkedList<HttpConnection> closelist = new LinkedList<>(); + // We may be in the process of adding new elements + // to the expiry list - but those elements will not + // have outlast their keep alive timer yet since we're + // just adding them. + if (!expiryList.purgeMaybeRequired()) return nextPurge; + List<HttpConnection> closelist; synchronized (this) { - ListIterator<ExpiryEntry> li = expiryList.listIterator(); + closelist = expiryList.purgeUntil(now); + for (HttpConnection c : closelist) { + if (c instanceof PlainHttpConnection) { + boolean wasPresent = removeFromPool(c, plainPool); + assert wasPresent; + } else { + boolean wasPresent = removeFromPool(c, sslPool); + assert wasPresent; + } + } + nextPurge = now.until( + expiryList.nextExpiryDeadline().orElse(now), + ChronoUnit.MILLIS); + } + closelist.forEach(this::close); + return nextPurge; + } + + private void close(HttpConnection c) { + try { + c.close(); + } catch (Throwable e) {} // ignore + } + + void stop() { + List<HttpConnection> closelist = Collections.emptyList(); + try { + synchronized (this) { + stopped = true; + closelist = expiryList.stream() + .map(e -> e.connection) + .collect(Collectors.toList()); + expiryList.clear(); + plainPool.clear(); + sslPool.clear(); + } + } finally { + closelist.forEach(this::close); + } + } + + static final class ExpiryEntry { + final HttpConnection connection; + final Instant expiry; // absolute time in seconds of expiry time + ExpiryEntry(HttpConnection connection, Instant expiry) { + this.connection = connection; + this.expiry = expiry; + } + } + + /** + * Manages a LinkedList of sorted ExpiryEntry. The entry with the closer + * deadline is at the tail of the list, and the entry with the farther + * deadline is at the head. In the most common situation, new elements + * will need to be added at the head (or close to it), and expired elements + * will need to be purged from the tail. + */ + private static final class ExpiryList { + private final LinkedList<ExpiryEntry> list = new LinkedList<>(); + private volatile boolean mayContainEntries; + + // A loosely accurate boolean whose value is computed + // at the end of each operation performed on ExpiryList; + // Does not require synchronizing on the ConnectionPool. + boolean purgeMaybeRequired() { + return mayContainEntries; + } + + // Returns the next expiry deadline + // should only be called while holding a synchronization + // lock on the ConnectionPool + Optional<Instant> nextExpiryDeadline() { + if (list.isEmpty()) return Optional.empty(); + else return Optional.of(list.getLast().expiry); + } + + // should only be called while holding a synchronization + // lock on the ConnectionPool + void add(HttpConnection conn) { + add(conn, Instant.now(), KEEP_ALIVE); + } + + // Used by whitebox test. + void add(HttpConnection conn, Instant now, long keepAlive) { + Instant then = now.truncatedTo(ChronoUnit.SECONDS) + .plus(keepAlive, ChronoUnit.SECONDS); + + // Elements with the farther deadline are at the head of + // the list. It's more likely that the new element will + // have the farthest deadline, and will need to be inserted + // at the head of the list, so we're using an ascending + // list iterator to find the right insertion point. + ListIterator<ExpiryEntry> li = list.listIterator(); while (li.hasNext()) { ExpiryEntry entry = li.next(); - if (entry.expiry <= now) { + + if (then.isAfter(entry.expiry)) { + li.previous(); + // insert here + li.add(new ExpiryEntry(conn, then)); + mayContainEntries = true; + return; + } + } + // last (or first) element of list (the last element is + // the first when the list is empty) + list.add(new ExpiryEntry(conn, then)); + mayContainEntries = true; + } + + // should only be called while holding a synchronization + // lock on the ConnectionPool + void remove(HttpConnection c) { + if (c == null || list.isEmpty()) return; + ListIterator<ExpiryEntry> li = list.listIterator(); + while (li.hasNext()) { + ExpiryEntry e = li.next(); + if (e.connection.equals(c)) { + li.remove(); + mayContainEntries = !list.isEmpty(); + return; + } + } + } + + // should only be called while holding a synchronization + // lock on the ConnectionPool. + // Purge all elements whose deadline is before now (now included). + List<HttpConnection> purgeUntil(Instant now) { + if (list.isEmpty()) return Collections.emptyList(); + + List<HttpConnection> closelist = new ArrayList<>(); + + // elements with the closest deadlines are at the tail + // of the queue, so we're going to use a descending iterator + // to remove them, and stop when we find the first element + // that has not expired yet. + Iterator<ExpiryEntry> li = list.descendingIterator(); + while (li.hasNext()) { + ExpiryEntry entry = li.next(); + // use !isAfter instead of isBefore in order to + // remove the entry if its expiry == now + if (!entry.expiry.isAfter(now)) { li.remove(); HttpConnection c = entry.connection; closelist.add(c); - if (c instanceof PlainHttpConnection) { - removeFromPool(c, plainPool); - } else { - removeFromPool(c, sslPool); - } - } - } - if (expiryList.isEmpty() && cleaner != null) { - this.cleanerRef.compareAndSet(cleaner, null); - cleaner.stopCleaner(); + } else break; // the list is sorted } + mayContainEntries = !list.isEmpty(); + return closelist; } - for (HttpConnection c : closelist) { - //System.out.println ("KAC: closing " + c); - c.close(); + + // should only be called while holding a synchronization + // lock on the ConnectionPool + java.util.stream.Stream<ExpiryEntry> stream() { + return list.stream(); + } + + // should only be called while holding a synchronization + // lock on the ConnectionPool + void clear() { + list.clear(); + mayContainEntries = false; } } - private synchronized void addToExpiryList(HttpConnection conn) { - long now = System.currentTimeMillis() / 1000; - long then = now + KEEP_ALIVE; - if (expiryList.isEmpty()) { - CacheCleaner cleaner = new CacheCleaner(this); - if (this.cleanerRef.compareAndSet(null, cleaner)) { - cleaner.start(); + void cleanup(HttpConnection c, Throwable error) { + debug.log(Level.DEBUG, + "%s : ConnectionPool.cleanup(%s)", + String.valueOf(c.getConnectionFlow()), + error); + synchronized(this) { + if (c instanceof PlainHttpConnection) { + removeFromPool(c, plainPool); + } else { + assert c.isSecure(); + removeFromPool(c, sslPool); } - expiryList.add(new ExpiryEntry(conn, then)); - return; + expiryList.remove(c); } - - ListIterator<ExpiryEntry> li = expiryList.listIterator(); - while (li.hasNext()) { - ExpiryEntry entry = li.next(); - - if (then > entry.expiry) { - li.previous(); - // insert here - li.add(new ExpiryEntry(conn, then)); - return; - } - } - // first element of list - expiryList.add(new ExpiryEntry(conn, then)); + c.close(); } + + /** + * An object that subscribes to the flow while the connection is in + * the pool. Anything that comes in will cause the connection to be closed + * and removed from the pool. + */ + private final class CleanupTrigger implements + FlowTube.TubeSubscriber, FlowTube.TubePublisher, + Flow.Subscription { + + private final HttpConnection connection; + private volatile boolean done; + + public CleanupTrigger(HttpConnection connection) { + this.connection = connection; + } + + public boolean isDone() { return done;} + + private void triggerCleanup(Throwable error) { + done = true; + cleanup(connection, error); + } + + @Override public void request(long n) {} + @Override public void cancel() {} + + @Override + public void onSubscribe(Flow.Subscription subscription) { + subscription.request(1); + } + @Override + public void onError(Throwable error) { triggerCleanup(error); } + @Override + public void onComplete() { triggerCleanup(null); } + @Override + public void onNext(List<ByteBuffer> item) { + triggerCleanup(new IOException("Data received while in pool")); + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + subscriber.onSubscribe(this); + } + + @Override + public String toString() { + return "CleanupTrigger(" + connection.getConnectionFlow() + ")"; + } + + } + } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java index 204d5d03b7d..8ebf726f04a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,7 @@ package jdk.incubator.http; import java.io.IOException; -import java.net.CookieManager; +import java.net.CookieHandler; import java.util.List; import java.util.Map; import java.util.Optional; @@ -41,11 +41,11 @@ class CookieFilter implements HeaderFilter { @Override public void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException { HttpClientImpl client = e.client(); - Optional<CookieManager> cookieManOpt = client.cookieManager(); - if (cookieManOpt.isPresent()) { - CookieManager cookieMan = cookieManOpt.get(); + Optional<CookieHandler> cookieHandlerOpt = client.cookieHandler(); + if (cookieHandlerOpt.isPresent()) { + CookieHandler cookieHandler = cookieHandlerOpt.get(); Map<String,List<String>> userheaders = r.getUserHeaders().map(); - Map<String,List<String>> cookies = cookieMan.get(r.uri(), userheaders); + Map<String,List<String>> cookies = cookieHandler.get(r.uri(), userheaders); // add the returned cookies HttpHeadersImpl systemHeaders = r.getSystemHeaders(); @@ -74,11 +74,11 @@ class CookieFilter implements HeaderFilter { HttpRequestImpl request = r.request(); Exchange<?> e = r.exchange; Log.logTrace("Response: processing cookies for {0}", request.uri()); - Optional<CookieManager> cookieManOpt = e.client().cookieManager(); - if (cookieManOpt.isPresent()) { - CookieManager cookieMan = cookieManOpt.get(); + Optional<CookieHandler> cookieHandlerOpt = e.client().cookieHandler(); + if (cookieHandlerOpt.isPresent()) { + CookieHandler cookieHandler = cookieHandlerOpt.get(); Log.logTrace("Response: parsing cookies from {0}", hdrs.map()); - cookieMan.put(request.uri(), hdrs.map()); + cookieHandler.put(request.uri(), hdrs.map()); } else { Log.logTrace("Response: No cookie manager found for {0}", request.uri()); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java deleted file mode 100644 index ea0137b4bbe..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.Executor; -import java.util.concurrent.Flow; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Supplier; - -class DefaultPublisher<T> implements Flow.Publisher<T> { - - private final Supplier<Optional<T>> supplier; - // this executor will be wrapped in another executor - // which may override it and just run in the calling thread - // if it knows the user call is blocking - private final Executor executor; - - /** - * Supplier returns non empty Optionals until final - */ - DefaultPublisher(Supplier<Optional<T>> supplier, Executor executor) { - this.supplier = supplier; - this.executor = executor; - } - - @Override - public void subscribe(Flow.Subscriber<? super T> subscriber) { - try { - subscriber.onSubscribe(new Subscription(subscriber)); - } catch (RejectedExecutionException e) { - subscriber.onError(new IllegalStateException(e)); - } - } - - private class Subscription implements Flow.Subscription { - - private final Flow.Subscriber<? super T> subscriber; - private final AtomicBoolean done = new AtomicBoolean(); - - private final AtomicLong demand = new AtomicLong(); - - private final Lock consumerLock = new ReentrantLock(); - private final Condition consumerAlarm = consumerLock.newCondition(); - - Subscription(Flow.Subscriber<? super T> subscriber) { - this.subscriber = subscriber; - - executor.execute(() -> { - try { - while (!done.get()) { - consumerLock.lock(); - try { - while (!done.get() && demand.get() == 0) { - consumerAlarm.await(); - } - } finally { - consumerLock.unlock(); - } - - long nbItemsDemanded = demand.getAndSet(0); - for (long i = 0; i < nbItemsDemanded && !done.get(); i++) { - try { - Optional<T> item = Objects.requireNonNull(supplier.get()); - if (item.isPresent()) { - subscriber.onNext(item.get()); - } else { - if (done.compareAndSet(false, true)) { - subscriber.onComplete(); - } - } - } catch (RuntimeException e) { - if (done.compareAndSet(false, true)) { - subscriber.onError(e); - } - } - } - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - if (done.compareAndSet(false, true)) { - subscriber.onError(e); - } - } - }); - } - - @Override - public void request(long n) { - if (!done.get() && n > 0) { - demand.updateAndGet(d -> (d + n > 0) ? d + n : Long.MAX_VALUE); - wakeConsumer(); - } else if (done.compareAndSet(false, true)) { - subscriber.onError(new IllegalArgumentException("request(" + n + ")")); - } - } - - @Override - public void cancel() { - done.set(true); - wakeConsumer(); - } - - private void wakeConsumer() { - consumerLock.lock(); - try { - consumerAlarm.signal(); - } finally { - consumerLock.unlock(); - } - } - - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java index 1d8abede2af..2cead021166 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java @@ -26,26 +26,23 @@ package jdk.incubator.http; import java.io.IOException; -import java.io.UncheckedIOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.net.ProxySelector; -import java.net.SocketPermission; import java.net.URI; import java.net.URISyntaxException; import java.net.URLPermission; import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Utils; import jdk.incubator.http.internal.common.Log; +import static jdk.incubator.http.internal.common.Utils.permissionForProxy; + /** * One request/response exchange (handles 100/101 intermediate response also). * depth field used to track number of times a new request is being sent @@ -61,19 +58,22 @@ import jdk.incubator.http.internal.common.Log; */ final class Exchange<T> { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final HttpRequestImpl request; final HttpClientImpl client; volatile ExchangeImpl<T> exchImpl; + volatile CompletableFuture<? extends ExchangeImpl<T>> exchangeCF; // used to record possible cancellation raised before the exchImpl // has been established. private volatile IOException failed; - final List<SocketPermission> permissions = new LinkedList<>(); final AccessControlContext acc; final MultiExchange<?,T> multi; final Executor parentExecutor; - final HttpRequest.BodyProcessor requestProcessor; boolean upgrading; // to HTTP/2 final PushGroup<?,T> pushGroup; + final String dbgTag; Exchange(HttpRequestImpl request, MultiExchange<?,T> multi) { this.request = request; @@ -82,8 +82,8 @@ final class Exchange<T> { this.multi = multi; this.acc = multi.acc; this.parentExecutor = multi.executor; - this.requestProcessor = request.requestProcessor; this.pushGroup = multi.pushGroup; + this.dbgTag = "Exchange"; } /* If different AccessControlContext to be used */ @@ -97,8 +97,8 @@ final class Exchange<T> { this.client = multi.client(); this.multi = multi; this.parentExecutor = multi.executor; - this.requestProcessor = request.requestProcessor; this.pushGroup = multi.pushGroup; + this.dbgTag = "Exchange"; } PushGroup<?,T> getPushGroup() { @@ -117,18 +117,35 @@ final class Exchange<T> { return client; } - public Response response() throws IOException, InterruptedException { - return responseImpl(null); - } - - public T readBody(HttpResponse.BodyHandler<T> responseHandler) throws IOException { - // The connection will not be returned to the pool in the case of WebSocket - return exchImpl.readBody(responseHandler, !request.isWebSocket()); - } public CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler) { // The connection will not be returned to the pool in the case of WebSocket - return exchImpl.readBodyAsync(handler, !request.isWebSocket(), parentExecutor); + return exchImpl.readBodyAsync(handler, !request.isWebSocket(), parentExecutor) + .whenComplete((r,t) -> exchImpl.completed()); + } + + /** + * Called after a redirect or similar kind of retry where a body might + * be sent but we don't want it. Should send a RESET in h2. For http/1.1 + * we can consume small quantity of data, or close the connection in + * other cases. + */ + public CompletableFuture<Void> ignoreBody() { + return exchImpl.ignoreBody(); + } + + /** + * Called when a new exchange is created to replace this exchange. + * At this point it is guaranteed that readBody/readBodyAsync will + * not be called. + */ + public void released() { + ExchangeImpl<?> impl = exchImpl; + if (impl != null) impl.released(); + // Don't set exchImpl to null here. We need to keep + // it alive until it's replaced by a Stream in wrapForUpgrade. + // Setting it to null here might get it GC'ed too early, because + // the Http1Response is now only weakly referenced by the Selector. } public void cancel() { @@ -153,19 +170,15 @@ final class Exchange<T> { ExchangeImpl<?> impl = exchImpl; if (impl != null) { // propagate the exception to the impl + debug.log(Level.DEBUG, "Cancelling exchImpl: %s", exchImpl); impl.cancel(cause); } else { - try { - // no impl yet. record the exception - failed = cause; - // now call checkCancelled to recheck the impl. - // if the failed state is set and the impl is not null, reset - // the failed state and propagate the exception to the impl. - checkCancelled(false); - } catch (IOException x) { - // should not happen - we passed 'false' above - throw new UncheckedIOException(x); - } + // no impl yet. record the exception + failed = cause; + // now call checkCancelled to recheck the impl. + // if the failed state is set and the impl is not null, reset + // the failed state and propagate the exception to the impl. + checkCancelled(); } } @@ -175,37 +188,34 @@ final class Exchange<T> { // will persist until the exception can be raised and the failed state // can be cleared. // Takes care of possible race conditions. - private void checkCancelled(boolean throwIfNoImpl) throws IOException { + private void checkCancelled() { ExchangeImpl<?> impl = null; IOException cause = null; + CompletableFuture<? extends ExchangeImpl<T>> cf = null; if (failed != null) { synchronized(this) { cause = failed; impl = exchImpl; - if (throwIfNoImpl || impl != null) { - // The exception will be raised by one of the two methods - // below: reset the failed state. - failed = null; - } + cf = exchangeCF; } } if (cause == null) return; if (impl != null) { // The exception is raised by propagating it to the impl. + debug.log(Level.DEBUG, "Cancelling exchImpl: %s", impl); impl.cancel(cause); - } else if (throwIfNoImpl) { - // The exception is raised by throwing it immediately - throw cause; + failed = null; } else { Log.logTrace("Exchange: request [{0}/timeout={1}ms] no impl is set." + "\n\tCan''t cancel yet with {2}", request.uri(), - request.duration() == null ? -1 : + request.timeout().isPresent() ? // calling duration.toMillis() can throw an exception. // this is just debugging, we don't care if it overflows. - (request.duration().getSeconds() * 1000 - + request.duration().getNano() / 1000000), + (request.timeout().get().getSeconds() * 1000 + + request.timeout().get().getNano() / 1000000) : -1, cause); + if (cf != null) cf.completeExceptionally(cause); } } @@ -214,92 +224,51 @@ final class Exchange<T> { request.setH2Upgrade(client.client2()); } - static final SocketPermission[] SOCKET_ARRAY = new SocketPermission[0]; - - Response responseImpl(HttpConnection connection) - throws IOException, InterruptedException - { - SecurityException e = securityCheck(acc); - if (e != null) { - throw e; - } - - if (permissions.size() > 0) { - try { - return AccessController.doPrivileged( - (PrivilegedExceptionAction<Response>)() -> - responseImpl0(connection), - null, - permissions.toArray(SOCKET_ARRAY)); - } catch (Throwable ee) { - if (ee instanceof PrivilegedActionException) { - ee = ee.getCause(); - } - if (ee instanceof IOException) { - throw (IOException) ee; - } else { - throw new RuntimeException(ee); // TODO: fix - } - } - } else { - return responseImpl0(connection); - } + synchronized IOException getCancelCause() { + return failed; } // get/set the exchange impl, solving race condition issues with // potential concurrent calls to cancel() or cancel(IOException) - private void establishExchange(HttpConnection connection) - throws IOException, InterruptedException - { - // check if we have been cancelled first. - checkCancelled(true); - // not yet cancelled: create/get a new impl - exchImpl = ExchangeImpl.get(this, connection); - // recheck for cancelled, in case of race conditions - checkCancelled(true); - // now we're good to go. because exchImpl is no longer null - // cancel() will be able to propagate directly to the impl - // after this point. - } - - private Response responseImpl0(HttpConnection connection) - throws IOException, InterruptedException - { - establishExchange(connection); - if (request.expectContinue()) { - Log.logTrace("Sending Expect: 100-Continue"); - request.addSystemHeader("Expect", "100-Continue"); - exchImpl.sendHeadersOnly(); - - Log.logTrace("Waiting for 407-Expectation-Failed or 100-Continue"); - Response resp = exchImpl.getResponse(); - HttpResponseImpl.logResponse(resp); - int rcode = resp.statusCode(); - if (rcode != 100) { - Log.logTrace("Expectation failed: Received {0}", - rcode); - if (upgrading && rcode == 101) { - throw new IOException( - "Unable to handle 101 while waiting for 100-Continue"); - } - return resp; - } - - Log.logTrace("Received 100-Continue: sending body"); - exchImpl.sendBody(); - - Log.logTrace("Body sent: waiting for response"); - resp = exchImpl.getResponse(); - HttpResponseImpl.logResponse(resp); - - return checkForUpgrade(resp, exchImpl); - } else { - exchImpl.sendHeadersOnly(); - exchImpl.sendBody(); - Response resp = exchImpl.getResponse(); - HttpResponseImpl.logResponse(resp); - return checkForUpgrade(resp, exchImpl); + private CompletableFuture<? extends ExchangeImpl<T>> + establishExchange(HttpConnection connection) { + if (debug.isLoggable(Level.DEBUG)) { + debug.log(Level.DEBUG, + "establishing exchange for %s,%n\t proxy=%s", + request, + request.proxy()); } + // check if we have been cancelled first. + Throwable t = getCancelCause(); + checkCancelled(); + if (t != null) { + return MinimalFuture.failedFuture(t); + } + + CompletableFuture<? extends ExchangeImpl<T>> cf, res; + cf = ExchangeImpl.get(this, connection); + // We should probably use a VarHandle to get/set exchangeCF + // instead - as we need CAS semantics. + synchronized (this) { exchangeCF = cf; }; + res = cf.whenComplete((r,x) -> { + synchronized(Exchange.this) { + if (exchangeCF == cf) exchangeCF = null; + } + }); + checkCancelled(); + return res.thenCompose((eimpl) -> { + // recheck for cancelled, in case of race conditions + exchImpl = eimpl; + IOException tt = getCancelCause(); + checkCancelled(); + if (tt != null) { + return MinimalFuture.failedFuture(tt); + } else { + // Now we're good to go. Because exchImpl is no longer + // null cancel() will be able to propagate directly to + // the impl after this point ( if needed ). + return MinimalFuture.completedFuture(eimpl); + } }); } // Completed HttpResponse will be null if response succeeded @@ -310,35 +279,23 @@ final class Exchange<T> { } CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) { - SecurityException e = securityCheck(acc); + SecurityException e = checkPermissions(); if (e != null) { return MinimalFuture.failedFuture(e); - } - if (permissions.size() > 0) { - return AccessController.doPrivileged( - (PrivilegedAction<CompletableFuture<Response>>)() -> - responseAsyncImpl0(connection), - null, - permissions.toArray(SOCKET_ARRAY)); } else { return responseAsyncImpl0(connection); } } CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) { - try { - establishExchange(connection); - } catch (IOException | InterruptedException e) { - return MinimalFuture.failedFuture(e); - } if (request.expectContinue()) { request.addSystemHeader("Expect", "100-Continue"); Log.logTrace("Sending Expect: 100-Continue"); - return exchImpl - .sendHeadersAsync() + return establishExchange(connection) + .thenCompose((ex) -> ex.sendHeadersAsync()) .thenCompose(v -> exchImpl.getResponseAsync(parentExecutor)) .thenCompose((Response r1) -> { - HttpResponseImpl.logResponse(r1); + Log.logResponse(r1::toString); int rcode = r1.statusCode(); if (rcode == 100) { Log.logTrace("Received 100-Continue: sending body"); @@ -361,8 +318,8 @@ final class Exchange<T> { } }); } else { - CompletableFuture<Response> cf = exchImpl - .sendHeadersAsync() + CompletableFuture<Response> cf = establishExchange(connection) + .thenCompose((ex) -> ex.sendHeadersAsync()) .thenCompose(ExchangeImpl::sendBodyAsync) .thenCompose(exIm -> exIm.getResponseAsync(parentExecutor)); cf = wrapForUpgrade(cf); @@ -381,15 +338,15 @@ final class Exchange<T> { private CompletableFuture<Response> wrapForLog(CompletableFuture<Response> cf) { if (Log.requests()) { return cf.thenApply(response -> { - HttpResponseImpl.logResponse(response); + Log.logResponse(response::toString); return response; }); } return cf; } - HttpResponse.BodyProcessor<T> ignoreBody(int status, HttpHeaders hdrs) { - return HttpResponse.BodyProcessor.discard((T)null); + HttpResponse.BodySubscriber<T> ignoreBody(int status, HttpHeaders hdrs) { + return HttpResponse.BodySubscriber.discard((T)null); } // if this response was received in reply to an upgrade @@ -406,50 +363,59 @@ final class Exchange<T> { // check for 101 switching protocols // 101 responses are not supposed to contain a body. // => should we fail if there is one? + debug.log(Level.DEBUG, "Upgrading async %s", e.connection()); return e.readBodyAsync(this::ignoreBody, false, parentExecutor) - .thenCompose((T v) -> // v is null - Http2Connection.createAsync(e.connection(), + .thenCompose((T v) -> {// v is null + debug.log(Level.DEBUG, "Ignored body"); + // we pass e::getBuffer to allow the ByteBuffers to accumulate + // while we build the Http2Connection + return Http2Connection.createAsync(e.connection(), client.client2(), - this, e.getBuffer()) + this, e::drainLeftOverBytes) .thenCompose((Http2Connection c) -> { c.putConnection(); Stream<T> s = c.getStream(1); - exchImpl = s; + if (s == null) { + // s can be null if an exception occurred + // asynchronously while sending the preface. + Throwable t = c.getRecordedCause(); + if (t != null) { + return MinimalFuture.failedFuture( + new IOException("Can't get stream 1: " + t, t)); + } + } + exchImpl.released(); + Throwable t; + // There's a race condition window where an external + // thread (SelectorManager) might complete the + // exchange in timeout at the same time where we're + // trying to switch the exchange impl. + // 'failed' will be reset to null after + // exchImpl.cancel() has completed, so either we + // will observe failed != null here, or we will + // observe e.getCancelCause() != null, or the + // timeout exception will be routed to 's'. + // Either way, we need to relay it to s. + synchronized (this) { + exchImpl = s; + t = failed; + } + // Check whether the HTTP/1.1 was cancelled. + if (t == null) t = e.getCancelCause(); + // if HTTP/1.1 exchange was timed out, don't + // try to go further. + if (t instanceof HttpTimeoutException) { + s.cancelImpl(t); + return MinimalFuture.failedFuture(t); + } + debug.log(Level.DEBUG, "Getting response async %s", s); return s.getResponseAsync(null); - }) + });} ); } return MinimalFuture.completedFuture(resp); } - private Response checkForUpgrade(Response resp, - ExchangeImpl<T> ex) - throws IOException, InterruptedException - { - int rcode = resp.statusCode(); - if (upgrading && (rcode == 101)) { - Http1Exchange<T> e = (Http1Exchange<T>) ex; - - // 101 responses are not supposed to contain a body. - // => should we fail if there is one? - // => readBody called here by analogy with - // checkForUpgradeAsync above - e.readBody(this::ignoreBody, false); - - // must get connection from Http1Exchange - Http2Connection h2con = new Http2Connection(e.connection(), - client.client2(), - this, e.getBuffer()); - h2con.putConnection(); - Stream<T> s = h2con.getStream(1); - exchImpl = s; - Response xx = s.getResponse(); - HttpResponseImpl.logResponse(xx); - return xx; - } - return resp; - } - private URI getURIForSecurityCheck() { URI u; String method = request.method(); @@ -476,91 +442,65 @@ final class Exchange<T> { } /** - * Do the security check and return any exception. - * Return null if no check needed or passes. - * - * Also adds any generated permissions to the "permissions" list. + * Returns the security permission required for the given details. + * If method is CONNECT, then uri must be of form "scheme://host:port" */ - private SecurityException securityCheck(AccessControlContext acc) { + private static URLPermission permissionForServer(URI uri, + String method, + Map<String, List<String>> headers) { + if (method.equals("CONNECT")) { + return new URLPermission(uri.toString(), "CONNECT"); + } else { + return Utils.permissionForServer(uri, method, headers.keySet().stream()); + } + } + + /** + * Performs the necessary security permission checks required to retrieve + * the response. Returns a security exception representing the denied + * permission, or null if all checks pass or there is no security manager. + */ + private SecurityException checkPermissions() { + String method = request.method(); SecurityManager sm = System.getSecurityManager(); - if (sm == null) { + if (sm == null || method.equals("CONNECT")) { + // tunneling will have a null acc, which is fine. The proxy + // permission check will have already been preformed. return null; } - String method = request.method(); HttpHeaders userHeaders = request.getUserHeaders(); URI u = getURIForSecurityCheck(); - URLPermission p = Utils.getPermission(u, method, userHeaders.map()); + URLPermission p = permissionForServer(u, method, userHeaders.map()); try { assert acc != null; sm.checkPermission(p, acc); - permissions.add(getSocketPermissionFor(u)); } catch (SecurityException e) { return e; } - ProxySelector ps = client.proxy().orElse(null); + ProxySelector ps = client.proxySelector(); if (ps != null) { - InetSocketAddress proxy = (InetSocketAddress) - ps.select(u).get(0).address(); // TODO: check this - // may need additional check if (!method.equals("CONNECT")) { - // a direct http proxy. Need to check access to proxy - try { - u = new URI("socket", null, proxy.getHostString(), - proxy.getPort(), null, null, null); - } catch (URISyntaxException e) { - throw new InternalError(e); // shouldn't happen + // a non-tunneling HTTP proxy. Need to check access + URLPermission proxyPerm = permissionForProxy(request.proxy()); + if (proxyPerm != null) { + try { + sm.checkPermission(proxyPerm, acc); + } catch (SecurityException e) { + return e; + } } - p = new URLPermission(u.toString(), "CONNECT"); - try { - sm.checkPermission(p, acc); - } catch (SecurityException e) { - permissions.clear(); - return e; - } - String sockperm = proxy.getHostString() + - ":" + Integer.toString(proxy.getPort()); - - permissions.add(new SocketPermission(sockperm, "connect,resolve")); } } return null; } - HttpClient.Redirect followRedirects() { - return client.followRedirects(); - } - HttpClient.Version version() { return multi.version(); } - private static SocketPermission getSocketPermissionFor(URI url) { - if (System.getSecurityManager() == null) { - return null; - } - - StringBuilder sb = new StringBuilder(); - String host = url.getHost(); - sb.append(host); - int port = url.getPort(); - if (port == -1) { - String scheme = url.getScheme(); - if ("http".equals(scheme)) { - sb.append(":80"); - } else { // scheme must be https - sb.append(":443"); - } - } else { - sb.append(':') - .append(Integer.toString(port)); - } - String target = sb.toString(); - return new SocketPermission(target, "connect"); - } - - AccessControlContext getAccessControlContext() { - return acc; + String dbgString() { + return dbgTag; } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java index 3d41179d903..3b4b6fc6983 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,10 +26,13 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Function; import jdk.incubator.http.internal.common.MinimalFuture; import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; +import jdk.incubator.http.internal.common.Utils; /** * Splits request so that headers and body can be sent separately with optional @@ -46,6 +49,10 @@ import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; */ abstract class ExchangeImpl<T> { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + private static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("ExchangeImpl"::toString, DEBUG); + final Exchange<T> exchange; ExchangeImpl(Exchange<T> e) { @@ -68,94 +75,129 @@ abstract class ExchangeImpl<T> { * Initiates a new exchange and assigns it to a connection if one exists * already. connection usually null. */ - static <U> ExchangeImpl<U> get(Exchange<U> exchange, HttpConnection connection) - throws IOException, InterruptedException + static <U> CompletableFuture<? extends ExchangeImpl<U>> + get(Exchange<U> exchange, HttpConnection connection) { - HttpRequestImpl req = exchange.request(); if (exchange.version() == HTTP_1_1) { - return new Http1Exchange<>(exchange, connection); + DEBUG_LOGGER.log(Level.DEBUG, "get: HTTP/1.1: new Http1Exchange"); + return createHttp1Exchange(exchange, connection); } else { Http2ClientImpl c2 = exchange.client().client2(); // TODO: improve HttpRequestImpl request = exchange.request(); - Http2Connection c; - try { - c = c2.getConnectionFor(request); - } catch (Http2Connection.ALPNException e) { - // failed to negotiate "h2" - AbstractAsyncSSLConnection as = e.getConnection(); - as.stopAsyncReading(); - HttpConnection sslc = as.downgrade(); - ExchangeImpl<U> ex = new Http1Exchange<>(exchange, sslc); + CompletableFuture<Http2Connection> c2f = c2.getConnectionFor(request); + DEBUG_LOGGER.log(Level.DEBUG, "get: Trying to get HTTP/2 connection"); + return c2f.handle((h2c, t) -> createExchangeImpl(h2c, t, exchange, connection)) + .thenCompose(Function.identity()); + } + } + + private static <U> CompletableFuture<? extends ExchangeImpl<U>> + createExchangeImpl(Http2Connection c, + Throwable t, + Exchange<U> exchange, + HttpConnection connection) + { + DEBUG_LOGGER.log(Level.DEBUG, "handling HTTP/2 connection creation result"); + if (t != null) { + DEBUG_LOGGER.log(Level.DEBUG, + "handling HTTP/2 connection creation failed: %s", + (Object)t); + t = Utils.getCompletionCause(t); + if (t instanceof Http2Connection.ALPNException) { + Http2Connection.ALPNException ee = (Http2Connection.ALPNException)t; + AbstractAsyncSSLConnection as = ee.getConnection(); + DEBUG_LOGGER.log(Level.DEBUG, "downgrading to HTTP/1.1 with: %s", as); + CompletableFuture<? extends ExchangeImpl<U>> ex = + createHttp1Exchange(exchange, as); return ex; + } else { + DEBUG_LOGGER.log(Level.DEBUG, "HTTP/2 connection creation failed " + + "with unexpected exception: %s", (Object)t); + return CompletableFuture.failedFuture(t); } - if (c == null) { - // no existing connection. Send request with HTTP 1 and then - // upgrade if successful - ExchangeImpl<U> ex = new Http1Exchange<>(exchange, connection); - exchange.h2Upgrade(); - return ex; - } - return c.createStream(exchange); + } + if (c == null) { + // no existing connection. Send request with HTTP 1 and then + // upgrade if successful + DEBUG_LOGGER.log(Level.DEBUG, "new Http1Exchange, try to upgrade"); + return createHttp1Exchange(exchange, connection) + .thenApply((e) -> { + exchange.h2Upgrade(); + return e; + }); + } else { + DEBUG_LOGGER.log(Level.DEBUG, "creating HTTP/2 streams"); + Stream<U> s = c.createStream(exchange); + CompletableFuture<? extends ExchangeImpl<U>> ex = MinimalFuture.completedFuture(s); + return ex; + } + } + + private static <T> CompletableFuture<Http1Exchange<T>> + createHttp1Exchange(Exchange<T> ex, HttpConnection as) + { + try { + return MinimalFuture.completedFuture(new Http1Exchange<>(ex, as)); + } catch (Throwable e) { + return MinimalFuture.failedFuture(e); } } /* The following methods have separate HTTP/1.1 and HTTP/2 implementations */ - /** - * Sends the request headers only. May block until all sent. - */ - abstract void sendHeadersOnly() throws IOException, InterruptedException; + abstract CompletableFuture<ExchangeImpl<T>> sendHeadersAsync(); - // Blocking impl but in async style - - CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() { - // this is blocking. cf will already be completed. - return MinimalFuture.supply(() -> { - sendHeadersOnly(); - return this; - }); - } - - /** - * Gets response by blocking if necessary. This may be an - * intermediate response (like 101) or a final response 200 etc. Returns - * before body is read. - */ - abstract Response getResponse() throws IOException; - - abstract T readBody(HttpResponse.BodyHandler<T> handler, - boolean returnConnectionToPool) throws IOException; + /** Sends a request body, after request headers have been sent. */ + abstract CompletableFuture<ExchangeImpl<T>> sendBodyAsync(); abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler, boolean returnConnectionToPool, Executor executor); /** - * Async version of getResponse. Completes before body is read. + * Ignore/consume the body. */ + abstract CompletableFuture<Void> ignoreBody(); + + /** Gets the response headers. Completes before body is read. */ abstract CompletableFuture<Response> getResponseAsync(Executor executor); - /** - * Sends a request body after request headers. - */ - abstract void sendBody() throws IOException, InterruptedException; - // Async version of sendBody(). This only used when body sent separately - // to headers (100 continue) - CompletableFuture<ExchangeImpl<T>> sendBodyAsync() { - return MinimalFuture.supply(() -> { - sendBody(); - return this; - }); - } - - /** - * Cancels a request. Not currently exposed through API. - */ + /** Cancels a request. Not currently exposed through API. */ abstract void cancel(); /** * Cancels a request with a cause. Not currently exposed through API. */ abstract void cancel(IOException cause); + + /** + * Called when the exchange is released, so that cleanup actions may be + * performed - such as deregistering callbacks. + * Typically released is called during upgrade, when an HTTP/2 stream + * takes over from an Http1Exchange, or when a new exchange is created + * during a multi exchange before the final response body was received. + */ + abstract void released(); + + /** + * Called when the exchange is completed, so that cleanup actions may be + * performed - such as deregistering callbacks. + * Typically, completed is called at the end of the exchange, when the + * final response body has been received (or an error has caused the + * completion of the exchange). + */ + abstract void completed(); + + /** + * Returns true if this exchange was canceled. + * @return true if this exchange was canceled. + */ + abstract boolean isCanceled(); + + /** + * Returns the cause for which this exchange was canceled, if available. + * @return the cause for which this exchange was canceled, if available. + */ + abstract Throwable getCancelCause(); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java deleted file mode 100644 index 54b8cd242ff..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.net.SocketPermission; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.concurrent.Executor; -import jdk.internal.misc.InnocuousThread; - -/** - * Wraps the supplied user Executor - * - * when a Security manager set, the correct access control context is used to execute task - * - * The access control context is captured at creation time of this object - */ -class ExecutorWrapper { - - final Executor userExecutor; // the undeerlying executor provided by user - final Executor executor; // the executur which wraps the user's one - final AccessControlContext acc; - final ClassLoader ccl; - - public ExecutorWrapper(Executor userExecutor, AccessControlContext acc) { - this.userExecutor = userExecutor; - this.acc = acc; - this.ccl = getCCL(); - if (System.getSecurityManager() == null) { - this.executor = userExecutor; - } else { - this.executor = this::run; - } - } - - private ClassLoader getCCL() { - return AccessController.doPrivileged( - (PrivilegedAction<ClassLoader>) () -> { - return Thread.currentThread().getContextClassLoader(); - } - ); - } - - /** - * This is only used for the default HttpClient to deal with - * different application contexts that might be using it. - * The default client uses InnocuousThreads in its Executor. - */ - private void prepareThread() { - final Thread me = Thread.currentThread(); - if (!(me instanceof InnocuousThread)) - return; - InnocuousThread innocuousMe = (InnocuousThread)me; - - AccessController.doPrivileged( - (PrivilegedAction<Void>) () -> { - innocuousMe.setContextClassLoader(ccl); - innocuousMe.eraseThreadLocals(); - return null; - } - ); - } - - - void run(Runnable r) { - prepareThread(); - try { - userExecutor.execute(r); // all throwables must be caught - } catch (Throwable t) { - t.printStackTrace(); - } - } - - public Executor userExecutor() { - return userExecutor; - } - - public Executor executor() { - return executor; - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java index 9d0f0a447e2..0dac70da00a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -40,8 +40,8 @@ class FilterFactory { List<HeaderFilter> l = new LinkedList<>(); for (Class<? extends HeaderFilter> clazz : filterClasses) { try { - @SuppressWarnings("deprecation") - HeaderFilter headerFilter = clazz.newInstance(); + // Requires a public no arg constructor. + HeaderFilter headerFilter = clazz.getConstructor().newInstance(); l.add(headerFilter); } catch (ReflectiveOperationException e) { throw new InternalError(e); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java index 06f3843a056..c4baf8655ad 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java index ec1ac485ed2..5235d625ce7 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -58,26 +58,26 @@ class HeaderParser { parse(); } - private HeaderParser () { } +// private HeaderParser () { } - /** - * Creates a new HeaderParser from this, whose keys (and corresponding - * values) range from "start" to "end-1" - */ - public HeaderParser subsequence(int start, int end) { - if (start == 0 && end == nkeys) { - return this; - } - if (start < 0 || start >= end || end > nkeys) { - throw new IllegalArgumentException("invalid start or end"); - } - HeaderParser n = new HeaderParser(); - n.tab = new String [asize][2]; - n.asize = asize; - System.arraycopy (tab, start, n.tab, 0, (end-start)); - n.nkeys= (end-start); - return n; - } +// /** +// * Creates a new HeaderParser from this, whose keys (and corresponding +// * values) range from "start" to "end-1" +// */ +// public HeaderParser subsequence(int start, int end) { +// if (start == 0 && end == nkeys) { +// return this; +// } +// if (start < 0 || start >= end || end > nkeys) { +// throw new IllegalArgumentException("invalid start or end"); +// } +// HeaderParser n = new HeaderParser(); +// n.tab = new String [asize][2]; +// n.asize = asize; +// System.arraycopy (tab, start, n.tab, 0, (end-start)); +// n.nkeys= (end-start); +// return n; +// } private void parse() { @@ -216,9 +216,9 @@ class HeaderParser { return new ParserIterator (false); } - public Iterator<String> values () { - return new ParserIterator (true); - } +// public Iterator<String> values () { +// return new ParserIterator (true); +// } @Override public String toString () { @@ -242,11 +242,11 @@ class HeaderParser { return sb.toString(); } - public int findInt(String k, int Default) { - try { - return Integer.parseInt(findValue(k, String.valueOf(Default))); - } catch (Throwable t) { - return Default; - } - } +// public int findInt(String k, int Default) { +// try { +// return Integer.parseInt(findValue(k, String.valueOf(Default))); +// } catch (Throwable t) { +// return Default; +// } +// } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java new file mode 100644 index 00000000000..4f8fdf4da6a --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java @@ -0,0 +1,651 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.io.EOFException; +import java.io.IOException; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.FlowTube.TubeSubscriber; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.ConnectionExpiredException; +import jdk.incubator.http.internal.common.Utils; + + +/** + * A helper class that will queue up incoming data until the receiving + * side is ready to handle it. + */ +class Http1AsyncReceiver { + + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + + /** + * A delegate that can asynchronously receive data from an upstream flow, + * parse, it, then possibly transform it and either store it (response + * headers) or possibly pass it to a downstream subscriber (response body). + * Usually, there will be one Http1AsyncDelegate in charge of receiving + * and parsing headers, and another one in charge of receiving, parsing, + * and forwarding body. Each will sequentially subscribe with the + * Http1AsyncReceiver in turn. There may be additional delegates which + * subscribe to the Http1AsyncReceiver, mainly for the purpose of handling + * errors while the connection is busy transmitting the request body and the + * Http1Exchange::readBody method hasn't been called yet, and response + * delegates haven't subscribed yet. + */ + static interface Http1AsyncDelegate { + /** + * Receives and handles a byte buffer reference. + * @param ref A byte buffer reference coming from upstream. + * @return false, if the byte buffer reference should be kept in the queue. + * Usually, this means that either the byte buffer reference + * was handled and parsing is finished, or that the receiver + * didn't handle the byte reference at all. + * There may or may not be any remaining data in the + * byte buffer, and the byte buffer reference must not have + * been cleared. + * true, if the byte buffer reference was fully read and + * more data can be received. + */ + public boolean tryAsyncReceive(ByteBuffer ref); + + /** + * Called when an exception is raised. + * @param ex The raised Throwable. + */ + public void onReadError(Throwable ex); + + /** + * Must be called before any other method on the delegate. + * The subscription can be either used directly by the delegate + * to request more data (e.g. if the delegate is a header parser), + * or can be forwarded to a downstream subscriber (if the delegate + * is a body parser that wraps a response BodySubscriber). + * In all cases, it is the responsibility of the delegate to ensure + * that request(n) and demand.tryDecrement() are called appropriately. + * No data will be sent to {@code tryAsyncReceive} unless + * the subscription has some demand. + * + * @param s A subscription that allows the delegate to control the + * data flow. + */ + public void onSubscribe(AbstractSubscription s); + + /** + * Returns the subscription that was passed to {@code onSubscribe} + * @return the subscription that was passed to {@code onSubscribe}.. + */ + public AbstractSubscription subscription(); + + } + + /** + * A simple subclass of AbstractSubscription that ensures the + * SequentialScheduler will be run when request() is called and demand + * becomes positive again. + */ + private static final class Http1AsyncDelegateSubscription + extends AbstractSubscription + { + private final Runnable onCancel; + private final SequentialScheduler scheduler; + Http1AsyncDelegateSubscription(SequentialScheduler scheduler, + Runnable onCancel) { + this.scheduler = scheduler; + this.onCancel = onCancel; + } + @Override + public void request(long n) { + final Demand demand = demand(); + if (demand.increase(n)) { + scheduler.runOrSchedule(); + } + } + @Override + public void cancel() { onCancel.run();} + } + + private final ConcurrentLinkedDeque<ByteBuffer> queue + = new ConcurrentLinkedDeque<>(); + private final SequentialScheduler scheduler = + SequentialScheduler.synchronizedScheduler(this::flush); + private final Executor executor; + private final Http1TubeSubscriber subscriber = new Http1TubeSubscriber(); + private final AtomicReference<Http1AsyncDelegate> pendingDelegateRef; + private final AtomicLong received = new AtomicLong(); + final AtomicBoolean canRequestMore = new AtomicBoolean(); + + private volatile Throwable error; + private volatile Http1AsyncDelegate delegate; + // This reference is only used to prevent early GC of the exchange. + private volatile Http1Exchange<?> owner; + // Only used for checking whether we run on the selector manager thread. + private final HttpClientImpl client; + private boolean retry; + + public Http1AsyncReceiver(Executor executor, Http1Exchange<?> owner) { + this.pendingDelegateRef = new AtomicReference<>(); + this.executor = executor; + this.owner = owner; + this.client = owner.client; + } + + // This is the main loop called by the SequentialScheduler. + // It attempts to empty the queue until the scheduler is stopped, + // or the delegate is unregistered, or the delegate is unable to + // process the data (because it's not ready or already done), which + // it signals by returning 'true'; + private void flush() { + ByteBuffer buf; + try { + assert !client.isSelectorThread() : + "Http1AsyncReceiver::flush should not run in the selector: " + + Thread.currentThread().getName(); + + // First check whether we have a pending delegate that has + // just subscribed, and if so, create a Subscription for it + // and call onSubscribe. + handlePendingDelegate(); + + // Then start emptying the queue, if possible. + while ((buf = queue.peek()) != null) { + Http1AsyncDelegate delegate = this.delegate; + debug.log(Level.DEBUG, "Got %s bytes for delegate %s", + buf.remaining(), delegate); + if (!hasDemand(delegate)) { + // The scheduler will be invoked again later when the demand + // becomes positive. + return; + } + + assert delegate != null; + debug.log(Level.DEBUG, "Forwarding %s bytes to delegate %s", + buf.remaining(), delegate); + // The delegate has demand: feed it the next buffer. + if (!delegate.tryAsyncReceive(buf)) { + final long remaining = buf.remaining(); + debug.log(Level.DEBUG, () -> { + // If the scheduler is stopped, the queue may already + // be empty and the reference may already be released. + String remstr = scheduler.isStopped() ? "" : + " remaining in ref: " + + remaining; + remstr = remstr + + " total remaining: " + remaining(); + return "Delegate done: " + remaining; + }); + canRequestMore.set(false); + // The last buffer parsed may have remaining unparsed bytes. + // Don't take it out of the queue. + return; // done. + } + + // removed parsed buffer from queue, and continue with next + // if available + ByteBuffer parsed = queue.remove(); + canRequestMore.set(queue.isEmpty()); + assert parsed == buf; + } + + // queue is empty: let's see if we should request more + checkRequestMore(); + + } catch (Throwable t) { + Throwable x = error; + if (x == null) error = t; // will be handled in the finally block + debug.log(Level.DEBUG, "Unexpected error caught in flush()", t); + } finally { + // Handles any pending error. + // The most recently subscribed delegate will get the error. + checkForErrors(); + } + } + + /** + * Must be called from within the scheduler main loop. + * Handles any pending errors by calling delegate.onReadError(). + * If the error can be forwarded to the delegate, stops the scheduler. + */ + private void checkForErrors() { + // Handles any pending error. + // The most recently subscribed delegate will get the error. + // If the delegate is null, the error will be handled by the next + // delegate that subscribes. + // If the queue is not empty, wait until it it is empty before + // handling the error. + Http1AsyncDelegate delegate = pendingDelegateRef.get(); + if (delegate == null) delegate = this.delegate; + Throwable x = error; + if (delegate != null && x != null && queue.isEmpty()) { + // forward error only after emptying the queue. + final Object captured = delegate; + debug.log(Level.DEBUG, () -> "flushing " + x + + "\n\t delegate: " + captured + + "\t\t queue.isEmpty: " + queue.isEmpty()); + scheduler.stop(); + delegate.onReadError(x); + } + } + + /** + * Must be called from within the scheduler main loop. + * Figure out whether more data should be requested from the + * Http1TubeSubscriber. + */ + private void checkRequestMore() { + Http1AsyncDelegate delegate = this.delegate; + boolean more = this.canRequestMore.get(); + boolean hasDemand = hasDemand(delegate); + debug.log(Level.DEBUG, () -> "checkRequestMore: " + + "canRequestMore=" + more + ", hasDemand=" + hasDemand + + (delegate == null ? ", delegate=null" : "")); + if (hasDemand) { + subscriber.requestMore(); + } + } + + /** + * Must be called from within the scheduler main loop. + * Return true if the delegate is not null and has some demand. + * @param delegate The Http1AsyncDelegate delegate + * @return true if the delegate is not null and has some demand + */ + private boolean hasDemand(Http1AsyncDelegate delegate) { + if (delegate == null) return false; + AbstractSubscription subscription = delegate.subscription(); + long demand = subscription.demand().get(); + debug.log(Level.DEBUG, "downstream subscription demand is %s", demand); + return demand > 0; + } + + /** + * Must be called from within the scheduler main loop. + * Handles pending delegate subscription. + * Return true if there was some pending delegate subscription and a new + * delegate was subscribed, false otherwise. + * + * @return true if there was some pending delegate subscription and a new + * delegate was subscribed, false otherwise. + */ + private boolean handlePendingDelegate() { + Http1AsyncDelegate pending = pendingDelegateRef.get(); + if (pending != null && pendingDelegateRef.compareAndSet(pending, null)) { + Http1AsyncDelegate delegate = this.delegate; + if (delegate != null) unsubscribe(delegate); + Runnable cancel = () -> { + debug.log(Level.DEBUG, "Downstream subscription cancelled by %s", pending); + // The connection should be closed, as some data may + // be left over in the stream. + try { + setRetryOnError(false); + onReadError(new IOException("subscription cancelled")); + unsubscribe(pending); + } finally { + Http1Exchange<?> exchg = owner; + stop(); + if (exchg != null) exchg.connection().close(); + } + }; + // The subscription created by a delegate is only loosely + // coupled with the upstream subscription. This is partly because + // the header/body parser work with a flow of ByteBuffer, whereas + // we have a flow List<ByteBuffer> upstream. + Http1AsyncDelegateSubscription subscription = + new Http1AsyncDelegateSubscription(scheduler, cancel); + pending.onSubscribe(subscription); + this.delegate = delegate = pending; + final Object captured = delegate; + debug.log(Level.DEBUG, () -> "delegate is now " + captured + + ", demand=" + subscription.demand().get() + + ", canRequestMore=" + canRequestMore.get() + + ", queue.isEmpty=" + queue.isEmpty()); + return true; + } + return false; + } + + synchronized void setRetryOnError(boolean retry) { + this.retry = retry; + } + + void clear() { + debug.log(Level.DEBUG, "cleared"); + this.pendingDelegateRef.set(null); + this.delegate = null; + this.owner = null; + } + + void subscribe(Http1AsyncDelegate delegate) { + synchronized(this) { + pendingDelegateRef.set(delegate); + } + if (queue.isEmpty()) { + canRequestMore.set(true); + } + debug.log(Level.DEBUG, () -> + "Subscribed pending " + delegate + " queue.isEmpty: " + + queue.isEmpty()); + // Everything may have been received already. Make sure + // we parse it. + if (client.isSelectorThread()) { + scheduler.deferOrSchedule(executor); + } else { + scheduler.runOrSchedule(); + } + } + + // Used for debugging only! + long remaining() { + return Utils.remaining(queue.toArray(Utils.EMPTY_BB_ARRAY)); + } + + void unsubscribe(Http1AsyncDelegate delegate) { + synchronized(this) { + if (this.delegate == delegate) { + debug.log(Level.DEBUG, "Unsubscribed %s", delegate); + this.delegate = null; + } + } + } + + // Callback: Consumer of ByteBuffer + private void asyncReceive(ByteBuffer buf) { + debug.log(Level.DEBUG, "Putting %s bytes into the queue", buf.remaining()); + received.addAndGet(buf.remaining()); + queue.offer(buf); + + // This callback is called from within the selector thread. + // Use an executor here to avoid doing the heavy lifting in the + // selector. + scheduler.deferOrSchedule(executor); + } + + // Callback: Consumer of Throwable + void onReadError(Throwable ex) { + Http1AsyncDelegate delegate; + Throwable recorded; + debug.log(Level.DEBUG, "onError: %s", (Object) ex); + synchronized (this) { + delegate = this.delegate; + recorded = error; + if (recorded == null) { + // retry is set to true by HttpExchange when the connection is + // already connected, which means it's been retrieved from + // the pool. + if (retry && (ex instanceof IOException)) { + // could be either EOFException, or + // IOException("connection reset by peer), or + // SSLHandshakeException resulting from the server having + // closed the SSL session. + if (received.get() == 0) { + // If we receive such an exception before having + // received any byte, then in this case, we will + // throw ConnectionExpiredException + // to try & force a retry of the request. + retry = false; + ex = new ConnectionExpiredException( + "subscription is finished", ex); + } + } + error = ex; + } + final Throwable t = (recorded == null ? ex : recorded); + debug.log(Level.DEBUG, () -> "recorded " + t + + "\n\t delegate: " + delegate + + "\t\t queue.isEmpty: " + queue.isEmpty(), ex); + } + if (queue.isEmpty() || pendingDelegateRef.get() != null) { + // This callback is called from within the selector thread. + // Use an executor here to avoid doing the heavy lifting in the + // selector. + scheduler.deferOrSchedule(executor); + } + } + + void stop() { + debug.log(Level.DEBUG, "stopping"); + scheduler.stop(); + delegate = null; + owner = null; + } + + /** + * Returns the TubeSubscriber for reading from the connection flow. + * @return the TubeSubscriber for reading from the connection flow. + */ + TubeSubscriber subscriber() { + return subscriber; + } + + /** + * A simple tube subscriber for reading from the connection flow. + */ + final class Http1TubeSubscriber implements TubeSubscriber { + volatile Flow.Subscription subscription; + volatile boolean completed; + volatile boolean dropped; + + public void onSubscribe(Flow.Subscription subscription) { + // supports being called multiple time. + // doesn't cancel the previous subscription, since that is + // most probably the same as the new subscription. + assert this.subscription == null || dropped == false; + this.subscription = subscription; + dropped = false; + canRequestMore.set(true); + if (delegate != null) { + scheduler.deferOrSchedule(executor); + } + } + + void requestMore() { + Flow.Subscription s = subscription; + if (s == null) return; + if (canRequestMore.compareAndSet(true, false)) { + if (!completed && !dropped) { + debug.log(Level.DEBUG, + "Http1TubeSubscriber: requesting one more from upstream"); + s.request(1); + return; + } + } + debug.log(Level.DEBUG, "Http1TubeSubscriber: no need to request more"); + } + + @Override + public void onNext(List<ByteBuffer> item) { + canRequestMore.set(item.isEmpty()); + for (ByteBuffer buffer : item) { + asyncReceive(buffer); + } + } + + @Override + public void onError(Throwable throwable) { + onReadError(throwable); + completed = true; + } + + @Override + public void onComplete() { + onReadError(new EOFException("EOF reached while reading")); + completed = true; + } + + public void dropSubscription() { + debug.log(Level.DEBUG, "Http1TubeSubscriber: dropSubscription"); + // we could probably set subscription to null here... + // then we might not need the 'dropped' boolean? + dropped = true; + } + + } + + // Drains the content of the queue into a single ByteBuffer. + // The scheduler must be permanently stopped before calling drain(). + ByteBuffer drain(ByteBuffer initial) { + // Revisit: need to clean that up. + // + ByteBuffer b = initial = (initial == null ? Utils.EMPTY_BYTEBUFFER : initial); + assert scheduler.isStopped(); + + if (queue.isEmpty()) return b; + + // sanity check: we shouldn't have queued the same + // buffer twice. + ByteBuffer[] qbb = queue.toArray(new ByteBuffer[queue.size()]); + assert java.util.stream.Stream.of(qbb) + .collect(Collectors.toSet()) + .size() == qbb.length : debugQBB(qbb); + + // compute the number of bytes in the queue, the number of bytes + // in the initial buffer + // TODO: will need revisiting - as it is not guaranteed that all + // data will fit in single BB! + int size = Utils.remaining(qbb, Integer.MAX_VALUE); + int remaining = b.remaining(); + int free = b.capacity() - b.position() - remaining; + debug.log(Level.DEBUG, + "Flushing %s bytes from queue into initial buffer (remaining=%s, free=%s)", + size, remaining, free); + + // check whether the initial buffer has enough space + if (size > free) { + debug.log(Level.DEBUG, + "Allocating new buffer for initial: %s", (size + remaining)); + // allocates a new buffer and copy initial to it + b = ByteBuffer.allocate(size + remaining); + Utils.copy(initial, b); + assert b.position() == remaining; + b.flip(); + assert b.position() == 0; + assert b.limit() == remaining; + assert b.remaining() == remaining; + } + + // store position and limit + int pos = b.position(); + int limit = b.limit(); + assert limit - pos == remaining; + assert b.capacity() >= remaining + size + : "capacity: " + b.capacity() + + ", remaining: " + b.remaining() + + ", size: " + size; + + // prepare to copy the content of the queue + b.position(limit); + b.limit(pos + remaining + size); + assert b.remaining() >= size : + "remaining: " + b.remaining() + ", size: " + size; + + // copy the content of the queue + int count = 0; + for (int i=0; i<qbb.length; i++) { + ByteBuffer b2 = qbb[i]; + int r = b2.remaining(); + assert b.remaining() >= r : "need at least " + r + " only " + + b.remaining() + " available"; + int copied = Utils.copy(b2, b); + assert copied == r : "copied="+copied+" available="+r; + assert b2.remaining() == 0; + count += copied; + } + assert count == size; + assert b.position() == pos + remaining + size : + "b.position="+b.position()+" != "+pos+"+"+remaining+"+"+size; + + // reset limit and position + b.limit(limit+size); + b.position(pos); + + // we can clear the refs + queue.clear(); + final ByteBuffer bb = b; + debug.log(Level.DEBUG, () -> "Initial buffer now has " + bb.remaining() + + " pos=" + bb.position() + " limit=" + bb.limit()); + + return b; + } + + private String debugQBB(ByteBuffer[] qbb) { + StringBuilder msg = new StringBuilder(); + List<ByteBuffer> lbb = Arrays.asList(qbb); + Set<ByteBuffer> sbb = new HashSet<>(Arrays.asList(qbb)); + + int uniquebb = sbb.size(); + msg.append("qbb: ").append(lbb.size()) + .append(" (unique: ").append(uniquebb).append("), ") + .append("duplicates: "); + String sep = ""; + for (ByteBuffer b : lbb) { + if (!sbb.remove(b)) { + msg.append(sep) + .append(String.valueOf(b)) + .append("[remaining=") + .append(b.remaining()) + .append(", position=") + .append(b.position()) + .append(", capacity=") + .append(b.capacity()) + .append("]"); + sep = ", "; + } + } + return msg.toString(); + } + + volatile String dbgTag; + String dbgString() { + String tag = dbgTag; + if (tag == null) { + String flowTag = null; + Http1Exchange<?> exchg = owner; + Object flow = (exchg != null) + ? exchg.connection().getConnectionFlow() + : null; + flowTag = tag = flow == null ? null: (String.valueOf(flow)); + if (flowTag != null) { + dbgTag = tag = flowTag + " Http1AsyncReceiver"; + } else { + tag = "Http1AsyncReceiver"; + } + } + return tag; + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java index faa3304e22b..3dc754eff29 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java @@ -26,40 +26,126 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import jdk.incubator.http.HttpResponse.BodyHandler; -import jdk.incubator.http.HttpResponse.BodyProcessor; +import jdk.incubator.http.HttpResponse.BodySubscriber; import java.nio.ByteBuffer; +import java.util.Objects; import java.util.concurrent.CompletableFuture; -import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import jdk.incubator.http.internal.common.Demand; import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SequentialScheduler; import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Utils; +import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; /** - * Encapsulates one HTTP/1.1 request/responseAsync exchange. + * Encapsulates one HTTP/1.1 request/response exchange. */ class Http1Exchange<T> extends ExchangeImpl<T> { - final HttpRequestImpl request; // main request - private final List<CompletableFuture<?>> operations; // used for cancel + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + private static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("Http1Exchange"::toString, DEBUG); + + final HttpRequestImpl request; // main request final Http1Request requestAction; private volatile Http1Response<T> response; - // use to record possible cancellation raised before any operation - // has been initiated. - private IOException failed; final HttpConnection connection; final HttpClientImpl client; final Executor executor; - volatile ByteBuffer buffer; // used for receiving + private final Http1AsyncReceiver asyncReceiver; + + /** Records a possible cancellation raised before any operation + * has been initiated, or an error received while sending the request. */ + private Throwable failed; + private final List<CompletableFuture<?>> operations; // used for cancel + + /** Must be held when operating on any internal state or data. */ + private final Object lock = new Object(); + + /** Holds the outgoing data, either the headers or a request body part. Or + * an error from the request body publisher. At most there can be ~2 pieces + * of outgoing data ( onComplete|onError can be invoked without demand ).*/ + final ConcurrentLinkedDeque<DataPair> outgoing = new ConcurrentLinkedDeque<>(); + + /** The write publisher, responsible for writing the complete request ( both + * headers and body ( if any ). */ + private final Http1Publisher writePublisher = new Http1Publisher(); + + /** Completed when the header have been published, or there is an error */ + private volatile CompletableFuture<ExchangeImpl<T>> headersSentCF = new MinimalFuture<>(); + /** Completed when the body has been published, or there is an error */ + private volatile CompletableFuture<ExchangeImpl<T>> bodySentCF = new MinimalFuture<>(); + + /** The subscriber to the request's body published. Maybe null. */ + private volatile Http1BodySubscriber bodySubscriber; + + enum State { INITIAL, + HEADERS, + BODY, + ERROR, // terminal state + COMPLETING, + COMPLETED } // terminal state + + private State state = State.INITIAL; + + /** A carrier for either data or an error. Used to carry data, and communicate + * errors from the request ( both headers and body ) to the exchange. */ + static class DataPair { + Throwable throwable; + List<ByteBuffer> data; + DataPair(List<ByteBuffer> data, Throwable throwable){ + this.data = data; + this.throwable = throwable; + } + @Override + public String toString() { + return "DataPair [data=" + data + ", throwable=" + throwable + "]"; + } + } + + /** An abstract supertype for HTTP/1.1 body subscribers. There are two + * concrete implementations: {@link Http1Request.StreamSubscriber}, and + * {@link Http1Request.FixedContentSubscriber}, for receiving chunked and + * fixed length bodies, respectively. */ + static abstract class Http1BodySubscriber implements Flow.Subscriber<ByteBuffer> { + protected volatile Flow.Subscription subscription; + protected volatile boolean complete; + + /** Final sentinel in the stream of request body. */ + static final List<ByteBuffer> COMPLETED = List.of(ByteBuffer.allocate(0)); + + void request(long n) { + DEBUG_LOGGER.log(Level.DEBUG, () -> + "Http1BodySubscriber requesting " + n + ", from " + subscription); + subscription.request(n); + } + + static Http1BodySubscriber completeSubscriber() { + return new Http1BodySubscriber() { + @Override public void onSubscribe(Flow.Subscription subscription) { error(); } + @Override public void onNext(ByteBuffer item) { error(); } + @Override public void onError(Throwable throwable) { error(); } + @Override public void onComplete() { error(); } + private void error() { + throw new InternalError("should not reach here"); + } + }; + } + } @Override public String toString() { - return request.toString(); + return "HTTP/1.1 " + request.toString(); } HttpRequestImpl request() { @@ -74,44 +160,156 @@ class Http1Exchange<T> extends ExchangeImpl<T> { this.client = exchange.client(); this.executor = exchange.executor(); this.operations = new LinkedList<>(); - this.buffer = Utils.EMPTY_BYTEBUFFER; + operations.add(headersSentCF); + operations.add(bodySentCF); if (connection != null) { this.connection = connection; } else { - InetSocketAddress addr = request.getAddress(client); - this.connection = HttpConnection.getConnection(addr, client, request); + InetSocketAddress addr = request.getAddress(); + this.connection = HttpConnection.getConnection(addr, client, request, HTTP_1_1); } - this.requestAction = new Http1Request(request, client, this.connection); + this.requestAction = new Http1Request(request, this); + this.asyncReceiver = new Http1AsyncReceiver(executor, this); + asyncReceiver.subscribe(new InitialErrorReceiver()); } + /** An initial receiver that handles no data, but cancels the request if + * it receives an error. Will be replaced when reading response body. */ + final class InitialErrorReceiver implements Http1AsyncReceiver.Http1AsyncDelegate { + volatile AbstractSubscription s; + @Override + public boolean tryAsyncReceive(ByteBuffer ref) { + return false; // no data has been processed, leave it in the queue + } + @Override + public void onReadError(Throwable ex) { + cancelImpl(ex); + } + + @Override + public void onSubscribe(AbstractSubscription s) { + this.s = s; + } + + public AbstractSubscription subscription() { + return s; + } + } + + @Override HttpConnection connection() { return connection; } + private void connectFlows(HttpConnection connection) { + FlowTube tube = connection.getConnectionFlow(); + debug.log(Level.DEBUG, "%s connecting flows", tube); + + // Connect the flow to our Http1TubeSubscriber: + // asyncReceiver.subscriber(). + tube.connectFlows(writePublisher, + asyncReceiver.subscriber()); + } @Override - T readBody(BodyHandler<T> handler, boolean returnConnectionToPool) - throws IOException - { - BodyProcessor<T> processor = handler.apply(response.responseCode(), - response.responseHeaders()); - CompletableFuture<T> bodyCF = response.readBody(processor, - returnConnectionToPool, - this::executeInline); - try { - return bodyCF.join(); - } catch (CompletionException e) { - throw Utils.getIOException(e); + CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() { + // create the response before sending the request headers, so that + // the response can set the appropriate receivers. + debug.log(Level.DEBUG, "Sending headers only"); + if (response == null) { + response = new Http1Response<>(connection, this, asyncReceiver); } + + debug.log(Level.DEBUG, "response created in advance"); + // If the first attempt to read something triggers EOF, or + // IOException("channel reset by peer"), we're going to retry. + // Instruct the asyncReceiver to throw ConnectionExpiredException + // to force a retry. + asyncReceiver.setRetryOnError(true); + + CompletableFuture<Void> connectCF; + if (!connection.connected()) { + debug.log(Level.DEBUG, "initiating connect async"); + connectCF = connection.connectAsync(); + synchronized (lock) { + operations.add(connectCF); + } + } else { + connectCF = new MinimalFuture<>(); + connectCF.complete(null); + } + + return connectCF + .thenCompose(unused -> { + CompletableFuture<Void> cf = new MinimalFuture<>(); + try { + connectFlows(connection); + + debug.log(Level.DEBUG, "requestAction.headers"); + List<ByteBuffer> data = requestAction.headers(); + synchronized (lock) { + state = State.HEADERS; + } + debug.log(Level.DEBUG, "setting outgoing with headers"); + assert outgoing.isEmpty() : "Unexpected outgoing:" + outgoing; + appendToOutgoing(data); + cf.complete(null); + return cf; + } catch (Throwable t) { + debug.log(Level.DEBUG, "Failed to send headers: %s", t); + connection.close(); + cf.completeExceptionally(t); + return cf; + } }) + .thenCompose(unused -> headersSentCF); } - private void executeInline(Runnable r) { - r.run(); + @Override + CompletableFuture<ExchangeImpl<T>> sendBodyAsync() { + assert headersSentCF.isDone(); + try { + bodySubscriber = requestAction.continueRequest(); + if (bodySubscriber == null) { + bodySubscriber = Http1BodySubscriber.completeSubscriber(); + appendToOutgoing(Http1BodySubscriber.COMPLETED); + } else { + bodySubscriber.request(1); // start + } + } catch (Throwable t) { + connection.close(); + bodySentCF.completeExceptionally(t); + } + return bodySentCF; } - synchronized ByteBuffer getBuffer() { - return buffer; + @Override + CompletableFuture<Response> getResponseAsync(Executor executor) { + CompletableFuture<Response> cf = response.readHeadersAsync(executor); + Throwable cause; + synchronized (lock) { + operations.add(cf); + cause = failed; + failed = null; + } + + if (cause != null) { + Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms]" + + "\n\tCompleting exceptionally with {2}\n", + request.uri(), + request.timeout().isPresent() ? + // calling duration.toMillis() can throw an exception. + // this is just debugging, we don't care if it overflows. + (request.timeout().get().getSeconds() * 1000 + + request.timeout().get().getNano() / 1000000) : -1, + cause); + boolean acknowledged = cf.completeExceptionally(cause); + debug.log(Level.DEBUG, + () -> acknowledged + ? ("completed response with " + cause) + : ("response already completed, ignoring " + cause)); + } + return cf; } @Override @@ -119,53 +317,35 @@ class Http1Exchange<T> extends ExchangeImpl<T> { boolean returnConnectionToPool, Executor executor) { - BodyProcessor<T> processor = handler.apply(response.responseCode(), - response.responseHeaders()); - CompletableFuture<T> bodyCF = response.readBody(processor, + BodySubscriber<T> bs = handler.apply(response.responseCode(), + response.responseHeaders()); + CompletableFuture<T> bodyCF = response.readBody(bs, returnConnectionToPool, executor); return bodyCF; } @Override - void sendHeadersOnly() throws IOException, InterruptedException { - try { - if (!connection.connected()) { - connection.connect(); - } - requestAction.sendHeadersOnly(); - } catch (Throwable e) { - connection.close(); - throw e; + CompletableFuture<Void> ignoreBody() { + return response.ignoreBody(executor); + } + + ByteBuffer drainLeftOverBytes() { + synchronized (lock) { + asyncReceiver.stop(); + return asyncReceiver.drain(Utils.EMPTY_BYTEBUFFER); } } - @Override - void sendBody() throws IOException { - try { - requestAction.continueRequest(); - } catch (Throwable e) { - connection.close(); - throw e; - } + void released() { + Http1Response<T> resp = this.response; + if (resp != null) resp.completed(); + asyncReceiver.clear(); } - @Override - Response getResponse() throws IOException { - try { - response = new Http1Response<>(connection, this); - response.readHeaders(); - Response r = response.response(); - buffer = response.getBuffer(); - return r; - } catch (Throwable t) { - connection.close(); - throw t; - } - } - - private void closeConnection() { - connection.close(); + void completed() { + Http1Response<T> resp = this.response; + if (resp != null) resp.completed(); } /** @@ -174,7 +354,7 @@ class Http1Exchange<T> extends ExchangeImpl<T> { */ @Override void cancel() { - cancel(new IOException("Request cancelled")); + cancelImpl(new IOException("Request cancelled")); } /** @@ -182,66 +362,255 @@ class Http1Exchange<T> extends ExchangeImpl<T> { * If not it closes the connection and completes all pending operations */ @Override - synchronized void cancel(IOException cause) { - if (requestAction != null && requestAction.finished() - && response != null && response.finished()) { - return; - } - connection.close(); + void cancel(IOException cause) { + cancelImpl(cause); + } + + private void cancelImpl(Throwable cause) { + LinkedList<CompletableFuture<?>> toComplete = null; int count = 0; - if (operations.isEmpty()) { - failed = cause; - Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms] no pending operation." - + "\n\tCan''t cancel yet with {2}", - request.uri(), - request.duration() == null ? -1 : - // calling duration.toMillis() can throw an exception. - // this is just debugging, we don't care if it overflows. - (request.duration().getSeconds() * 1000 - + request.duration().getNano() / 1000000), - cause); - } else { - for (CompletableFuture<?> cf : operations) { - cf.completeExceptionally(cause); - count++; + synchronized (lock) { + if (failed == null) + failed = cause; + if (requestAction != null && requestAction.finished() + && response != null && response.finished()) { + return; + } + connection.close(); // TODO: ensure non-blocking if holding the lock + writePublisher.writeScheduler.stop(); + if (operations.isEmpty()) { + Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms] no pending operation." + + "\n\tCan''t cancel yet with {2}", + request.uri(), + request.timeout().isPresent() ? + // calling duration.toMillis() can throw an exception. + // this is just debugging, we don't care if it overflows. + (request.timeout().get().getSeconds() * 1000 + + request.timeout().get().getNano() / 1000000) : -1, + cause); + } else { + for (CompletableFuture<?> cf : operations) { + if (!cf.isDone()) { + if (toComplete == null) toComplete = new LinkedList<>(); + toComplete.add(cf); + count++; + } + } + operations.clear(); } } Log.logError("Http1Exchange.cancel: count=" + count); + if (toComplete != null) { + // We might be in the selector thread in case of timeout, when + // the SelectorManager calls purgeTimeoutsAndReturnNextDeadline() + // There may or may not be other places that reach here + // from the SelectorManager thread, so just make sure we + // don't complete any CF from within the selector manager + // thread. + Executor exec = client.isSelectorThread() + ? executor + : this::runInline; + while (!toComplete.isEmpty()) { + CompletableFuture<?> cf = toComplete.poll(); + exec.execute(() -> { + if (cf.completeExceptionally(cause)) { + debug.log(Level.DEBUG, "completed cf with %s", + (Object) cause); + } + }); + } + } } - CompletableFuture<Response> getResponseAsyncImpl(Executor executor) { - return MinimalFuture.supply( () -> { - response = new Http1Response<>(connection, Http1Exchange.this); - response.readHeaders(); - Response r = response.response(); - buffer = response.getBuffer(); - return r; - }, executor); + private void runInline(Runnable run) { + assert !client.isSelectorThread(); + run.run(); } - @Override - CompletableFuture<Response> getResponseAsync(Executor executor) { - CompletableFuture<Response> cf = - connection.whenReceivingResponse() - .thenCompose((v) -> getResponseAsyncImpl(executor)); - IOException cause; - synchronized(this) { - operations.add(cf); - cause = failed; - failed = null; + /** Returns true if this exchange was canceled. */ + boolean isCanceled() { + synchronized (lock) { + return failed != null; } - if (cause != null) { - Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms]" - + "\n\tCompleting exceptionally with {2}\n", - request.uri(), - request.duration() == null ? -1 : - // calling duration.toMillis() can throw an exception. - // this is just debugging, we don't care if it overflows. - (request.duration().getSeconds() * 1000 - + request.duration().getNano() / 1000000), - cause); - cf.completeExceptionally(cause); + } + + /** Returns the cause for which this exchange was canceled, if available. */ + Throwable getCancelCause() { + synchronized (lock) { + return failed; } - return cf; + } + + /** Convenience for {@link #appendToOutgoing(DataPair)}, with just a Throwable. */ + void appendToOutgoing(Throwable throwable) { + appendToOutgoing(new DataPair(null, throwable)); + } + + /** Convenience for {@link #appendToOutgoing(DataPair)}, with just data. */ + void appendToOutgoing(List<ByteBuffer> item) { + appendToOutgoing(new DataPair(item, null)); + } + + private void appendToOutgoing(DataPair dp) { + debug.log(Level.DEBUG, "appending to outgoing " + dp); + outgoing.add(dp); + writePublisher.writeScheduler.runOrSchedule(); + } + + /** Tells whether, or not, there is any outgoing data that can be published, + * or if there is an error. */ + private boolean hasOutgoing() { + return !outgoing.isEmpty(); + } + + // Invoked only by the publisher + // ALL tasks should execute off the Selector-Manager thread + /** Returns the next portion of the HTTP request, or the error. */ + private DataPair getOutgoing() { + final Executor exec = client.theExecutor(); + final DataPair dp = outgoing.pollFirst(); + + if (dp == null) // publisher has not published anything yet + return null; + + synchronized (lock) { + if (dp.throwable != null) { + state = State.ERROR; + exec.execute(() -> { + connection.close(); + headersSentCF.completeExceptionally(dp.throwable); + bodySentCF.completeExceptionally(dp.throwable); + }); + return dp; + } + + switch (state) { + case HEADERS: + state = State.BODY; + // completeAsync, since dependent tasks should run in another thread + debug.log(Level.DEBUG, "initiating completion of headersSentCF"); + headersSentCF.completeAsync(() -> this, exec); + break; + case BODY: + if (dp.data == Http1BodySubscriber.COMPLETED) { + state = State.COMPLETING; + debug.log(Level.DEBUG, "initiating completion of bodySentCF"); + bodySentCF.completeAsync(() -> this, exec); + } else { + debug.log(Level.DEBUG, "requesting more body from the subscriber"); + exec.execute(() -> bodySubscriber.request(1)); + } + break; + case INITIAL: + case ERROR: + case COMPLETING: + case COMPLETED: + default: + assert false : "Unexpected state:" + state; + } + + return dp; + } + } + + /** A Publisher of HTTP/1.1 headers and request body. */ + final class Http1Publisher implements FlowTube.TubePublisher { + + final System.Logger debug = Utils.getDebugLogger(this::dbgString); + volatile Flow.Subscriber<? super List<ByteBuffer>> subscriber; + volatile boolean cancelled; + final Http1WriteSubscription subscription = new Http1WriteSubscription(); + final Demand demand = new Demand(); + final SequentialScheduler writeScheduler = + SequentialScheduler.synchronizedScheduler(new WriteTask()); + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) { + assert state == State.INITIAL; + Objects.requireNonNull(s); + assert subscriber == null; + + subscriber = s; + debug.log(Level.DEBUG, "got subscriber: %s", s); + s.onSubscribe(subscription); + } + + volatile String dbgTag; + String dbgString() { + String tag = dbgTag; + Object flow = connection.getConnectionFlow(); + if (tag == null && flow != null) { + dbgTag = tag = "Http1Publisher(" + flow + ")"; + } else if (tag == null) { + tag = "Http1Publisher(?)"; + } + return tag; + } + + final class WriteTask implements Runnable { + @Override + public void run() { + assert state != State.COMPLETED : "Unexpected state:" + state; + debug.log(Level.DEBUG, "WriteTask"); + if (subscriber == null) { + debug.log(Level.DEBUG, "no subscriber yet"); + return; + } + debug.log(Level.DEBUG, () -> "hasOutgoing = " + hasOutgoing()); + while (hasOutgoing() && demand.tryDecrement()) { + DataPair dp = getOutgoing(); + + if (dp.throwable != null) { + debug.log(Level.DEBUG, "onError"); + // Do not call the subscriber's onError, it is not required. + writeScheduler.stop(); + } else { + List<ByteBuffer> data = dp.data; + if (data == Http1BodySubscriber.COMPLETED) { + synchronized (lock) { + assert state == State.COMPLETING : "Unexpected state:" + state; + state = State.COMPLETED; + } + debug.log(Level.DEBUG, + "completed, stopping %s", writeScheduler); + writeScheduler.stop(); + // Do nothing more. Just do not publish anything further. + // The next Subscriber will eventually take over. + + } else { + debug.log(Level.DEBUG, () -> + "onNext with " + Utils.remaining(data) + " bytes"); + subscriber.onNext(data); + } + } + } + } + } + + final class Http1WriteSubscription implements Flow.Subscription { + + @Override + public void request(long n) { + if (cancelled) + return; //no-op + demand.increase(n); + debug.log(Level.DEBUG, + "subscription request(%d), demand=%s", n, demand); + writeScheduler.deferOrSchedule(client.theExecutor()); + } + + @Override + public void cancel() { + debug.log(Level.DEBUG, "subscription cancelled"); + if (cancelled) + return; //no-op + cancelled = true; + writeScheduler.stop(); + } + } + } + + String dbgString() { + return "Http1Exchange"; } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java new file mode 100644 index 00000000000..0777529573b --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.net.ProtocolException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +class Http1HeaderParser { + + private static final char CR = '\r'; + private static final char LF = '\n'; + private static final char HT = '\t'; + private static final char SP = ' '; + + private StringBuilder sb = new StringBuilder(); + private String statusLine; + private int responseCode; + private HttpHeaders headers; + private Map<String,List<String>> privateMap = new HashMap<>(); + + enum State { STATUS_LINE, + STATUS_LINE_FOUND_CR, + STATUS_LINE_END, + STATUS_LINE_END_CR, + HEADER, + HEADER_FOUND_CR, + HEADER_FOUND_LF, + HEADER_FOUND_CR_LF, + HEADER_FOUND_CR_LF_CR, + FINISHED } + + private State state = State.STATUS_LINE; + + /** Returns the status-line. */ + String statusLine() { return statusLine; } + + /** Returns the response code. */ + int responseCode() { return responseCode; } + + /** Returns the headers, possibly empty. */ + HttpHeaders headers() { assert state == State.FINISHED; return headers; } + + /** + * Parses HTTP/1.X status-line and headers from the given bytes. Must be + * called successive times, with additional data, until returns true. + * + * All given ByteBuffers will be consumed, until ( possibly ) the last one + * ( when true is returned ), which may not be fully consumed. + * + * @param input the ( partial ) header data + * @return true iff the end of the headers block has been reached + */ + boolean parse(ByteBuffer input) throws ProtocolException { + requireNonNull(input, "null input"); + + while (input.hasRemaining() && state != State.FINISHED) { + switch (state) { + case STATUS_LINE: + readResumeStatusLine(input); + break; + case STATUS_LINE_FOUND_CR: + readStatusLineFeed(input); + break; + case STATUS_LINE_END: + maybeStartHeaders(input); + break; + case STATUS_LINE_END_CR: + maybeEndHeaders(input); + break; + case HEADER: + readResumeHeader(input); + break; + // fallthrough + case HEADER_FOUND_CR: + case HEADER_FOUND_LF: + resumeOrLF(input); + break; + case HEADER_FOUND_CR_LF: + resumeOrSecondCR(input); + break; + case HEADER_FOUND_CR_LF_CR: + resumeOrEndHeaders(input); + break; + default: + throw new InternalError( + "Unexpected state: " + String.valueOf(state)); + } + } + + return state == State.FINISHED; + } + + private void readResumeStatusLine(ByteBuffer input) { + char c = 0; + while (input.hasRemaining() && (c =(char)input.get()) != CR) { + sb.append(c); + } + + if (c == CR) { + state = State.STATUS_LINE_FOUND_CR; + } + } + + private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { + char c = (char)input.get(); + if (c != LF) { + throw protocolException("Bad trailing char, \"%s\", when parsing status-line, \"%s\"", + c, sb.toString()); + } + + statusLine = sb.toString(); + sb = new StringBuilder(); + if (!statusLine.startsWith("HTTP/1.")) { + throw protocolException("Invalid status line: \"%s\"", statusLine); + } + if (statusLine.length() < 12) { + throw protocolException("Invalid status line: \"%s\"", statusLine); + } + responseCode = Integer.parseInt(statusLine.substring(9, 12)); + + state = State.STATUS_LINE_END; + } + + private void maybeStartHeaders(ByteBuffer input) { + assert state == State.STATUS_LINE_END; + assert sb.length() == 0; + char c = (char)input.get(); + if (c == CR) { + state = State.STATUS_LINE_END_CR; + } else { + sb.append(c); + state = State.HEADER; + } + } + + private void maybeEndHeaders(ByteBuffer input) throws ProtocolException { + assert state == State.STATUS_LINE_END_CR; + assert sb.length() == 0; + char c = (char)input.get(); + if (c == LF) { + headers = ImmutableHeaders.of(privateMap); + privateMap = null; + state = State.FINISHED; // no headers + } else { + throw protocolException("Unexpected \"%s\", after status-line CR", c); + } + } + + private void readResumeHeader(ByteBuffer input) { + assert state == State.HEADER; + assert input.hasRemaining(); + while (input.hasRemaining()) { + char c = (char)input.get(); + if (c == CR) { + state = State.HEADER_FOUND_CR; + break; + } else if (c == LF) { + state = State.HEADER_FOUND_LF; + break; + } + + if (c == HT) + c = SP; + sb.append(c); + } + } + + private void addHeaderFromString(String headerString) { + assert sb.length() == 0; + int idx = headerString.indexOf(':'); + if (idx == -1) + return; + String name = headerString.substring(0, idx).trim(); + if (name.isEmpty()) + return; + String value = headerString.substring(idx + 1, headerString.length()).trim(); + + privateMap.computeIfAbsent(name.toLowerCase(Locale.US), + k -> new ArrayList<>()).add(value); + } + + private void resumeOrLF(ByteBuffer input) { + assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF; + char c = (char)input.get(); + if (c == LF && state == State.HEADER_FOUND_CR) { + String headerString = sb.toString(); + sb = new StringBuilder(); + addHeaderFromString(headerString); + state = State.HEADER_FOUND_CR_LF; + } else if (c == SP || c == HT) { + sb.append(SP); // parity with MessageHeaders + state = State.HEADER; + } else { + sb = new StringBuilder(); + sb.append(c); + state = State.HEADER; + } + } + + private void resumeOrSecondCR(ByteBuffer input) { + assert state == State.HEADER_FOUND_CR_LF; + assert sb.length() == 0; + char c = (char)input.get(); + if (c == CR) { + state = State.HEADER_FOUND_CR_LF_CR; + } else { + sb.append(c); + state = State.HEADER; + } + } + + private void resumeOrEndHeaders(ByteBuffer input) throws ProtocolException { + assert state == State.HEADER_FOUND_CR_LF_CR; + char c = (char)input.get(); + if (c == LF) { + state = State.FINISHED; + headers = ImmutableHeaders.of(privateMap); + privateMap = null; + } else { + throw protocolException("Unexpected \"%s\", after CR LF CR", c); + } + } + + private ProtocolException protocolException(String format, Object... args) { + return new ProtocolException(format(format, args)); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java index 5b58483686c..15e53be75a3 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,97 +26,73 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.URI; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; import java.net.InetSocketAddress; -import jdk.incubator.http.HttpConnection.Mode; -import java.nio.charset.StandardCharsets; -import static java.nio.charset.StandardCharsets.US_ASCII; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; +import java.util.Objects; import java.util.concurrent.Flow; +import jdk.incubator.http.Http1Exchange.Http1BodySubscriber; import jdk.incubator.http.internal.common.HttpHeadersImpl; import jdk.incubator.http.internal.common.Log; -import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Utils; +import static java.nio.charset.StandardCharsets.US_ASCII; + /** - * A HTTP/1.1 request. - * - * send() -> Writes the request + body to the given channel, in one blocking - * operation. + * An HTTP/1.1 request. */ class Http1Request { - final HttpClientImpl client; - final HttpRequestImpl request; - final HttpConnection chan; - // Multiple buffers are used to hold different parts of request - // See line 206 and below for description - final ByteBuffer[] buffers; - final HttpRequest.BodyProcessor requestProc; - final HttpHeaders userHeaders; - final HttpHeadersImpl systemHeaders; - boolean streaming; - long contentLength; - final CompletableFuture<Void> cf; + private final HttpRequestImpl request; + private final Http1Exchange<?> http1Exchange; + private final HttpConnection connection; + private final HttpRequest.BodyPublisher requestPublisher; + private final HttpHeaders userHeaders; + private final HttpHeadersImpl systemHeaders; + private volatile boolean streaming; + private volatile long contentLength; - Http1Request(HttpRequestImpl request, HttpClientImpl client, HttpConnection connection) + Http1Request(HttpRequestImpl request, + Http1Exchange<?> http1Exchange) throws IOException { - this.client = client; this.request = request; - this.chan = connection; - buffers = new ByteBuffer[5]; // TODO: check - this.requestProc = request.requestProcessor; + this.http1Exchange = http1Exchange; + this.connection = http1Exchange.connection(); + this.requestPublisher = request.requestPublisher; // may be null this.userHeaders = request.getUserHeaders(); this.systemHeaders = request.getSystemHeaders(); - this.cf = new MinimalFuture<>(); } - private void logHeaders() throws IOException { - StringBuilder sb = new StringBuilder(256); - sb.append("REQUEST HEADERS:\n"); - Log.dumpHeaders(sb, " ", systemHeaders); - Log.dumpHeaders(sb, " ", userHeaders); - Log.logHeaders(sb.toString()); - } - - private void dummy(long x) { - // not used in this class - } - - private void collectHeaders0() throws IOException { + private void logHeaders(String completeHeaders) { if (Log.headers()) { - logHeaders(); + //StringBuilder sb = new StringBuilder(256); + //sb.append("REQUEST HEADERS:\n"); + //Log.dumpHeaders(sb, " ", systemHeaders); + //Log.dumpHeaders(sb, " ", userHeaders); + //Log.logHeaders(sb.toString()); + + String s = completeHeaders.replaceAll("\r\n", "\n"); + Log.logHeaders("REQUEST HEADERS:\n" + s); } - StringBuilder sb = new StringBuilder(256); - collectHeaders1(sb, request, systemHeaders); - collectHeaders1(sb, request, userHeaders); - sb.append("\r\n"); - String headers = sb.toString(); - buffers[1] = ByteBuffer.wrap(headers.getBytes(StandardCharsets.US_ASCII)); } - private void collectHeaders1(StringBuilder sb, - HttpRequestImpl request, - HttpHeaders headers) - throws IOException - { - Map<String,List<String>> h = headers.map(); - Set<Map.Entry<String,List<String>>> entries = h.entrySet(); + private void collectHeaders0(StringBuilder sb) { + collectHeaders1(sb, systemHeaders); + collectHeaders1(sb, userHeaders); + sb.append("\r\n"); + } - for (Map.Entry<String,List<String>> entry : entries) { + private void collectHeaders1(StringBuilder sb, HttpHeaders headers) { + for (Map.Entry<String,List<String>> entry : headers.map().entrySet()) { String key = entry.getKey(); List<String> values = entry.getValue(); for (String value : values) { - sb.append(key) - .append(": ") - .append(value) - .append("\r\n"); + sb.append(key).append(": ").append(value).append("\r\n"); } } } @@ -166,7 +142,7 @@ class Http1Request { URI uri = request.uri(); String method = request.method(); - if ((request.proxy(client) == null && !method.equals("CONNECT")) + if ((request.proxy() == null && !method.equals("CONNECT")) || request.isWebSocket()) { return getPathAndQuery(uri); } @@ -179,25 +155,12 @@ class Http1Request { return getPathAndQuery(uri); } } - return uri == null? authorityString(request.authority()) : uri.toString(); - } - - void sendHeadersOnly() throws IOException { - collectHeaders(); - chan.write(buffers, 0, 2); - } - - void sendRequest() throws IOException { - collectHeaders(); - chan.configureMode(Mode.BLOCKING); - if (contentLength == 0) { - chan.write(buffers, 0, 2); - } else if (contentLength > 0) { - writeFixedContent(true); - } else { - writeStreamedContent(true); + if (request.method().equals("CONNECT")) { + // use authority for connect itself + return authorityString(request.authority()); } - setFinished(); + + return uri == null? authorityString(request.authority()) : uri.toString(); } private boolean finished; @@ -210,7 +173,7 @@ class Http1Request { finished = true; } - private void collectHeaders() throws IOException { + List<ByteBuffer> headers() { if (Log.requests() && request != null) { Log.logRequest(request.toString()); } @@ -220,250 +183,183 @@ class Http1Request { .append(' ') .append(uriString) .append(" HTTP/1.1\r\n"); - String cmd = sb.toString(); - buffers[0] = ByteBuffer.wrap(cmd.getBytes(StandardCharsets.US_ASCII)); URI uri = request.uri(); if (uri != null) { systemHeaders.setHeader("Host", hostString()); } - if (request == null) { - // this is not a user request. No content + if (request == null || requestPublisher == null) { + // Not a user request, or maybe a method, e.g. GET, with no body. contentLength = 0; } else { - contentLength = requestProc.contentLength(); + contentLength = requestPublisher.contentLength(); } if (contentLength == 0) { systemHeaders.setHeader("Content-Length", "0"); - collectHeaders0(); } else if (contentLength > 0) { - /* [0] request line [1] headers [2] body */ - systemHeaders.setHeader("Content-Length", - Integer.toString((int) contentLength)); + systemHeaders.setHeader("Content-Length", Long.toString(contentLength)); streaming = false; - collectHeaders0(); - buffers[2] = getBuffer(); } else { - /* Chunked: - * - * [0] request line [1] headers [2] chunk header [3] chunk data [4] - * final chunk header and trailing CRLF of previous chunks - * - * 2,3,4 used repeatedly */ streaming = true; systemHeaders.setHeader("Transfer-encoding", "chunked"); - collectHeaders0(); - buffers[3] = getBuffer(); } + collectHeaders0(sb); + String hs = sb.toString(); + logHeaders(hs); + ByteBuffer b = ByteBuffer.wrap(hs.getBytes(US_ASCII)); + return List.of(b); } - private ByteBuffer getBuffer() { - return ByteBuffer.allocate(Utils.BUFSIZE); - } - - // The following two methods used by Http1Exchange to handle expect continue - - void continueRequest() throws IOException { + Http1BodySubscriber continueRequest() { + Http1BodySubscriber subscriber; if (streaming) { - writeStreamedContent(false); + subscriber = new StreamSubscriber(); + requestPublisher.subscribe(subscriber); } else { - writeFixedContent(false); + if (contentLength == 0) + return null; + + subscriber = new FixedContentSubscriber(); + requestPublisher.subscribe(subscriber); } - setFinished(); + return subscriber; } - class StreamSubscriber implements Flow.Subscriber<ByteBuffer> { - volatile Flow.Subscription subscription; - volatile boolean includeHeaders; - - StreamSubscriber(boolean includeHeaders) { - this.includeHeaders = includeHeaders; - } + class StreamSubscriber extends Http1BodySubscriber { @Override public void onSubscribe(Flow.Subscription subscription) { if (this.subscription != null) { - throw new IllegalStateException("already subscribed"); + Throwable t = new IllegalStateException("already subscribed"); + http1Exchange.appendToOutgoing(t); + } else { + this.subscription = subscription; } - this.subscription = subscription; - subscription.request(1); } @Override public void onNext(ByteBuffer item) { - int startbuf, nbufs; - - if (cf.isDone()) { - throw new IllegalStateException("subscription already completed"); - } - - if (includeHeaders) { - startbuf = 0; - nbufs = 5; + Objects.requireNonNull(item); + if (complete) { + Throwable t = new IllegalStateException("subscription already completed"); + http1Exchange.appendToOutgoing(t); } else { - startbuf = 2; - nbufs = 3; + int chunklen = item.remaining(); + ArrayList<ByteBuffer> l = new ArrayList<>(3); + l.add(getHeader(chunklen)); + l.add(item); + l.add(ByteBuffer.wrap(CRLF)); + http1Exchange.appendToOutgoing(l); } - int chunklen = item.remaining(); - buffers[3] = item; - buffers[2] = getHeader(chunklen); - buffers[4] = CRLF_BUFFER(); - try { - chan.write(buffers, startbuf, nbufs); - } catch (IOException e) { - subscription.cancel(); - cf.completeExceptionally(e); - } - includeHeaders = false; - subscription.request(1); } @Override public void onError(Throwable throwable) { - if (cf.isDone()) { + if (complete) return; - } + subscription.cancel(); - cf.completeExceptionally(throwable); + http1Exchange.appendToOutgoing(throwable); } @Override public void onComplete() { - if (cf.isDone()) { - throw new IllegalStateException("subscription already completed"); + if (complete) { + Throwable t = new IllegalStateException("subscription already completed"); + http1Exchange.appendToOutgoing(t); + } else { + ArrayList<ByteBuffer> l = new ArrayList<>(2); + l.add(ByteBuffer.wrap(EMPTY_CHUNK_BYTES)); + l.add(ByteBuffer.wrap(CRLF)); + complete = true; + //setFinished(); + http1Exchange.appendToOutgoing(l); + http1Exchange.appendToOutgoing(COMPLETED); + setFinished(); // TODO: before or after,? does it matter? + } - buffers[3] = EMPTY_CHUNK_HEADER(); - buffers[4] = CRLF_BUFFER(); - try { - chan.write(buffers, 3, 2); - } catch (IOException ex) { - cf.completeExceptionally(ex); - return; - } - cf.complete(null); } } - private void waitForCompletion() throws IOException { - try { - cf.join(); - } catch (CompletionException e) { - throw Utils.getIOException(e); - } - } + class FixedContentSubscriber extends Http1BodySubscriber { - /* Entire request is sent, or just body only */ - private void writeStreamedContent(boolean includeHeaders) - throws IOException - { - StreamSubscriber subscriber = new StreamSubscriber(includeHeaders); - requestProc.subscribe(subscriber); - waitForCompletion(); - } - - class FixedContentSubscriber implements Flow.Subscriber<ByteBuffer> - { - volatile Flow.Subscription subscription; - volatile boolean includeHeaders; - volatile long contentWritten = 0; - - FixedContentSubscriber(boolean includeHeaders) { - this.includeHeaders = includeHeaders; - } + private volatile long contentWritten; @Override public void onSubscribe(Flow.Subscription subscription) { if (this.subscription != null) { - throw new IllegalStateException("already subscribed"); + Throwable t = new IllegalStateException("already subscribed"); + http1Exchange.appendToOutgoing(t); + } else { + this.subscription = subscription; } - this.subscription = subscription; - subscription.request(1); } @Override public void onNext(ByteBuffer item) { - int startbuf, nbufs; - long headersLength; - - if (includeHeaders) { - startbuf = 0; - nbufs = 3; - headersLength = buffers[0].remaining() + buffers[1].remaining(); + debug.log(Level.DEBUG, "onNext"); + Objects.requireNonNull(item); + if (complete) { + Throwable t = new IllegalStateException("subscription already completed"); + http1Exchange.appendToOutgoing(t); } else { - startbuf = 2; - nbufs = 1; - headersLength = 0; - } - buffers[2] = item; - try { - long writing = buffers[2].remaining() + headersLength; - contentWritten += buffers[2].remaining(); - chan.checkWrite(writing, buffers, startbuf, nbufs); + long writing = item.remaining(); + long written = (contentWritten += writing); - if (contentWritten > contentLength) { - String msg = "Too many bytes in request body. Expected: " + - Long.toString(contentLength) + " Sent: " + - Long.toString(contentWritten); - throw new IOException(msg); + if (written > contentLength) { + subscription.cancel(); + String msg = connection.getConnectionFlow() + + " [" + Thread.currentThread().getName() +"] " + + "Too many bytes in request body. Expected: " + + contentLength + ", got: " + written; + http1Exchange.appendToOutgoing(new IOException(msg)); + } else { + http1Exchange.appendToOutgoing(List.of(item)); } - subscription.request(1); - } catch (IOException e) { - subscription.cancel(); - cf.completeExceptionally(e); } } @Override public void onError(Throwable throwable) { - if (cf.isDone()) { + debug.log(Level.DEBUG, "onError"); + if (complete) // TODO: error? return; - } + subscription.cancel(); - cf.completeExceptionally(throwable); + http1Exchange.appendToOutgoing(throwable); } @Override public void onComplete() { - if (cf.isDone()) { - throw new IllegalStateException("subscription already completed"); - } - - if (contentLength > contentWritten) { - subscription.cancel(); - Exception e = new IOException("Too few bytes returned by the processor"); - cf.completeExceptionally(e); + debug.log(Level.DEBUG, "onComplete"); + if (complete) { + Throwable t = new IllegalStateException("subscription already completed"); + http1Exchange.appendToOutgoing(t); } else { - cf.complete(null); + complete = true; + long written = contentWritten; + if (contentLength > written) { + subscription.cancel(); + Throwable t = new IOException(connection.getConnectionFlow() + + " [" + Thread.currentThread().getName() +"] " + + "Too few bytes returned by the publisher (" + + written + "/" + + contentLength + ")"); + http1Exchange.appendToOutgoing(t); + } else { + http1Exchange.appendToOutgoing(COMPLETED); + } } } } - /* Entire request is sent, or just body only */ - private void writeFixedContent(boolean includeHeaders) - throws IOException { - if (contentLength == 0) { - return; - } - FixedContentSubscriber subscriber = new FixedContentSubscriber(includeHeaders); - requestProc.subscribe(subscriber); - waitForCompletion(); - } - private static final byte[] CRLF = {'\r', '\n'}; private static final byte[] EMPTY_CHUNK_BYTES = {'0', '\r', '\n'}; - private ByteBuffer CRLF_BUFFER() { - return ByteBuffer.wrap(CRLF); - } - - private ByteBuffer EMPTY_CHUNK_HEADER() { - return ByteBuffer.wrap(EMPTY_CHUNK_BYTES); - } - - /* Returns a header for a particular chunk size */ - private static ByteBuffer getHeader(int size){ - String hexStr = Integer.toHexString(size); + /** Returns a header for a particular chunk size */ + private static ByteBuffer getHeader(int size) { + String hexStr = Integer.toHexString(size); byte[] hexBytes = hexStr.getBytes(US_ASCII); byte[] header = new byte[hexStr.length()+2]; System.arraycopy(hexBytes, 0, header, 0, hexBytes.length); @@ -471,4 +367,8 @@ class Http1Request { header[hexBytes.length+1] = CRLF[1]; return ByteBuffer.wrap(header); } + + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::toString, DEBUG); + } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java index 92ce50c28bf..7e948649185 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java @@ -25,16 +25,24 @@ package jdk.incubator.http; -import java.io.IOException; +import java.io.EOFException; +import java.lang.System.Logger.Level; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import java.util.concurrent.Executor; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import jdk.incubator.http.ResponseContent.BodyParser; import jdk.incubator.http.internal.common.Log; import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; +import jdk.incubator.http.internal.common.MinimalFuture; +import jdk.incubator.http.internal.common.Utils; /** - * Handles a HTTP/1.1 response in two blocking calls. readHeaders() and - * readBody(). There can be more than one of these per Http exchange. + * Handles a HTTP/1.1 response (headers + body). + * There can be more than one of these per Http exchange. */ class Http1Response<T> { @@ -42,48 +50,71 @@ class Http1Response<T> { private final HttpRequestImpl request; private Response response; private final HttpConnection connection; - private ResponseHeaders headers; + private HttpHeaders headers; private int responseCode; - private ByteBuffer buffer; private final Http1Exchange<T> exchange; - private final boolean redirecting; // redirecting private boolean return2Cache; // return connection to cache when finished + private final HeadersReader headersReader; // used to read the headers + private final BodyReader bodyReader; // used to read the body + private final Http1AsyncReceiver asyncReceiver; + private volatile EOFException eof; + // max number of bytes of (fixed length) body to ignore on redirect + private final static int MAX_IGNORE = 1024; - Http1Response(HttpConnection conn, Http1Exchange<T> exchange) { + // Revisit: can we get rid of this? + static enum State {INITIAL, READING_HEADERS, READING_BODY, DONE} + private volatile State readProgress = State.INITIAL; + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this.getClass()::getSimpleName, DEBUG); + + + Http1Response(HttpConnection conn, + Http1Exchange<T> exchange, + Http1AsyncReceiver asyncReceiver) { + this.readProgress = State.INITIAL; this.request = exchange.request(); this.exchange = exchange; this.connection = conn; - this.redirecting = false; - buffer = exchange.getBuffer(); + this.asyncReceiver = asyncReceiver; + headersReader = new HeadersReader(this::advance); + bodyReader = new BodyReader(this::advance); } - @SuppressWarnings("unchecked") - public void readHeaders() throws IOException { - String statusline = readStatusLine(); - if (statusline == null) { - if (Log.errors()) { - Log.logError("Connection closed. Retry"); - } - connection.close(); - // connection was closed - throw new IOException("Connection closed"); - } - if (!statusline.startsWith("HTTP/1.")) { - throw new IOException("Invalid status line: " + statusline); - } - if (Log.trace()) { - Log.logTrace("Statusline: {0}", statusline); - } - char c = statusline.charAt(7); - responseCode = Integer.parseInt(statusline.substring(9, 12)); + public CompletableFuture<Response> readHeadersAsync(Executor executor) { + debug.log(Level.DEBUG, () -> "Reading Headers: (remaining: " + + asyncReceiver.remaining() +") " + readProgress); + // with expect continue we will resume reading headers + body. + asyncReceiver.unsubscribe(bodyReader); + bodyReader.reset(); + Http1HeaderParser hd = new Http1HeaderParser(); + readProgress = State.READING_HEADERS; + headersReader.start(hd); + asyncReceiver.subscribe(headersReader); + CompletableFuture<State> cf = headersReader.completion(); + assert cf != null : "parsing not started"; - headers = new ResponseHeaders(connection, buffer); - if (Log.headers()) { - logHeaders(headers); + Function<State, Response> lambda = (State completed) -> { + assert completed == State.READING_HEADERS; + debug.log(Level.DEBUG, () -> + "Reading Headers: creating Response object;" + + " state is now " + readProgress); + asyncReceiver.unsubscribe(headersReader); + responseCode = hd.responseCode(); + headers = hd.headers(); + + response = new Response(request, + exchange.getExchange(), + headers, + responseCode, + HTTP_1_1); + return response; + }; + + if (executor != null) { + return cf.thenApplyAsync(lambda, executor); + } else { + return cf.thenApply(lambda); } - response = new Response( - request, exchange.getExchange(), - headers, responseCode, HTTP_1_1); } private boolean finished; @@ -96,10 +127,6 @@ class Http1Response<T> { return finished; } - ByteBuffer getBuffer() { - return buffer; - } - int fixupContentLen(int clen) { if (request.method().equalsIgnoreCase("HEAD")) { return 0; @@ -114,79 +141,123 @@ class Http1Response<T> { return clen; } - public CompletableFuture<T> readBody( - HttpResponse.BodyProcessor<T> p, - boolean return2Cache, - Executor executor) { - final BlockingPushPublisher<ByteBuffer> publisher = new BlockingPushPublisher<>(); - return readBody(p, return2Cache, publisher, executor); + /** + * Read up to MAX_IGNORE bytes discarding + */ + public CompletableFuture<Void> ignoreBody(Executor executor) { + int clen = (int)headers.firstValueAsLong("Content-Length").orElse(-1); + if (clen == -1 || clen > MAX_IGNORE) { + connection.close(); + return MinimalFuture.completedFuture(null); // not treating as error + } else { + return readBody(HttpResponse.BodySubscriber.discard((Void)null), true, executor); + } } - private CompletableFuture<T> readBody( - HttpResponse.BodyProcessor<T> p, - boolean return2Cache, - AbstractPushPublisher<ByteBuffer> publisher, - Executor executor) { + public <U> CompletableFuture<U> readBody(HttpResponse.BodySubscriber<U> p, + boolean return2Cache, + Executor executor) { this.return2Cache = return2Cache; - final jdk.incubator.http.HttpResponse.BodyProcessor<T> pusher = p; - final CompletableFuture<T> cf = p.getBody().toCompletableFuture(); + final HttpResponse.BodySubscriber<U> pusher = p; + final CompletionStage<U> bodyCF = p.getBody(); + final CompletableFuture<U> cf = MinimalFuture.of(bodyCF); + + int clen0 = (int)headers.firstValueAsLong("Content-Length").orElse(-1); - int clen0; - try { - clen0 = headers.getContentLength(); - } catch (IOException ex) { - cf.completeExceptionally(ex); - return cf; - } final int clen = fixupContentLen(clen0); + // expect-continue reads headers and body twice. + // if we reach here, we must reset the headersReader state. + asyncReceiver.unsubscribe(headersReader); + headersReader.reset(); + executor.execute(() -> { try { + HttpClientImpl client = connection.client(); content = new ResponseContent( connection, clen, headers, pusher, - publisher.asDataConsumer(), - (t -> { - publisher.acceptError(t); - connection.close(); - cf.completeExceptionally(t); - }), - () -> onFinished() + this::onFinished ); - publisher.subscribe(p); if (cf.isCompletedExceptionally()) { // if an error occurs during subscription connection.close(); return; } - content.pushBody(buffer); + // increment the reference count on the HttpClientImpl + // to prevent the SelectorManager thread from exiting until + // the body is fully read. + client.reference(); + bodyReader.start(content.getBodyParser( + (t) -> { + try { + if (t != null) { + pusher.onError(t); + connection.close(); + if (!cf.isDone()) + cf.completeExceptionally(t); + } + } finally { + // decrement the reference count on the HttpClientImpl + // to allow the SelectorManager thread to exit if no + // other operation is pending and the facade is no + // longer referenced. + client.unreference(); + bodyReader.onComplete(t); + } + })); + CompletableFuture<State> bodyReaderCF = bodyReader.completion(); + asyncReceiver.subscribe(bodyReader); + assert bodyReaderCF != null : "parsing not started"; + // Make sure to keep a reference to asyncReceiver from + // within this + CompletableFuture<?> trailingOp = bodyReaderCF.whenComplete((s,t) -> { + t = Utils.getCompletionCause(t); + try { + if (t != null) { + debug.log(Level.DEBUG, () -> + "Finished reading body: " + s); + assert s == State.READING_BODY; + } + if (t != null && !cf.isDone()) { + pusher.onError(t); + cf.completeExceptionally(t); + } + } catch (Throwable x) { + // not supposed to happen + asyncReceiver.onReadError(x); + } + }); + connection.addTrailingOperation(trailingOp); } catch (Throwable t) { - cf.completeExceptionally(t); + debug.log(Level.DEBUG, () -> "Failed reading body: " + t); + try { + if (!cf.isDone()) { + pusher.onError(t); + cf.completeExceptionally(t); + } + } finally { + asyncReceiver.onReadError(t); + } } }); return cf; } + private void onFinished() { + asyncReceiver.clear(); if (return2Cache) { - Log.logTrace("Returning connection to the pool: {0}", connection); - connection.returnToCache(headers); + Log.logTrace("Attempting to return connection to the pool: {0}", connection); + // TODO: need to do something here? + // connection.setAsyncCallbacks(null, null, null); + + // don't return the connection to the cache if EOF happened. + debug.log(Level.DEBUG, () -> connection.getConnectionFlow() + + ": return to HTTP/1.1 pool"); + connection.closeOrReturnToCache(eof == null ? headers : null); } } - private void logHeaders(ResponseHeaders headers) { - StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n"); - Log.dumpHeaders(sb, " ", headers); - Log.logHeaders(sb.toString()); - } - - Response response() { - return response; - } - - boolean redirecting() { - return redirecting; - } - HttpHeaders responseHeaders() { return headers; } @@ -195,50 +266,251 @@ class Http1Response<T> { return responseCode; } - static final char CR = '\r'; - static final char LF = '\n'; +// ================ Support for plugging into Http1Receiver ================= +// ============================================================================ - private int obtainBuffer() throws IOException { - int n = buffer.remaining(); - - if (n == 0) { - buffer = connection.read(); - if (buffer == null) { - return -1; - } - n = buffer.remaining(); + // Callback: Error receiver: Consumer of Throwable. + void onReadError(Throwable t) { + Log.logError(t); + Receiver<?> receiver = receiver(readProgress); + if (t instanceof EOFException) { + debug.log(Level.DEBUG, "onReadError: received EOF"); + eof = (EOFException) t; } - return n; + CompletableFuture<?> cf = receiver == null ? null : receiver.completion(); + debug.log(Level.DEBUG, () -> "onReadError: cf is " + + (cf == null ? "null" + : (cf.isDone() ? "already completed" + : "not yet completed"))); + if (cf != null && !cf.isDone()) cf.completeExceptionally(t); + else { debug.log(Level.DEBUG, "onReadError", t); } + debug.log(Level.DEBUG, () -> "closing connection: cause is " + t); + connection.close(); } - String readStatusLine() throws IOException { - boolean cr = false; - StringBuilder statusLine = new StringBuilder(128); - while ((obtainBuffer()) != -1) { - byte[] buf = buffer.array(); - int offset = buffer.position(); - int len = buffer.limit() - offset; + // ======================================================================== - for (int i = 0; i < len; i++) { - char c = (char) buf[i+offset]; + private State advance(State previous) { + assert readProgress == previous; + switch(previous) { + case READING_HEADERS: + asyncReceiver.unsubscribe(headersReader); + return readProgress = State.READING_BODY; + case READING_BODY: + asyncReceiver.unsubscribe(bodyReader); + return readProgress = State.DONE; + default: + throw new InternalError("can't advance from " + previous); + } + } - if (cr) { - if (c == LF) { - buffer.position(i + 1 + offset); - return statusLine.toString(); - } else { - throw new IOException("invalid status line"); - } + Receiver<?> receiver(State state) { + switch(state) { + case READING_HEADERS: return headersReader; + case READING_BODY: return bodyReader; + default: return null; + } + + } + + static abstract class Receiver<T> + implements Http1AsyncReceiver.Http1AsyncDelegate { + abstract void start(T parser); + abstract CompletableFuture<State> completion(); + // accepts a buffer from upstream. + // this should be implemented as a simple call to + // accept(ref, parser, cf) + public abstract boolean tryAsyncReceive(ByteBuffer buffer); + public abstract void onReadError(Throwable t); + // handle a byte buffer received from upstream. + // this method should set the value of Http1Response.buffer + // to ref.get() before beginning parsing. + abstract void handle(ByteBuffer buf, T parser, + CompletableFuture<State> cf); + // resets this objects state so that it can be reused later on + // typically puts the reference to parser and completion to null + abstract void reset(); + + // accepts a byte buffer received from upstream + // returns true if the buffer is fully parsed and more data can + // be accepted, false otherwise. + final boolean accept(ByteBuffer buf, T parser, + CompletableFuture<State> cf) { + if (cf == null || parser == null || cf.isDone()) return false; + handle(buf, parser, cf); + return !cf.isDone(); + } + public abstract void onSubscribe(AbstractSubscription s); + public abstract AbstractSubscription subscription(); + + } + + // Invoked with each new ByteBuffer when reading headers... + final class HeadersReader extends Receiver<Http1HeaderParser> { + final Consumer<State> onComplete; + volatile Http1HeaderParser parser; + volatile CompletableFuture<State> cf; + volatile long count; // bytes parsed (for debug) + volatile AbstractSubscription subscription; + + HeadersReader(Consumer<State> onComplete) { + this.onComplete = onComplete; + } + + @Override + public AbstractSubscription subscription() { + return subscription; + } + + @Override + public void onSubscribe(AbstractSubscription s) { + this.subscription = s; + s.request(1); + } + + @Override + void reset() { + cf = null; + parser = null; + count = 0; + subscription = null; + } + + // Revisit: do we need to support restarting? + @Override + final void start(Http1HeaderParser hp) { + count = 0; + cf = new MinimalFuture<>(); + parser = hp; + } + + @Override + CompletableFuture<State> completion() { + return cf; + } + + @Override + public final boolean tryAsyncReceive(ByteBuffer ref) { + boolean hasDemand = subscription.demand().tryDecrement(); + assert hasDemand; + boolean needsMore = accept(ref, parser, cf); + if (needsMore) subscription.request(1); + return needsMore; + } + + @Override + public final void onReadError(Throwable t) { + Http1Response.this.onReadError(t); + } + + @Override + final void handle(ByteBuffer b, + Http1HeaderParser parser, + CompletableFuture<State> cf) { + assert cf != null : "parsing not started"; + assert parser != null : "no parser"; + try { + count += b.remaining(); + debug.log(Level.DEBUG, () -> "Sending " + b.remaining() + + "/" + b.capacity() + " bytes to header parser"); + if (parser.parse(b)) { + count -= b.remaining(); + debug.log(Level.DEBUG, () -> + "Parsing headers completed. bytes=" + count); + onComplete.accept(State.READING_HEADERS); + cf.complete(State.READING_HEADERS); } - if (c == CR) { - cr = true; - } else { - statusLine.append(c); + } catch (Throwable t) { + debug.log(Level.DEBUG, + () -> "Header parser failed to handle buffer: " + t); + cf.completeExceptionally(t); + } + } + } + + // Invoked with each new ByteBuffer when reading bodies... + final class BodyReader extends Receiver<BodyParser> { + final Consumer<State> onComplete; + volatile BodyParser parser; + volatile CompletableFuture<State> cf; + volatile AbstractSubscription subscription; + BodyReader(Consumer<State> onComplete) { + this.onComplete = onComplete; + } + + @Override + void reset() { + parser = null; + cf = null; + subscription = null; + } + + // Revisit: do we need to support restarting? + @Override + final void start(BodyParser parser) { + cf = new MinimalFuture<>(); + this.parser = parser; + } + + @Override + CompletableFuture<State> completion() { + return cf; + } + + @Override + public final boolean tryAsyncReceive(ByteBuffer b) { + return accept(b, parser, cf); + } + + @Override + public final void onReadError(Throwable t) { + Http1Response.this.onReadError(t); + } + + @Override + public AbstractSubscription subscription() { + return subscription; + } + + @Override + public void onSubscribe(AbstractSubscription s) { + this.subscription = s; + parser.onSubscribe(s); + } + + @Override + final void handle(ByteBuffer b, + BodyParser parser, + CompletableFuture<State> cf) { + assert cf != null : "parsing not started"; + assert parser != null : "no parser"; + try { + debug.log(Level.DEBUG, () -> "Sending " + b.remaining() + + "/" + b.capacity() + " bytes to body parser"); + parser.accept(b); + } catch (Throwable t) { + debug.log(Level.DEBUG, + () -> "Body parser failed to handle buffer: " + t); + if (!cf.isDone()) { + cf.completeExceptionally(t); } } - // unlikely, but possible, that multiple reads required - buffer.position(buffer.limit()); } - return null; + + final void onComplete(Throwable closedExceptionally) { + if (cf.isDone()) return; + if (closedExceptionally != null) { + cf.completeExceptionally(closedExceptionally); + } else { + onComplete.accept(State.READING_BODY); + cf.complete(State.READING_BODY); + } + } + + @Override + public String toString() { + return super.toString() + "/parser=" + String.valueOf(parser); + } + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java index 7b8205cfe5a..21a8a4a4fd2 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,17 +25,18 @@ package jdk.incubator.http; -import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.net.URI; import java.util.Base64; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - +import java.util.concurrent.CompletableFuture; +import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Utils; import jdk.incubator.http.internal.frame.SettingsFrame; import static jdk.incubator.http.internal.frame.SettingsFrame.INITIAL_WINDOW_SIZE; @@ -49,6 +50,10 @@ import static jdk.incubator.http.internal.frame.SettingsFrame.MAX_FRAME_SIZE; */ class Http2ClientImpl { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final static System.Logger debug = + Utils.getDebugLogger("Http2ClientImpl"::toString, DEBUG); + private final HttpClientImpl client; Http2ClientImpl(HttpClientImpl client) { @@ -59,13 +64,26 @@ class Http2ClientImpl { private final Map<String,Http2Connection> connections = new ConcurrentHashMap<>(); private final Set<String> opening = Collections.synchronizedSet(new HashSet<>()); + private final Map<String,Set<CompletableFuture<Http2Connection>>> waiting = + Collections.synchronizedMap(new HashMap<>()); - boolean haveConnectionFor(URI uri, InetSocketAddress proxy) { - return connections.containsKey(Http2Connection.keyFor(uri,proxy)); + private void addToWaiting(String key, CompletableFuture<Http2Connection> cf) { + synchronized (waiting) { + Set<CompletableFuture<Http2Connection>> waiters = waiting.get(key); + if (waiters == null) { + waiters = new HashSet<>(); + waiting.put(key, waiters); + } + waiters.add(cf); + } } +// boolean haveConnectionFor(URI uri, InetSocketAddress proxy) { +// return connections.containsKey(Http2Connection.keyFor(uri,proxy)); +// } + /** - * If a https request then blocks and waits until a connection is opened. + * If a https request then async waits until a connection is opened. * Returns null if the request is 'http' as a different (upgrade) * mechanism is used. * @@ -78,49 +96,60 @@ class Http2ClientImpl { * In latter case, when the Http2Connection is connected, putConnection() must * be called to store it. */ - Http2Connection getConnectionFor(HttpRequestImpl req) - throws IOException, InterruptedException { + CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req) { URI uri = req.uri(); - InetSocketAddress proxy = req.proxy(client); + InetSocketAddress proxy = req.proxy(); String key = Http2Connection.keyFor(uri, proxy); - Http2Connection connection = connections.get(key); - if (connection != null) { // fast path if connection already exists - return connection; - } - synchronized (opening) { - while ((connection = connections.get(key)) == null) { - if (!req.secure()) { - return null; - } - if (!opening.contains(key)) { - opening.add(key); - break; - } else { - opening.wait(); - } - } - } - if (connection != null) { - return connection; - } - // we are opening the connection here blocking until it is done. - try { - connection = new Http2Connection(req, this); - } catch (Throwable t) { - synchronized (opening) { - opening.remove(key); - opening.notifyAll(); - } - throw t; - } - synchronized (opening) { - connections.put(key, connection); - opening.remove(key); - opening.notifyAll(); - } - return connection; - } + synchronized (opening) { + Http2Connection connection = connections.get(key); + if (connection != null) { // fast path if connection already exists + return CompletableFuture.completedFuture(connection); + } + + if (!req.secure()) { + return MinimalFuture.completedFuture(null); + } + + if (!opening.contains(key)) { + debug.log(Level.DEBUG, "Opening: %s", key); + opening.add(key); + } else { + CompletableFuture<Http2Connection> cf = new MinimalFuture<>(); + addToWaiting(key, cf); + return cf; + } + } + return Http2Connection + .createAsync(req, this) + .whenComplete((conn, t) -> { + debug.log(Level.DEBUG, + "waking up dependents with created connection"); + synchronized (opening) { + Set<CompletableFuture<Http2Connection>> waiters = waiting.remove(key); + debug.log(Level.DEBUG, "Opening completed: %s", key); + opening.remove(key); + if (t == null && conn != null) + putConnection(conn); + final Throwable cause = Utils.getCompletionCause(t); + if (waiters == null) { + debug.log(Level.DEBUG, "no dependent to wake up"); + return; + } else if (cause instanceof Http2Connection.ALPNException) { + waiters.forEach((cf1) -> cf1.completeAsync(() -> null, + client.theExecutor())); + } else if (cause != null) { + debug.log(Level.DEBUG, + () -> "waking up dependants: failed: " + cause); + waiters.forEach((cf1) -> cf1.completeExceptionally(cause)); + } else { + debug.log(Level.DEBUG, "waking up dependants: succeeded"); + waiters.forEach((cf1) -> cf1.completeAsync(() -> conn, + client.theExecutor())); + } + } + }); + } /* * TODO: If there isn't a connection to the same destination, then @@ -134,6 +163,16 @@ class Http2ClientImpl { connections.remove(c.key()); } + void stop() { + debug.log(Level.DEBUG, "stopping"); + connections.values().forEach(this::close); + connections.clear(); + } + + private void close(Http2Connection h2c) { + try { h2c.close(); } catch (Throwable t) {} + } + HttpClientImpl client() { return client; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java index 8e42022c286..e1461d8b5ea 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java @@ -25,27 +25,50 @@ package jdk.incubator.http; +import java.io.EOFException; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.net.URI; -import jdk.incubator.http.HttpConnection.Mode; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.ArrayList; -import java.util.Collections; -import java.util.Formatter; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.stream.Collectors; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Flow; +import java.util.function.Function; +import java.util.function.Supplier; import javax.net.ssl.SSLEngine; -import jdk.incubator.http.internal.common.*; -import jdk.incubator.http.internal.frame.*; +import jdk.incubator.http.HttpConnection.HttpPublisher; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.FlowTube.TubeSubscriber; +import jdk.incubator.http.internal.common.HttpHeadersImpl; +import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.MinimalFuture; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.Utils; +import jdk.incubator.http.internal.frame.ContinuationFrame; +import jdk.incubator.http.internal.frame.DataFrame; +import jdk.incubator.http.internal.frame.ErrorFrame; +import jdk.incubator.http.internal.frame.FramesDecoder; +import jdk.incubator.http.internal.frame.FramesEncoder; +import jdk.incubator.http.internal.frame.GoAwayFrame; +import jdk.incubator.http.internal.frame.HeaderFrame; +import jdk.incubator.http.internal.frame.HeadersFrame; +import jdk.incubator.http.internal.frame.Http2Frame; +import jdk.incubator.http.internal.frame.MalformedFrame; +import jdk.incubator.http.internal.frame.OutgoingHeaders; +import jdk.incubator.http.internal.frame.PingFrame; +import jdk.incubator.http.internal.frame.PushPromiseFrame; +import jdk.incubator.http.internal.frame.ResetFrame; +import jdk.incubator.http.internal.frame.SettingsFrame; +import jdk.incubator.http.internal.frame.WindowUpdateFrame; import jdk.incubator.http.internal.hpack.Encoder; import jdk.incubator.http.internal.hpack.Decoder; import jdk.incubator.http.internal.hpack.DecodingCallback; @@ -83,11 +106,21 @@ import static jdk.incubator.http.internal.frame.SettingsFrame.*; * stream are provided by calling Stream.incoming(). */ class Http2Connection { + + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + static final boolean DEBUG_HPACK = Utils.DEBUG_HPACK; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final static System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("Http2Connection"::toString, DEBUG); + private final System.Logger debugHpack = + Utils.getHpackLogger(this::dbgString, DEBUG_HPACK); + static final ByteBuffer EMPTY_TRIGGER = ByteBuffer.allocate(0); + /* * ByteBuffer pooling strategy for HTTP/2 protocol: * * In general there are 4 points where ByteBuffers are used: - * - incoming/outgoing frames from/to ByteBufers plus incoming/outgoing encrypted data + * - incoming/outgoing frames from/to ByteBuffers plus incoming/outgoing encrypted data * in case of SSL connection. * * 1. Outgoing frames encoded to ByteBuffers. @@ -116,40 +149,49 @@ class Http2Connection { // preface is sent will be buffered. private final class FramesController { volatile boolean prefaceSent; - volatile List<ByteBufferReference> pending; + volatile List<ByteBuffer> pending; - boolean processReceivedData(FramesDecoder decoder, ByteBufferReference buf) + boolean processReceivedData(FramesDecoder decoder, ByteBuffer buf) throws IOException { // if preface is not sent, buffers data in the pending list if (!prefaceSent) { + debug.log(Level.DEBUG, "Preface is not sent: buffering %d", + buf.remaining()); synchronized (this) { if (!prefaceSent) { if (pending == null) pending = new ArrayList<>(); pending.add(buf); + debug.log(Level.DEBUG, () -> "there are now " + + Utils.remaining(pending) + + " bytes buffered waiting for preface to be sent"); return false; } } } // Preface is sent. Checks for pending data and flush it. - // We rely on this method being called from within the readlock, - // so we know that no other thread could execute this method + // We rely on this method being called from within the Http2TubeSubscriber + // scheduler, so we know that no other thread could execute this method // concurrently while we're here. // This ensures that later incoming buffers will not // be processed before we have flushed the pending queue. // No additional synchronization is therefore necessary here. - List<ByteBufferReference> pending = this.pending; + List<ByteBuffer> pending = this.pending; this.pending = null; if (pending != null) { // flush pending data - for (ByteBufferReference b : pending) { + debug.log(Level.DEBUG, () -> "Processing buffered data: " + + Utils.remaining(pending)); + for (ByteBuffer b : pending) { decoder.decode(b); } } - // push the received buffer to the frames decoder. - decoder.decode(buf); + if (buf != EMPTY_TRIGGER) { + debug.log(Level.DEBUG, "Processing %d", buf.remaining()); + decoder.decode(buf); + } return true; } @@ -167,7 +209,6 @@ class Http2Connection { //------------------------------------- final HttpConnection connection; - private final HttpClientImpl client; private final Http2ClientImpl client2; private final Map<Integer,Stream<?>> streams = new ConcurrentHashMap<>(); private int nextstreamid; @@ -186,7 +227,10 @@ class Http2Connection { */ private final WindowController windowController = new WindowController(); private final FramesController framesController = new FramesController(); + private final Http2TubeSubscriber subscriber = new Http2TubeSubscriber(); final WindowUpdateSender windowUpdater; + private volatile Throwable cause; + private volatile Supplier<ByteBuffer> initial; static final int DEFAULT_FRAME_SIZE = 16 * 1024; @@ -199,7 +243,6 @@ class Http2Connection { int nextstreamid, String key) { this.connection = connection; - this.client = client2.client(); this.client2 = client2; this.nextstreamid = nextstreamid; this.key = key; @@ -209,102 +252,147 @@ class Http2Connection { this.serverSettings = SettingsFrame.getDefaultSettings(); this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE)); this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); - this.windowUpdater = new ConnectionWindowUpdateSender(this, client.getReceiveBufferSize()); + debugHpack.log(Level.DEBUG, () -> "For the record:" + super.toString()); + debugHpack.log(Level.DEBUG, "Decoder created: %s", hpackIn); + debugHpack.log(Level.DEBUG, "Encoder created: %s", hpackOut); + this.windowUpdater = new ConnectionWindowUpdateSender(this, client().getReceiveBufferSize()); } /** * Case 1) Create from upgraded HTTP/1.1 connection. - * Is ready to use. Will not be SSL. exchange is the Exchange + * Is ready to use. Can be SSL. exchange is the Exchange * that initiated the connection, whose response will be delivered * on a Stream. */ - Http2Connection(HttpConnection connection, + private Http2Connection(HttpConnection connection, Http2ClientImpl client2, Exchange<?> exchange, - ByteBuffer initial) + Supplier<ByteBuffer> initial) throws IOException, InterruptedException { this(connection, client2, 3, // stream 1 is registered during the upgrade keyFor(connection)); - assert !(connection instanceof SSLConnection); Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); Stream<?> initialStream = createStream(exchange); initialStream.registerStream(1); windowController.registerStream(1, getInitialSendWindowSize()); initialStream.requestSent(); + // Upgrading: + // set callbacks before sending preface - makes sure anything that + // might be sent by the server will come our way. + this.initial = initial; + connectFlows(connection); sendConnectionPreface(); - // start reading and writing - // start reading - AsyncConnection asyncConn = (AsyncConnection)connection; - asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown, this::getReadBuffer); - connection.configureMode(Mode.ASYNC); // set mode only AFTER setAsyncCallbacks to provide visibility. - asyncReceive(ByteBufferReference.of(initial)); - asyncConn.startReading(); } - // async style but completes immediately + // Used when upgrading an HTTP/1.1 connection to HTTP/2 after receiving + // agreement from the server. Async style but completes immediately, because + // the connection is already connected. static CompletableFuture<Http2Connection> createAsync(HttpConnection connection, Http2ClientImpl client2, Exchange<?> exchange, - ByteBuffer initial) { + Supplier<ByteBuffer> initial) + { return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial)); } + // Requires TLS handshake. So, is really async + static CompletableFuture<Http2Connection> createAsync(HttpRequestImpl request, + Http2ClientImpl h2client) { + assert request.secure(); + AbstractAsyncSSLConnection connection = (AbstractAsyncSSLConnection) + HttpConnection.getConnection(request.getAddress(), + h2client.client(), + request, + HttpClient.Version.HTTP_2); + + return connection.connectAsync() + .thenCompose(unused -> checkSSLConfig(connection)) + .thenCompose(notused-> { + CompletableFuture<Http2Connection> cf = new MinimalFuture<>(); + try { + Http2Connection hc = new Http2Connection(request, h2client, connection); + cf.complete(hc); + } catch (IOException e) { + cf.completeExceptionally(e); + } + return cf; } ); + } + /** * Cases 2) 3) * * request is request to be sent. */ - Http2Connection(HttpRequestImpl request, Http2ClientImpl h2client) - throws IOException, InterruptedException + private Http2Connection(HttpRequestImpl request, + Http2ClientImpl h2client, + HttpConnection connection) + throws IOException { - this(HttpConnection.getConnection(request.getAddress(h2client.client()), h2client.client(), request, true), - h2client, - 1, - keyFor(request.uri(), request.proxy(h2client.client()))); + this(connection, + h2client, + 1, + keyFor(request.uri(), request.proxy())); + Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize()); - // start reading - AsyncConnection asyncConn = (AsyncConnection)connection; - asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown, this::getReadBuffer); - connection.connect(); - checkSSLConfig(); // safe to resume async reading now. - asyncConn.enableCallback(); + connectFlows(connection); sendConnectionPreface(); } + private void connectFlows(HttpConnection connection) { + FlowTube tube = connection.getConnectionFlow(); + // Connect the flow to our Http2TubeSubscriber: + tube.connectFlows(connection.publisher(), subscriber); + } + + final HttpClientImpl client() { + return client2.client(); + } + /** * Throws an IOException if h2 was not negotiated */ - private void checkSSLConfig() throws IOException { - AbstractAsyncSSLConnection aconn = (AbstractAsyncSSLConnection)connection; - SSLEngine engine = aconn.getEngine(); - String alpn = engine.getApplicationProtocol(); - if (alpn == null || !alpn.equals("h2")) { - String msg; - if (alpn == null) { - Log.logSSL("ALPN not supported"); - msg = "ALPN not supported"; - } else switch (alpn) { - case "": - Log.logSSL("No ALPN returned"); - msg = "No ALPN negotiated"; - break; - case "http/1.1": - Log.logSSL("HTTP/1.1 ALPN returned"); - msg = "HTTP/1.1 ALPN returned"; - break; - default: - Log.logSSL("unknown ALPN returned"); - msg = "Unexpected ALPN: " + alpn; - throw new IOException(msg); + private static CompletableFuture<?> checkSSLConfig(AbstractAsyncSSLConnection aconn) { + assert aconn.isSecure(); + + Function<String, CompletableFuture<Void>> checkAlpnCF = (alpn) -> { + CompletableFuture<Void> cf = new MinimalFuture<>(); + SSLEngine engine = aconn.getEngine(); + assert Objects.equals(alpn, engine.getApplicationProtocol()); + + DEBUG_LOGGER.log(Level.DEBUG, "checkSSLConfig: alpn: %s", alpn ); + + if (alpn == null || !alpn.equals("h2")) { + String msg; + if (alpn == null) { + Log.logSSL("ALPN not supported"); + msg = "ALPN not supported"; + } else { + switch (alpn) { + case "": + Log.logSSL(msg = "No ALPN negotiated"); + break; + case "http/1.1": + Log.logSSL( msg = "HTTP/1.1 ALPN returned"); + break; + default: + Log.logSSL(msg = "Unexpected ALPN: " + alpn); + cf.completeExceptionally(new IOException(msg)); + } + } + cf.completeExceptionally(new ALPNException(msg, aconn)); + return cf; } - throw new ALPNException(msg, aconn); - } + cf.complete(null); + return cf; + }; + + return aconn.getALPN().thenCompose(checkAlpnCF); } static String keyFor(HttpConnection connection) { @@ -322,7 +410,7 @@ class Http2Connection { String host; int port; - if (isProxy) { + if (proxy != null) { host = proxy.getHostString(); port = proxy.getPort(); } else { @@ -350,47 +438,26 @@ class Http2Connection { client2.putConnection(this); } - private static String toHexdump1(ByteBuffer bb) { - bb.mark(); - StringBuilder sb = new StringBuilder(512); - Formatter f = new Formatter(sb); - - while (bb.hasRemaining()) { - int i = Byte.toUnsignedInt(bb.get()); - f.format("%02x:", i); - } - sb.deleteCharAt(sb.length()-1); - bb.reset(); - return sb.toString(); + private HttpPublisher publisher() { + return connection.publisher(); } - private static String toHexdump(ByteBuffer bb) { - List<String> words = new ArrayList<>(); - int i = 0; - bb.mark(); - while (bb.hasRemaining()) { - if (i % 2 == 0) { - words.add(""); - } - byte b = bb.get(); - String hex = Integer.toHexString(256 + Byte.toUnsignedInt(b)).substring(1); - words.set(i / 2, words.get(i / 2) + hex); - i++; - } - bb.reset(); - return words.stream().collect(Collectors.joining(" ")); - } + private void decodeHeaders(HeaderFrame frame, DecodingCallback decoder) + throws IOException + { + debugHpack.log(Level.DEBUG, "decodeHeaders(%s)", decoder); - private void decodeHeaders(HeaderFrame frame, DecodingCallback decoder) { boolean endOfHeaders = frame.getFlag(HeaderFrame.END_HEADERS); - ByteBufferReference[] buffers = frame.getHeaderBlock(); - for (int i = 0; i < buffers.length; i++) { - hpackIn.decode(buffers[i].get(), endOfHeaders && (i == buffers.length - 1), decoder); + List<ByteBuffer> buffers = frame.getHeaderBlock(); + int len = buffers.size(); + for (int i = 0; i < len; i++) { + ByteBuffer b = buffers.get(i); + hpackIn.decode(b, endOfHeaders && (i == len - 1), decoder); } } - int getInitialSendWindowSize() { + final int getInitialSendWindowSize() { return serverSettings.getParameter(INITIAL_WINDOW_SIZE); } @@ -400,16 +467,8 @@ class Http2Connection { sendFrame(f); } - private ByteBufferPool readBufferPool = new ByteBufferPool(); - - // provides buffer to read data (default size) - public ByteBufferReference getReadBuffer() { - return readBufferPool.get(getMaxReceiveFrameSize() + Http2Frame.FRAME_HEADER_SIZE); - } - - private final Object readlock = new Object(); - - public void asyncReceive(ByteBufferReference buffer) { + long count; + final void asyncReceive(ByteBuffer buffer) { // We don't need to read anything and // we don't want to send anything back to the server // until the connection preface has been sent. @@ -419,23 +478,61 @@ class Http2Connection { // SettingsFrame sent by the server) before the connection // preface is fully sent might result in the server // sending a GOAWAY frame with 'invalid_preface'. - synchronized (readlock) { - try { - // the readlock ensures that the order of incoming buffers - // is preserved. - framesController.processReceivedData(framesDecoder, buffer); - } catch (Throwable e) { - String msg = Utils.stackTrace(e); - Log.logTrace(msg); - shutdown(e); + // + // Note: asyncReceive is only called from the Http2TubeSubscriber + // sequential scheduler. + try { + Supplier<ByteBuffer> bs = initial; + // ensure that we always handle the initial buffer first, + // if any. + if (bs != null) { + initial = null; + ByteBuffer b = bs.get(); + if (b.hasRemaining()) { + long c = ++count; + debug.log(Level.DEBUG, () -> "H2 Receiving Initial(" + + c +"): " + b.remaining()); + framesController.processReceivedData(framesDecoder, b); + } } + ByteBuffer b = buffer; + // the Http2TubeSubscriber scheduler ensures that the order of incoming + // buffers is preserved. + if (b == EMPTY_TRIGGER) { + debug.log(Level.DEBUG, "H2 Received EMPTY_TRIGGER"); + boolean prefaceSent = framesController.prefaceSent; + assert prefaceSent; + // call framesController.processReceivedData to potentially + // trigger the processing of all the data buffered there. + framesController.processReceivedData(framesDecoder, buffer); + debug.log(Level.DEBUG, "H2 processed buffered data"); + } else { + long c = ++count; + debug.log(Level.DEBUG, "H2 Receiving(%d): %d", c, b.remaining()); + framesController.processReceivedData(framesDecoder, buffer); + debug.log(Level.DEBUG, "H2 processed(%d)", c); + } + } catch (Throwable e) { + String msg = Utils.stackTrace(e); + Log.logTrace(msg); + shutdown(e); } } + Throwable getRecordedCause() { + return cause; + } void shutdown(Throwable t) { + debug.log(Level.DEBUG, () -> "Shutting down h2c (closed="+closed+"): " + t); + if (closed == true) return; + synchronized (this) { + if (closed == true) return; + closed = true; + } Log.logError(t); - closed = true; + Throwable initialCause = this.cause; + if (initialCause == null) this.cause = t; client2.deleteConnection(this); List<Stream<?>> c = new LinkedList<>(streams.values()); for (Stream<?> s : c) { @@ -457,8 +554,12 @@ class Http2Connection { if (frame instanceof MalformedFrame) { Log.logError(((MalformedFrame) frame).getMessage()); if (streamid == 0) { - protocolError(((MalformedFrame) frame).getErrorCode()); + framesDecoder.close("Malformed frame on stream 0"); + protocolError(((MalformedFrame) frame).getErrorCode(), + ((MalformedFrame) frame).getMessage()); } else { + debug.log(Level.DEBUG, () -> "Reset stream: " + + ((MalformedFrame) frame).getMessage()); resetStream(streamid, ((MalformedFrame) frame).getErrorCode()); } return; @@ -468,6 +569,8 @@ class Http2Connection { } else { if (frame instanceof SettingsFrame) { // The stream identifier for a SETTINGS frame MUST be zero + framesDecoder.close( + "The stream identifier for a SETTINGS frame MUST be zero"); protocolError(GoAwayFrame.PROTOCOL_ERROR); return; } @@ -476,9 +579,16 @@ class Http2Connection { if (stream == null) { // Should never receive a frame with unknown stream id - // To avoid looping, an endpoint MUST NOT send a RST_STREAM in - // response to a RST_STREAM frame. - if (!(frame instanceof ResetFrame)) { + if (frame instanceof HeaderFrame) { + // always decode the headers as they may affect + // connection-level HPACK decoding state + HeaderDecoder decoder = new LoggingHeaderDecoder(new HeaderDecoder()); + decodeHeaders((HeaderFrame) frame, decoder); + } + + int sid = frame.streamid(); + if (sid >= nextstreamid && !(frame instanceof ResetFrame)) { + // otherwise the stream has already been reset/closed resetStream(streamid, ResetFrame.PROTOCOL_ERROR); } return; @@ -499,6 +609,11 @@ class Http2Connection { private <T> void handlePushPromise(Stream<T> parent, PushPromiseFrame pp) throws IOException { + // always decode the headers as they may affect connection-level HPACK + // decoding state + HeaderDecoder decoder = new LoggingHeaderDecoder(new HeaderDecoder()); + decodeHeaders(pp, decoder); + HttpRequestImpl parentReq = parent.request; int promisedStreamid = pp.getPromisedStream(); if (promisedStreamid != nextPushStream) { @@ -507,8 +622,7 @@ class Http2Connection { } else { nextPushStream += 2; } - HeaderDecoder decoder = new HeaderDecoder(); - decodeHeaders(pp, decoder); + HttpHeadersImpl headers = decoder.headers(); HttpRequestImpl pushReq = HttpRequestImpl.createPushRequest(parentReq, headers); Exchange<T> pushExch = new Exchange<>(pushReq, parent.exchange.multi); @@ -549,7 +663,15 @@ class Http2Connection { } void closeStream(int streamid) { + debug.log(Level.DEBUG, "Closed stream %d", streamid); Stream<?> s = streams.remove(streamid); + if (s != null) { + // decrement the reference count on the HttpClientImpl + // to allow the SelectorManager thread to exit if no + // other operation is pending and the facade is no + // longer referenced. + client().unreference(); + } // ## Remove s != null. It is a hack for delayed cancellation,reset if (s != null && !(s instanceof Stream.PushedStream)) { // Since PushStreams have no request body, then they have no @@ -578,10 +700,16 @@ class Http2Connection { private void protocolError(int errorCode) throws IOException + { + protocolError(errorCode, null); + } + + private void protocolError(int errorCode, String msg) + throws IOException { GoAwayFrame frame = new GoAwayFrame(0, errorCode); sendFrame(frame); - shutdown(new IOException("protocol error")); + shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg)))); } private void handleSettings(SettingsFrame frame) @@ -633,11 +761,6 @@ class Http2Connection { return clientSettings.getParameter(MAX_FRAME_SIZE); } - // Not sure how useful this is. - public int getMaxHeadersSize() { - return serverSettings.getParameter(MAX_HEADER_LIST_SIZE); - } - private static final String CLIENT_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; private static final byte[] PREFACE_BYTES = @@ -652,10 +775,12 @@ class Http2Connection { connection.channel().getLocalAddress(), connection.address()); SettingsFrame sf = client2.getClientSettings(); - ByteBufferReference ref = framesEncoder.encodeConnectionPreface(PREFACE_BYTES, sf); + ByteBuffer buf = framesEncoder.encodeConnectionPreface(PREFACE_BYTES, sf); Log.logFrames(sf, "OUT"); // send preface bytes and SettingsFrame together - connection.write(ref.get()); + HttpPublisher publisher = publisher(); + publisher.enqueue(List.of(buf)); + publisher.signalEnqueued(); // mark preface sent. framesController.markPrefaceSent(); Log.logTrace("PREFACE_BYTES sent"); @@ -669,6 +794,9 @@ class Http2Connection { // cause any pending data stored before the preface was sent to be // flushed (see PrefaceController). Log.logTrace("finished sending connection preface"); + debug.log(Level.DEBUG, "Triggering processing of buffered data" + + " after sending connection preface"); + subscriber.onNext(List.of(EMPTY_TRIGGER)); } /** @@ -682,38 +810,37 @@ class Http2Connection { /** * Creates Stream with given id. */ - <T> Stream<T> createStream(Exchange<T> exchange) { - Stream<T> stream = new Stream<>(client, this, exchange, windowController); + final <T> Stream<T> createStream(Exchange<T> exchange) { + Stream<T> stream = new Stream<>(this, exchange, windowController); return stream; } <T> Stream.PushedStream<?,T> createPushStream(Stream<T> parent, Exchange<T> pushEx) { PushGroup<?,T> pg = parent.exchange.getPushGroup(); - return new Stream.PushedStream<>(pg, client, this, parent, pushEx); + return new Stream.PushedStream<>(pg, this, pushEx); } <T> void putStream(Stream<T> stream, int streamid) { + // increment the reference count on the HttpClientImpl + // to prevent the SelectorManager thread from exiting until + // the stream is closed. + client().reference(); streams.put(streamid, stream); } - void deleteStream(int streamid) { - streams.remove(streamid); - windowController.removeStream(streamid); - } - /** * Encode the headers into a List<ByteBuffer> and then create HEADERS * and CONTINUATION frames from the list and return the List<Http2Frame>. */ private List<HeaderFrame> encodeHeaders(OutgoingHeaders<Stream<?>> frame) { - List<ByteBufferReference> buffers = encodeHeadersImpl( + List<ByteBuffer> buffers = encodeHeadersImpl( getMaxSendFrameSize(), frame.getAttachment().getRequestPseudoHeaders(), frame.getUserHeaders(), frame.getSystemHeaders()); List<HeaderFrame> frames = new ArrayList<>(buffers.size()); - Iterator<ByteBufferReference> bufIterator = buffers.iterator(); + Iterator<ByteBuffer> bufIterator = buffers.iterator(); HeaderFrame oframe = new HeadersFrame(frame.streamid(), frame.getFlags(), bufIterator.next()); frames.add(oframe); while(bufIterator.hasNext()) { @@ -728,12 +855,12 @@ class Http2Connection { // There can be no concurrent access to this buffer as all access to this buffer // and its content happen within a single critical code block section protected // by the sendLock. / (see sendFrame()) - private ByteBufferPool headerEncodingPool = new ByteBufferPool(); + // private final ByteBufferPool headerEncodingPool = new ByteBufferPool(); - private ByteBufferReference getHeaderBuffer(int maxFrameSize) { - ByteBufferReference ref = headerEncodingPool.get(maxFrameSize); - ref.get().limit(maxFrameSize); - return ref; + private ByteBuffer getHeaderBuffer(int maxFrameSize) { + ByteBuffer buf = ByteBuffer.allocate(maxFrameSize); + buf.limit(maxFrameSize); + return buf; } /* @@ -747,29 +874,29 @@ class Http2Connection { * header field names MUST be converted to lowercase prior to their * encoding in HTTP/2... */ - private List<ByteBufferReference> encodeHeadersImpl(int maxFrameSize, HttpHeaders... headers) { - ByteBufferReference buffer = getHeaderBuffer(maxFrameSize); - List<ByteBufferReference> buffers = new ArrayList<>(); + private List<ByteBuffer> encodeHeadersImpl(int maxFrameSize, HttpHeaders... headers) { + ByteBuffer buffer = getHeaderBuffer(maxFrameSize); + List<ByteBuffer> buffers = new ArrayList<>(); for(HttpHeaders header : headers) { for (Map.Entry<String, List<String>> e : header.map().entrySet()) { String lKey = e.getKey().toLowerCase(); List<String> values = e.getValue(); for (String value : values) { hpackOut.header(lKey, value); - while (!hpackOut.encode(buffer.get())) { - buffer.get().flip(); + while (!hpackOut.encode(buffer)) { + buffer.flip(); buffers.add(buffer); buffer = getHeaderBuffer(maxFrameSize); } } } } - buffer.get().flip(); + buffer.flip(); buffers.add(buffer); return buffers; } - private ByteBufferReference[] encodeHeaders(OutgoingHeaders<Stream<?>> oh, Stream<?> stream) { + private List<ByteBuffer> encodeHeaders(OutgoingHeaders<Stream<?>> oh, Stream<?> stream) { oh.streamid(stream.streamid); if (Log.headers()) { StringBuilder sb = new StringBuilder("HEADERS FRAME (stream="); @@ -783,26 +910,13 @@ class Http2Connection { return encodeFrames(frames); } - private ByteBufferReference[] encodeFrames(List<HeaderFrame> frames) { + private List<ByteBuffer> encodeFrames(List<HeaderFrame> frames) { if (Log.frames()) { frames.forEach(f -> Log.logFrames(f, "OUT")); } return framesEncoder.encodeFrames(frames); } - static Throwable getExceptionFrom(CompletableFuture<?> cf) { - try { - cf.get(); - return null; - } catch (Throwable e) { - if (e.getCause() != null) { - return e.getCause(); - } else { - return e; - } - } - } - private Stream<?> registerNewStream(OutgoingHeaders<Stream<?>> oh) { Stream<?> stream = oh.getAttachment(); int streamid = nextstreamid; @@ -818,18 +932,19 @@ class Http2Connection { void sendFrame(Http2Frame frame) { try { + HttpPublisher publisher = publisher(); synchronized (sendlock) { if (frame instanceof OutgoingHeaders) { @SuppressWarnings("unchecked") OutgoingHeaders<Stream<?>> oh = (OutgoingHeaders<Stream<?>>) frame; Stream<?> stream = registerNewStream(oh); // provide protection from inserting unordered frames between Headers and Continuation - connection.writeAsync(encodeHeaders(oh, stream)); + publisher.enqueue(encodeHeaders(oh, stream)); } else { - connection.writeAsync(encodeFrame(frame)); + publisher.enqueue(encodeFrame(frame)); } } - connection.flushAsync(); + publisher.signalEnqueued(); } catch (IOException e) { if (!closed) { Log.logError(e); @@ -838,15 +953,16 @@ class Http2Connection { } } - private ByteBufferReference[] encodeFrame(Http2Frame frame) { + private List<ByteBuffer> encodeFrame(Http2Frame frame) { Log.logFrames(frame, "OUT"); return framesEncoder.encodeFrame(frame); } void sendDataFrame(DataFrame frame) { try { - connection.writeAsync(encodeFrame(frame)); - connection.flushAsync(); + HttpPublisher publisher = publisher(); + publisher.enqueue(encodeFrame(frame)); + publisher.signalEnqueued(); } catch (IOException e) { if (!closed) { Log.logError(e); @@ -862,8 +978,9 @@ class Http2Connection { */ void sendUnorderedFrame(Http2Frame frame) { try { - connection.writeAsyncUnordered(encodeFrame(frame)); - connection.flushAsync(); + HttpPublisher publisher = publisher(); + publisher.enqueueUnordered(encodeFrame(frame)); + publisher.signalEnqueued(); } catch (IOException e) { if (!closed) { Log.logError(e); @@ -872,6 +989,200 @@ class Http2Connection { } } + /** + * A simple tube subscriber for reading from the connection flow. + */ + final class Http2TubeSubscriber implements TubeSubscriber { + volatile Flow.Subscription subscription; + volatile boolean completed; + volatile boolean dropped; + volatile Throwable error; + final ConcurrentLinkedQueue<ByteBuffer> queue + = new ConcurrentLinkedQueue<>(); + final SequentialScheduler scheduler = + SequentialScheduler.synchronizedScheduler(this::processQueue); + + final void processQueue() { + try { + while (!queue.isEmpty() && !scheduler.isStopped()) { + ByteBuffer buffer = queue.poll(); + debug.log(Level.DEBUG, + "sending %d to Http2Connection.asyncReceive", + buffer.remaining()); + asyncReceive(buffer); + } + } catch (Throwable t) { + Throwable x = error; + if (x == null) error = t; + } finally { + Throwable x = error; + if (x != null) { + debug.log(Level.DEBUG, "Stopping scheduler", x); + scheduler.stop(); + Http2Connection.this.shutdown(x); + } + } + } + + + public void onSubscribe(Flow.Subscription subscription) { + // supports being called multiple time. + // doesn't cancel the previous subscription, since that is + // most probably the same as the new subscription. + assert this.subscription == null || dropped == false; + this.subscription = subscription; + dropped = false; + // TODO FIXME: request(1) should be done by the delegate. + if (!completed) { + debug.log(Level.DEBUG, "onSubscribe: requesting Long.MAX_VALUE for reading"); + subscription.request(Long.MAX_VALUE); + } else { + debug.log(Level.DEBUG, "onSubscribe: already completed"); + } + } + + @Override + public void onNext(List<ByteBuffer> item) { + debug.log(Level.DEBUG, () -> "onNext: got " + Utils.remaining(item) + + " bytes in " + item.size() + " buffers"); + queue.addAll(item); + scheduler.deferOrSchedule(client().theExecutor()); + } + + @Override + public void onError(Throwable throwable) { + debug.log(Level.DEBUG, () -> "onError: " + throwable); + error = throwable; + completed = true; + scheduler.deferOrSchedule(client().theExecutor()); + } + + @Override + public void onComplete() { + debug.log(Level.DEBUG, "EOF"); + error = new EOFException("EOF reached while reading"); + completed = true; + scheduler.deferOrSchedule(client().theExecutor()); + } + + public void dropSubscription() { + debug.log(Level.DEBUG, "dropSubscription"); + // we could probably set subscription to null here... + // then we might not need the 'dropped' boolean? + dropped = true; + } + } + + @Override + public final String toString() { + return dbgString(); + } + + final String dbgString() { + return "Http2Connection(" + + connection.getConnectionFlow() + ")"; + } + + final class LoggingHeaderDecoder extends HeaderDecoder { + + private final HeaderDecoder delegate; + private final System.Logger debugHpack = + Utils.getHpackLogger(this::dbgString, DEBUG_HPACK); + + LoggingHeaderDecoder(HeaderDecoder delegate) { + this.delegate = delegate; + } + + String dbgString() { + return Http2Connection.this.dbgString() + "/LoggingHeaderDecoder"; + } + + @Override + public void onDecoded(CharSequence name, CharSequence value) { + delegate.onDecoded(name, value); + } + + @Override + public void onIndexed(int index, + CharSequence name, + CharSequence value) { + debugHpack.log(Level.DEBUG, "onIndexed(%s, %s, %s)%n", + index, name, value); + delegate.onIndexed(index, name, value); + } + + @Override + public void onLiteral(int index, + CharSequence name, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteral(%s, %s, %s, %s)%n", + index, name, value, valueHuffman); + delegate.onLiteral(index, name, value, valueHuffman); + } + + @Override + public void onLiteral(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteral(%s, %s, %s, %s)%n", + name, nameHuffman, value, valueHuffman); + delegate.onLiteral(name, nameHuffman, value, valueHuffman); + } + + @Override + public void onLiteralNeverIndexed(int index, + CharSequence name, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteralNeverIndexed(%s, %s, %s, %s)%n", + index, name, value, valueHuffman); + delegate.onLiteralNeverIndexed(index, name, value, valueHuffman); + } + + @Override + public void onLiteralNeverIndexed(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteralNeverIndexed(%s, %s, %s, %s)%n", + name, nameHuffman, value, valueHuffman); + delegate.onLiteralNeverIndexed(name, nameHuffman, value, valueHuffman); + } + + @Override + public void onLiteralWithIndexing(int index, + CharSequence name, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteralWithIndexing(%s, %s, %s, %s)%n", + index, name, value, valueHuffman); + delegate.onLiteralWithIndexing(index, name, value, valueHuffman); + } + + @Override + public void onLiteralWithIndexing(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { + debugHpack.log(Level.DEBUG, "onLiteralWithIndexing(%s, %s, %s, %s)%n", + name, nameHuffman, value, valueHuffman); + delegate.onLiteralWithIndexing(name, nameHuffman, value, valueHuffman); + } + + @Override + public void onSizeUpdate(int capacity) { + debugHpack.log(Level.DEBUG, "onSizeUpdate(%s)%n", capacity); + delegate.onSizeUpdate(capacity); + } + + @Override + HttpHeadersImpl headers() { + return delegate.headers(); + } + } + static class HeaderDecoder implements DecodingCallback { HttpHeadersImpl headers; diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java index 89e138e69b7..d537c805d89 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -27,13 +27,16 @@ package jdk.incubator.http; import java.io.IOException; import java.net.Authenticator; -import java.net.CookieManager; +import java.net.CookieHandler; import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.ProxySelector; import java.net.URI; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; @@ -60,10 +63,25 @@ public abstract class HttpClient { /** * Returns a new HttpClient with default settings. * + * <p> Equivalent to {@code newBuilder().build()}. + * + * <p> The default settings include: the "GET" request method, a preference + * of {@linkplain HttpClient.Version#HTTP_2 HTTP/2}, a redirection policy of + * {@linkplain Redirect#NEVER NEVER}, the {@linkplain + * ProxySelector#getDefault() default proxy selector}, and the {@linkplain + * SSLContext#getDefault() default SSL context}. + * + * @implNote The system-wide default values are retrieved at the time the + * {@code HttpClient} instance is constructed. Changing the system-wide + * values after an {@code HttpClient} instance has been built, for + * instance, by calling {@link ProxySelector#setDefault(ProxySelector)} + * or {@link SSLContext#setDefault(SSLContext)}, has no effect on already + * built instances. + * * @return a new HttpClient */ public static HttpClient newHttpClient() { - return new HttpClientBuilderImpl().build(); + return newBuilder().build(); } /** @@ -76,55 +94,65 @@ public abstract class HttpClient { } /** - * A builder of immutable {@link HttpClient}s. {@code HttpClient.Builder}s - * are created by calling {@link HttpClient#newBuilder()}. + * A builder of immutable {@link HttpClient}s. * {@Incubating} * - * <p> Each of the setter methods in this class modifies the state of the - * builder and returns <i>this</i> (ie. the same instance). The methods are - * not synchronized and should not be called from multiple threads without - * external synchronization. - * - * <p> {@link #build()} returns a new {@code HttpClient} each time it is - * called. + * <p> Builders are created by invoking {@linkplain HttpClient#newBuilder() + * newBuilder}. Each of the setter methods modifies the state of the builder + * and returns the same instance. Builders are not thread-safe and should not be + * used concurrently from multiple threads without external synchronization. * * @since 9 */ public abstract static class Builder { + /** + * A proxy selector that always return {@link Proxy#NO_PROXY} implying + * a direct connection. + * This is a convenience object that can be passed to {@link #proxy(ProxySelector)} + * in order to build an instance of {@link HttpClient} that uses no + * proxy. + */ + public static final ProxySelector NO_PROXY = ProxySelector.of(null); + + /** + * Creates a Builder. + */ protected Builder() {} /** - * Sets a cookie manager. + * Sets a cookie handler. * - * @param cookieManager the cookie manager + * @param cookieHandler the cookie handler * @return this builder */ - public abstract Builder cookieManager(CookieManager cookieManager); + public abstract Builder cookieHandler(CookieHandler cookieHandler); /** - * Sets an {@code SSLContext}. If a security manager is set, then the caller - * must have the {@link java.net.NetPermission NetPermission} - * ({@code "setSSLContext"}) + * Sets an {@code SSLContext}. * - * <p> The effect of not calling this method, is that a default {@link - * javax.net.ssl.SSLContext} is used, which is normally adequate for - * client applications that do not need to specify protocols, or require - * client authentication. + * <p> If this method is not invoked prior to {@linkplain #build() + * building}, then newly built clients will use the {@linkplain + * SSLContext#getDefault() default context}, which is normally adequate + * for client applications that do not need to specify protocols, or + * require client authentication. * * @param sslContext the SSLContext * @return this builder - * @throws SecurityException if a security manager is set and the - * caller does not have any required permission */ public abstract Builder sslContext(SSLContext sslContext); /** - * Sets an {@code SSLParameters}. If this method is not called, then a default - * set of parameters are used. The contents of the given object are - * copied. Some parameters which are used internally by the HTTP protocol - * implementation (such as application protocol list) should not be set - * by callers, as they are ignored. + * Sets an {@code SSLParameters}. + * + * <p> If this method is not invoked prior to {@linkplain #build() + * building}, then newly built clients will use a default, + * implementation specific, set of parameters. + * + * <p> Some parameters which are used internally by the HTTP Client + * implementation (such as the application protocol list) should not be + * set by callers, as they may be ignored. The contents of the given + * object are copied. * * @param sslParameters the SSLParameters * @return this builder @@ -132,10 +160,17 @@ public abstract class HttpClient { public abstract Builder sslParameters(SSLParameters sslParameters); /** - * Sets the executor to be used for asynchronous tasks. If this method is - * not called, a default executor is set, which is the one returned from {@link - * java.util.concurrent.Executors#newCachedThreadPool() - * Executors.newCachedThreadPool}. + * Sets the executor to be used for asynchronous and dependent tasks. + * + * <p> If this method is not invoked prior to {@linkplain #build() + * building}, a default executor is created for each newly built {@code + * HttpClient}. The default executor uses a {@linkplain + * Executors#newCachedThreadPool(ThreadFactory) cached thread pool}, + * with a custom thread factory. + * + * @implNote If a security manager has been installed, the thread + * factory creates threads that run with an access control context that + * has no permissions. * * @param executor the Executor * @return this builder @@ -144,8 +179,11 @@ public abstract class HttpClient { /** * Specifies whether requests will automatically follow redirects issued - * by the server. This setting can be overridden on each request. The - * default value for this setting is {@link Redirect#NEVER NEVER} + * by the server. + * + * <p> If this method is not invoked prior to {@linkplain #build() + * building}, then newly built clients will use a default redirection + * policy of {@link Redirect#NEVER NEVER}. * * @param policy the redirection policy * @return this builder @@ -153,10 +191,14 @@ public abstract class HttpClient { public abstract Builder followRedirects(Redirect policy); /** - * Requests a specific HTTP protocol version where possible. If not set, - * the version defaults to {@link HttpClient.Version#HTTP_2}. If - * {@link HttpClient.Version#HTTP_2} is set, then each request will - * attempt to upgrade to HTTP/2. If the upgrade succeeds, then the + * Requests a specific HTTP protocol version where possible. + * + * <p> If this method is not invoked prior to {@linkplain #build() + * building}, then newly built clients will prefer {@linkplain + * Version#HTTP_2 HTTP/2}. + * + * <p> If set to {@linkplain Version#HTTP_2 HTTP/2}, then each request + * will attempt to upgrade to HTTP/2. If the upgrade succeeds, then the * response to this request will use HTTP/2 and all subsequent requests * and responses to the same * <a href="https://tools.ietf.org/html/rfc6454#section-4">origin server</a> @@ -180,12 +222,22 @@ public abstract class HttpClient { public abstract Builder priority(int priority); /** - * Sets a {@link java.net.ProxySelector} for this client. If no selector - * is set, then no proxies are used. If a {@code null} parameter is - * given then the system wide default proxy selector is used. + * Sets a {@link java.net.ProxySelector}. * - * @implNote {@link java.net.ProxySelector#of(InetSocketAddress)} - * provides a {@code ProxySelector} which uses one proxy for all requests. + * @apiNote {@link ProxySelector#of(InetSocketAddress)} + * provides a {@code ProxySelector} which uses a single proxy for all + * requests. The system-wide proxy selector can be retrieved by + * {@link ProxySelector#getDefault()}. + * + * @implNote + * If this method is not invoked prior to {@linkplain #build() + * building}, then newly built clients will use the {@linkplain + * ProxySelector#getDefault() default proxy selector}, which + * is normally adequate for client applications. This default + * behavior can be turned off by supplying an explicit proxy + * selector to this method, such as {@link #NO_PROXY} or one + * returned by {@link ProxySelector#of(InetSocketAddress)}, + * before calling {@link #build()}. * * @param selector the ProxySelector * @return this builder @@ -201,7 +253,7 @@ public abstract class HttpClient { public abstract Builder authenticator(Authenticator a); /** - * Returns a {@link HttpClient} built from the current state of this + * Returns a new {@link HttpClient} built from the current state of this * builder. * * @return this builder @@ -211,50 +263,58 @@ public abstract class HttpClient { /** - * Returns an {@code Optional} which contains this client's {@link - * CookieManager}. If no {@code CookieManager} was set in this client's builder, - * then the {@code Optional} is empty. + * Returns an {@code Optional} containing this client's {@linkplain + * CookieHandler}. If no {@code CookieHandler} was set in this client's + * builder, then the {@code Optional} is empty. * - * @return an {@code Optional} containing this client's {@code CookieManager} + * @return an {@code Optional} containing this client's {@code CookieHandler} */ - public abstract Optional<CookieManager> cookieManager(); + public abstract Optional<CookieHandler> cookieHandler(); /** - * Returns the follow-redirects setting for this client. The default value - * for this setting is {@link HttpClient.Redirect#NEVER} + * Returns the follow redirects policy for this client. The default value + * for client's built by builders that do not specify a redirect policy is + * {@link HttpClient.Redirect#NEVER NEVER}. * * @return this client's follow redirects setting */ public abstract Redirect followRedirects(); /** - * Returns an {@code Optional} containing the {@code ProxySelector} for this client. - * If no proxy is set then the {@code Optional} is empty. + * Returns an {@code Optional} containing the {@code ProxySelector} + * supplied to this client. If no proxy selector was set in this client's + * builder, then the {@code Optional} is empty. * - * @return an {@code Optional} containing this client's proxy selector + * <p> Even though this method may return an empty optional, the {@code + * HttpClient} may still have an non-exposed {@linkplain + * Builder#proxy(ProxySelector) default proxy selector} that is + * used for sending HTTP requests. + * + * @return an {@code Optional} containing the proxy selector supplied + * to this client. */ public abstract Optional<ProxySelector> proxy(); /** - * Returns the {@code SSLContext}, if one was set on this client. If a security - * manager is set, then the caller must have the - * {@link java.net.NetPermission NetPermission}("getSSLContext") permission. - * If no {@code SSLContext} was set, then the default context is returned. + * Returns this client's {@code SSLContext}. + * + * <p> If no {@code SSLContext} was set in this client's builder, then the + * {@linkplain SSLContext#getDefault() default context} is returned. * * @return this client's SSLContext - * @throws SecurityException if the caller does not have permission to get - * the SSLContext */ public abstract SSLContext sslContext(); /** - * Returns an {@code Optional} containing the {@link SSLParameters} set on - * this client. If no {@code SSLParameters} were set in the client's builder, - * then the {@code Optional} is empty. + * Returns a copy of this client's {@link SSLParameters}. * - * @return an {@code Optional} containing this client's {@code SSLParameters} + * <p> If no {@code SSLParameters} were set in the client's builder, then an + * implementation specific default set of parameters, that the client will + * use, is returned. + * + * @return this client's {@code SSLParameters} */ - public abstract Optional<SSLParameters> sslParameters(); + public abstract SSLParameters sslParameters(); /** * Returns an {@code Optional} containing the {@link Authenticator} set on @@ -274,14 +334,18 @@ public abstract class HttpClient { public abstract HttpClient.Version version(); /** - * Returns the {@code Executor} set on this client. If an {@code - * Executor} was not set on the client's builder, then a default - * object is returned. The default {@code Executor} is created independently - * for each client. + * Returns an {@code Optional} containing this client's {@linkplain + * Executor}. If no {@code Executor} was set in the client's builder, + * then the {@code Optional} is empty. * - * @return this client's Executor + * <p> Even though this method may return an empty optional, the {@code + * HttpClient} may still have an non-exposed {@linkplain + * HttpClient.Builder#executor(Executor) default executor} that is used for + * executing asynchronous and dependent tasks. + * + * @return an {@code Optional} containing this client's {@code Executor} */ - public abstract Executor executor(); + public abstract Optional<Executor> executor(); /** * The HTTP protocol version. @@ -349,8 +413,14 @@ public abstract class HttpClient { * @param req the request * @param responseBodyHandler the response body handler * @return the response body - * @throws java.io.IOException if an I/O error occurs when sending or receiving - * @throws java.lang.InterruptedException if the operation is interrupted + * @throws IOException if an I/O error occurs when sending or receiving + * @throws InterruptedException if the operation is interrupted + * @throws IllegalArgumentException if the request method is not supported + * @throws SecurityException If a security manager has been installed + * and it denies {@link java.net.URLPermission access} to the + * URL in the given request, or proxy if one is configured. + * See HttpRequest for further information about + * <a href="HttpRequest.html#securitychecks">security checks</a>. */ public abstract <T> HttpResponse<T> send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler) @@ -360,6 +430,17 @@ public abstract class HttpClient { * Sends the given request asynchronously using this client and the given * response handler. * + * <p> The returned completable future completes exceptionally with: + * <ul> + * <li>{@link IOException} - if an I/O error occurs when sending or receiving</li> + * <li>{@link IllegalArgumentException} - if the request method is not supported</li> + * <li>{@link SecurityException} - If a security manager has been installed + * and it denies {@link java.net.URLPermission access} to the + * URL in the given request, or proxy if one is configured. + * See HttpRequest for further information about + * <a href="HttpRequest.html#securitychecks">security checks</a>.</li> + * </ul> + * * @param <T> the response body type * @param req the request * @param responseBodyHandler the response body handler @@ -372,25 +453,34 @@ public abstract class HttpClient { * Sends the given request asynchronously using this client and the given * multi response handler. * + * <p> The returned completable future completes exceptionally with: + * <ul> + * <li>{@link IOException} - if an I/O error occurs when sending or receiving</li> + * <li>{@link IllegalArgumentException} - if the request method is not supported</li> + * <li>{@link SecurityException} - If a security manager has been installed + * and it denies {@link java.net.URLPermission access} to the + * URL in the given request, or proxy if one is configured. + * See HttpRequest for further information about + * <a href="HttpRequest.html#securitychecks">security checks</a>.</li> + * </ul> + * * @param <U> a type representing the aggregated results * @param <T> a type representing all of the response bodies * @param req the request - * @param multiProcessor the MultiProcessor for the request + * @param multiSubscriber the multiSubscriber for the request * @return a {@code CompletableFuture<U>} */ public abstract <U, T> CompletableFuture<U> - sendAsync(HttpRequest req, HttpResponse.MultiProcessor<U, T> multiProcessor); + sendAsync(HttpRequest req, HttpResponse.MultiSubscriber<U, T> multiSubscriber); /** - * Creates a builder of {@link WebSocket} instances connected to the given - * URI and receiving events and messages with the given {@code Listener}. + * Creates a new {@code WebSocket} builder (optional operation). * * <p> <b>Example</b> * <pre>{@code * HttpClient client = HttpClient.newHttpClient(); - * WebSocket.Builder builder = client.newWebSocketBuilder( - * URI.create("ws://websocket.example.com"), - * listener); + * CompletableFuture<WebSocket> ws = client.newWebSocketBuilder() + * .buildAsync(URI.create("ws://websocket.example.com"), listener); * }</pre> * * <p> Finer control over the WebSocket Opening Handshake can be achieved @@ -402,28 +492,33 @@ public abstract class HttpClient { * HttpClient client = HttpClient.newBuilder() * .proxy(ProxySelector.of(addr)) * .build(); - * WebSocket.Builder builder = client.newWebSocketBuilder( - * URI.create("ws://websocket.example.com"), - * listener); + * CompletableFuture<WebSocket> ws = client.newWebSocketBuilder() + * .buildAsync(URI.create("ws://websocket.example.com"), listener); * }</pre> * - * @implSpec The default implementation of this method throws {@code - * UnsupportedOperationException}. However, clients obtained through + * <p> A {@code WebSocket.Builder} returned from this method is not safe for + * use by multiple threads without external synchronization. + * + * @implSpec The default implementation of this method throws + * {@code UnsupportedOperationException}. Clients obtained through * {@link HttpClient#newHttpClient()} or {@link HttpClient#newBuilder()} - * provide WebSocket capability. + * return a {@code WebSocket} builder. * - * @param uri - * the WebSocket URI - * @param listener - * the listener + * @implNote Both builder and {@code WebSocket}s created with it operate in + * a non-blocking fashion. That is, their methods do not block before + * returning a {@code CompletableFuture}. Asynchronous tasks are executed in + * this {@code HttpClient}'s executor. * - * @return a builder of {@code WebSocket} instances + * <p> When a {@code CompletionStage} returned from + * {@link WebSocket.Listener#onClose Listener.onClose} completes, + * the {@code WebSocket} will send a Close message that has the same code + * the received message has and an empty reason. + * + * @return a {@code WebSocket.Builder} * @throws UnsupportedOperationException * if this {@code HttpClient} does not provide WebSocket support */ - public WebSocket.Builder newWebSocketBuilder(URI uri, - WebSocket.Listener listener) - { + public WebSocket.Builder newWebSocketBuilder() { throw new UnsupportedOperationException(); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java index f2351cffa52..98a838a58ed 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,7 @@ package jdk.incubator.http; import java.net.Authenticator; -import java.net.CookieManager; +import java.net.CookieHandler; import java.net.ProxySelector; import java.util.concurrent.Executor; import javax.net.ssl.SSLContext; @@ -36,7 +36,7 @@ import static java.util.Objects.requireNonNull; class HttpClientBuilderImpl extends HttpClient.Builder { - CookieManager cookieManager; + CookieHandler cookieHandler; HttpClient.Redirect followRedirects; ProxySelector proxy; Authenticator authenticator; @@ -48,9 +48,9 @@ class HttpClientBuilderImpl extends HttpClient.Builder { int priority = -1; @Override - public HttpClientBuilderImpl cookieManager(CookieManager cookieManager) { - requireNonNull(cookieManager); - this.cookieManager = cookieManager; + public HttpClientBuilderImpl cookieHandler(CookieHandler cookieHandler) { + requireNonNull(cookieHandler); + this.cookieHandler = cookieHandler; return this; } @@ -58,7 +58,6 @@ class HttpClientBuilderImpl extends HttpClient.Builder { @Override public HttpClientBuilderImpl sslContext(SSLContext sslContext) { requireNonNull(sslContext); - Utils.checkNetPermission("setSSLContext"); this.sslContext = sslContext; return this; } @@ -67,7 +66,7 @@ class HttpClientBuilderImpl extends HttpClient.Builder { @Override public HttpClientBuilderImpl sslParameters(SSLParameters sslParameters) { requireNonNull(sslParameters); - this.sslParams = sslParameters; + this.sslParams = Utils.copySSLParameters(sslParameters); return this; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java new file mode 100644 index 00000000000..449cc9830df --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.io.IOException; +import java.lang.ref.Reference; +import java.net.Authenticator; +import java.net.CookieHandler; +import java.net.ProxySelector; +import java.net.URI; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; + +/** + * An HttpClientFacade is a simple class that wraps an HttpClient implementation + * and delegates everything to its implementation delegate. + */ +final class HttpClientFacade extends HttpClient { + + final HttpClientImpl impl; + + /** + * Creates an HttpClientFacade. + */ + HttpClientFacade(HttpClientImpl impl) { + this.impl = impl; + } + + @Override + public Optional<CookieHandler> cookieHandler() { + return impl.cookieHandler(); + } + + @Override + public Redirect followRedirects() { + return impl.followRedirects(); + } + + @Override + public Optional<ProxySelector> proxy() { + return impl.proxy(); + } + + @Override + public SSLContext sslContext() { + return impl.sslContext(); + } + + @Override + public SSLParameters sslParameters() { + return impl.sslParameters(); + } + + @Override + public Optional<Authenticator> authenticator() { + return impl.authenticator(); + } + + @Override + public HttpClient.Version version() { + return impl.version(); + } + + @Override + public Optional<Executor> executor() { + return impl.executor(); + } + + @Override + public <T> HttpResponse<T> + send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler) + throws IOException, InterruptedException + { + try { + return impl.send(req, responseBodyHandler); + } finally { + Reference.reachabilityFence(this); + } + } + + @Override + public <T> CompletableFuture<HttpResponse<T>> + sendAsync(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler) { + try { + return impl.sendAsync(req, responseBodyHandler); + } finally { + Reference.reachabilityFence(this); + } + } + + @Override + public <U, T> CompletableFuture<U> + sendAsync(HttpRequest req, HttpResponse.MultiSubscriber<U, T> multiSubscriber) { + try { + return impl.sendAsync(req, multiSubscriber); + } finally { + Reference.reachabilityFence(this); + } + } + + @Override + public WebSocket.Builder newWebSocketBuilder() { + try { + return impl.newWebSocketBuilder(); + } finally { + Reference.reachabilityFence(this); + } + } + + @Override + public String toString() { + // Used by tests to get the client's id. + return impl.toString(); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java index c4087b0a4ec..d5753361686 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java @@ -28,33 +28,45 @@ package jdk.incubator.http; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import java.io.IOException; +import java.lang.System.Logger.Level; import java.lang.ref.WeakReference; import java.net.Authenticator; -import java.net.CookieManager; +import java.net.CookieHandler; import java.net.ProxySelector; -import java.net.URI; +import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import java.security.AccessControlContext; +import java.security.AccessController; import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; +import jdk.incubator.http.HttpResponse.BodyHandler; +import jdk.incubator.http.HttpResponse.MultiSubscriber; import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.Pair; import jdk.incubator.http.internal.common.Utils; import jdk.incubator.http.internal.websocket.BuilderImpl; +import jdk.internal.misc.InnocuousThread; /** * Client implementation. Contains all configuration information and also @@ -63,46 +75,138 @@ import jdk.incubator.http.internal.websocket.BuilderImpl; */ class HttpClientImpl extends HttpClient { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + static final boolean DEBUGELAPSED = Utils.TESTING || DEBUG; // Revisit: temporary dev flag. + static final boolean DEBUGTIMEOUT = false; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final System.Logger debugelapsed = Utils.getDebugLogger(this::dbgString, DEBUGELAPSED); + final System.Logger debugtimeout = Utils.getDebugLogger(this::dbgString, DEBUGTIMEOUT); + static final AtomicLong CLIENT_IDS = new AtomicLong(); + // Define the default factory as a static inner class // that embeds all the necessary logic to avoid // the risk of using a lambda that might keep a reference on the // HttpClient instance from which it was created (helps with // heapdump analysis). private static final class DefaultThreadFactory implements ThreadFactory { - private DefaultThreadFactory() {} + private final String namePrefix; + private final AtomicInteger nextId = new AtomicInteger(); + + DefaultThreadFactory(long clientID) { + namePrefix = "HttpClient-" + clientID + "-Worker-"; + } + @Override public Thread newThread(Runnable r) { - Thread t = new Thread(null, r, "HttpClient_worker", 0, true); + String name = namePrefix + nextId.getAndIncrement(); + Thread t; + if (System.getSecurityManager() == null) { + t = new Thread(null, r, name, 0, false); + } else { + t = InnocuousThread.newThread(name, r); + } t.setDaemon(true); return t; } - static final ThreadFactory INSTANCE = new DefaultThreadFactory(); } - private final CookieManager cookieManager; + private final CookieHandler cookieHandler; private final Redirect followRedirects; + private final Optional<ProxySelector> userProxySelector; private final ProxySelector proxySelector; private final Authenticator authenticator; private final Version version; private final ConnectionPool connections; private final Executor executor; + private final boolean isDefaultExecutor; // Security parameters private final SSLContext sslContext; private final SSLParameters sslParams; private final SelectorManager selmgr; private final FilterFactory filters; private final Http2ClientImpl client2; + private final long id; + private final String dbgTag; + + // This reference is used to keep track of the facade HttpClient + // that was returned to the application code. + // It makes it possible to know when the application no longer + // holds any reference to the HttpClient. + // Unfortunately, this information is not enough to know when + // to exit the SelectorManager thread. Because of the asynchronous + // nature of the API, we also need to wait until all pending operations + // have completed. + private final WeakReference<HttpClientFacade> facadeRef; + + // This counter keeps track of the number of operations pending + // on the HttpClient. The SelectorManager thread will wait + // until there are no longer any pending operations and the + // facadeRef is cleared before exiting. + // + // The pendingOperationCount is incremented every time a send/sendAsync + // operation is invoked on the HttpClient, and is decremented when + // the HttpResponse<T> object is returned to the user. + // However, at this point, the body may not have been fully read yet. + // This is the case when the response T is implemented as a streaming + // subscriber (such as an InputStream). + // + // To take care of this issue the pendingOperationCount will additionally + // be incremented/decremented in the following cases: + // + // 1. For HTTP/2 it is incremented when a stream is added to the + // Http2Connection streams map, and decreased when the stream is removed + // from the map. This should also take care of push promises. + // 2. For WebSocket the count is increased when creating a + // DetachedConnectionChannel for the socket, and decreased + // when the the channel is closed. + // In addition, the HttpClient facade is passed to the WebSocket builder, + // (instead of the client implementation delegate). + // 3. For HTTP/1.1 the count is incremented before starting to parse the body + // response, and decremented when the parser has reached the end of the + // response body flow. + // + // This should ensure that the selector manager thread remains alive until + // the response has been fully received or the web socket is closed. + private final AtomicLong pendingOperationCount = new AtomicLong(); + private final AtomicLong pendingWebSocketCount = new AtomicLong(); + private final AtomicLong pendingHttpRequestCount = new AtomicLong(); /** A Set of, deadline first, ordered timeout events. */ private final TreeSet<TimeoutEvent> timeouts; - public static HttpClientImpl create(HttpClientBuilderImpl builder) { - HttpClientImpl impl = new HttpClientImpl(builder); - impl.start(); - return impl; + /** + * This is a bit tricky: + * 1. an HttpClientFacade has a final HttpClientImpl field. + * 2. an HttpClientImpl has a final WeakReference<HttpClientFacade> field, + * where the referent is the facade created for that instance. + * 3. We cannot just create the HttpClientFacade in the HttpClientImpl + * constructor, because it would be only weakly referenced and could + * be GC'ed before we can return it. + * The solution is to use an instance of SingleFacadeFactory which will + * allow the caller of new HttpClientImpl(...) to retrieve the facade + * after the HttpClientImpl has been created. + */ + private static final class SingleFacadeFactory { + HttpClientFacade facade; + HttpClientFacade createFacade(HttpClientImpl impl) { + assert facade == null; + return (facade = new HttpClientFacade(impl)); + } } - private HttpClientImpl(HttpClientBuilderImpl builder) { + static HttpClientFacade create(HttpClientBuilderImpl builder) { + SingleFacadeFactory facadeFactory = new SingleFacadeFactory(); + HttpClientImpl impl = new HttpClientImpl(builder, facadeFactory); + impl.start(); + assert facadeFactory.facade != null; + assert impl.facadeRef.get() == facadeFactory.facade; + return facadeFactory.facade; + } + + private HttpClientImpl(HttpClientBuilderImpl builder, + SingleFacadeFactory facadeFactory) { + id = CLIENT_IDS.incrementAndGet(); + dbgTag = "HttpClientImpl(" + id +")"; if (builder.sslContext == null) { try { sslContext = SSLContext.getDefault(); @@ -114,16 +218,23 @@ class HttpClientImpl extends HttpClient { } Executor ex = builder.executor; if (ex == null) { - ex = Executors.newCachedThreadPool(DefaultThreadFactory.INSTANCE); + ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id)); + isDefaultExecutor = true; } else { ex = builder.executor; + isDefaultExecutor = false; } + facadeRef = new WeakReference<>(facadeFactory.createFacade(this)); client2 = new Http2ClientImpl(this); executor = ex; - cookieManager = builder.cookieManager; + cookieHandler = builder.cookieHandler; followRedirects = builder.followRedirects == null ? Redirect.NEVER : builder.followRedirects; - this.proxySelector = builder.proxy; + this.userProxySelector = Optional.ofNullable(builder.proxy); + this.proxySelector = userProxySelector + .orElseGet(HttpClientImpl::getDefaultProxySelector); + debug.log(Level.DEBUG, "proxySelector is %s (user-supplied=%s)", + this.proxySelector, userProxySelector.isPresent()); authenticator = builder.authenticator; if (builder.version == null) { version = HttpClient.Version.HTTP_2; @@ -135,7 +246,7 @@ class HttpClientImpl extends HttpClient { } else { sslParams = builder.sslParams; } - connections = new ConnectionPool(); + connections = new ConnectionPool(id); connections.start(); timeouts = new TreeSet<>(); try { @@ -147,30 +258,102 @@ class HttpClientImpl extends HttpClient { selmgr.setDaemon(true); filters = new FilterFactory(); initFilters(); + assert facadeRef.get() != null; } private void start() { selmgr.start(); } + // Called from the SelectorManager thread, just before exiting. + // Clears the HTTP/1.1 and HTTP/2 cache, ensuring that the connections + // that may be still lingering there are properly closed (and their + // possibly still opened SocketChannel released). + private void stop() { + // Clears HTTP/1.1 cache and close its connections + connections.stop(); + // Clears HTTP/2 cache and close its connections. + client2.stop(); + } + private static SSLParameters getDefaultParams(SSLContext ctx) { SSLParameters params = ctx.getSupportedSSLParameters(); params.setProtocols(new String[]{"TLSv1.2"}); return params; } + private static ProxySelector getDefaultProxySelector() { + PrivilegedAction<ProxySelector> action = ProxySelector::getDefault; + return AccessController.doPrivileged(action); + } + + // Returns the facade that was returned to the application code. + // May be null if that facade is no longer referenced. + final HttpClientFacade facade() { + return facadeRef.get(); + } + + // Increments the pendingOperationCount. + final long reference() { + pendingHttpRequestCount.incrementAndGet(); + return pendingOperationCount.incrementAndGet(); + } + + // Decrements the pendingOperationCount. + final long unreference() { + final long count = pendingOperationCount.decrementAndGet(); + final long httpCount = pendingHttpRequestCount.decrementAndGet(); + final long webSocketCount = pendingWebSocketCount.get(); + if (count == 0 && facade() == null) { + selmgr.wakeupSelector(); + } + assert httpCount >= 0 : "count of HTTP operations < 0"; + assert webSocketCount >= 0 : "count of WS operations < 0"; + assert count >= 0 : "count of pending operations < 0"; + return count; + } + + // Increments the pendingOperationCount. + final long webSocketOpen() { + pendingWebSocketCount.incrementAndGet(); + return pendingOperationCount.incrementAndGet(); + } + + // Decrements the pendingOperationCount. + final long webSocketClose() { + final long count = pendingOperationCount.decrementAndGet(); + final long webSocketCount = pendingWebSocketCount.decrementAndGet(); + final long httpCount = pendingHttpRequestCount.get(); + if (count == 0 && facade() == null) { + selmgr.wakeupSelector(); + } + assert httpCount >= 0 : "count of HTTP operations < 0"; + assert webSocketCount >= 0 : "count of WS operations < 0"; + assert count >= 0 : "count of pending operations < 0"; + return count; + } + + // Returns the pendingOperationCount. + final long referenceCount() { + return pendingOperationCount.get(); + } + + // Called by the SelectorManager thread to figure out whether it's time + // to terminate. + final boolean isReferenced() { + HttpClient facade = facade(); + return facade != null || referenceCount() > 0; + } + /** - * Wait for activity on given exchange (assuming blocking = false). - * It's a no-op if blocking = true. In particular, the following occurs - * in the SelectorManager thread. + * Wait for activity on given exchange. + * The following occurs in the SelectorManager thread. * - * 1) mark the connection non-blocking - * 2) add to selector - * 3) If selector fires for this exchange then - * 4) - mark connection as blocking - * 5) - call AsyncEvent.handle() + * 1) add to selector + * 2) If selector fires for this exchange then + * call AsyncEvent.handle() * - * If exchange needs to block again, then call registerEvent() again + * If exchange needs to change interest ops, then call registerEvent() again. */ void registerEvent(AsyncEvent exchange) throws IOException { selmgr.register(exchange); @@ -184,131 +367,196 @@ class HttpClientImpl extends HttpClient { selmgr.cancel(s); } + /** + * Allows an AsyncEvent to modify its interestOps. + * @param event The modified event. + */ + void eventUpdated(AsyncEvent event) throws ClosedChannelException { + assert !(event instanceof AsyncTriggerEvent); + selmgr.eventUpdated(event); + } + + boolean isSelectorThread() { + return Thread.currentThread() == selmgr; + } Http2ClientImpl client2() { return client2; } - /* - @Override - public ByteBuffer getBuffer() { - return pool.getBuffer(); + private void debugCompleted(String tag, long startNanos, HttpRequest req) { + if (debugelapsed.isLoggable(Level.DEBUG)) { + debugelapsed.log(Level.DEBUG, () -> tag + " elapsed " + + (System.nanoTime() - startNanos)/1000_000L + + " millis for " + req.method() + + " to " + req.uri()); + } } - // SSL buffers are larger. Manage separately - - int size = 16 * 1024; - - ByteBuffer getSSLBuffer() { - return ByteBuffer.allocate(size); - } - - /** - * Return a new buffer that's a bit bigger than the given one - * - * @param buf - * @return - * - ByteBuffer reallocSSLBuffer(ByteBuffer buf) { - size = buf.capacity() * 12 / 10; // 20% bigger - return ByteBuffer.allocate(size); - } - - synchronized void returnSSLBuffer(ByteBuffer buf) { - if (buf.capacity() >= size) - sslBuffers.add(0, buf); - } - - @Override - public void returnBuffer(ByteBuffer buffer) { - pool.returnBuffer(buffer); - } - */ - @Override public <T> HttpResponse<T> - send(HttpRequest req, HttpResponse.BodyHandler<T> responseHandler) + send(HttpRequest req, BodyHandler<T> responseHandler) throws IOException, InterruptedException { - MultiExchange<Void,T> mex = new MultiExchange<>(req, this, responseHandler); - return mex.response(); + try { + return sendAsync(req, responseHandler).get(); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof Error) + throw (Error)t; + if (t instanceof RuntimeException) + throw (RuntimeException)t; + else if (t instanceof IOException) + throw Utils.getIOException(t); + else + throw new InternalError("Unexpected exception", t); + } } @Override public <T> CompletableFuture<HttpResponse<T>> - sendAsync(HttpRequest req, HttpResponse.BodyHandler<T> responseHandler) + sendAsync(HttpRequest userRequest, BodyHandler<T> responseHandler) { - MultiExchange<Void,T> mex = new MultiExchange<>(req, this, responseHandler); - return mex.responseAsync() - .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b); + AccessControlContext acc = null; + if (System.getSecurityManager() != null) + acc = AccessController.getContext(); + + // Clone the, possibly untrusted, HttpRequest + HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector, acc); + if (requestImpl.method().equals("CONNECT")) + throw new IllegalArgumentException("Unsupported method CONNECT"); + + long start = DEBUGELAPSED ? System.nanoTime() : 0; + reference(); + try { + debugelapsed.log(Level.DEBUG, "ClientImpl (async) send %s", userRequest); + + MultiExchange<Void,T> mex = new MultiExchange<>(userRequest, + requestImpl, + this, + responseHandler, + acc); + CompletableFuture<HttpResponse<T>> res = + mex.responseAsync().whenComplete((b,t) -> unreference()); + if (DEBUGELAPSED) { + res = res.whenComplete( + (b,t) -> debugCompleted("ClientImpl (async)", start, userRequest)); + } + // makes sure that any dependent actions happen in the executor + if (acc != null) { + res.whenCompleteAsync((r, t) -> { /* do nothing */}, + new PrivilegedExecutor(executor, acc)); + } + + return res; + } catch(Throwable t) { + unreference(); + debugCompleted("ClientImpl (async)", start, userRequest); + throw t; + } } @Override public <U, T> CompletableFuture<U> - sendAsync(HttpRequest req, HttpResponse.MultiProcessor<U, T> responseHandler) { - MultiExchange<U,T> mex = new MultiExchange<>(req, this, responseHandler); - return mex.multiResponseAsync(); - } + sendAsync(HttpRequest userRequest, MultiSubscriber<U, T> responseHandler) { + AccessControlContext acc = null; + if (System.getSecurityManager() != null) + acc = AccessController.getContext(); - // new impl. Should get rid of above - /* - static class BufferPool implements BufferHandler { + // Clone the, possibly untrusted, HttpRequest + HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector, acc); + if (requestImpl.method().equals("CONNECT")) + throw new IllegalArgumentException("Unsupported method CONNECT"); - final LinkedList<ByteBuffer> freelist = new LinkedList<>(); + long start = DEBUGELAPSED ? System.nanoTime() : 0; + reference(); + try { + debugelapsed.log(Level.DEBUG, "ClientImpl (async) send multi %s", userRequest); - @Override - public synchronized ByteBuffer getBuffer() { - ByteBuffer buf; - - while (!freelist.isEmpty()) { - buf = freelist.removeFirst(); - buf.clear(); - return buf; + MultiExchange<U,T> mex = new MultiExchange<>(userRequest, + requestImpl, + this, + responseHandler, + acc); + CompletableFuture<U> res = mex.multiResponseAsync() + .whenComplete((b,t) -> unreference()); + if (DEBUGELAPSED) { + res = res.whenComplete( + (b,t) -> debugCompleted("ClientImpl (async)", start, userRequest)); + } + // makes sure that any dependent actions happen in the executor + if (acc != null) { + res.whenCompleteAsync((r, t) -> { /* do nothing */}, + new PrivilegedExecutor(executor, acc)); } - return ByteBuffer.allocate(BUFSIZE); - } - @Override - public synchronized void returnBuffer(ByteBuffer buffer) { - assert buffer.capacity() > 0; - freelist.add(buffer); + return res; + } catch(Throwable t) { + unreference(); + debugCompleted("ClientImpl (async)", start, userRequest); + throw t; } } - static BufferPool pool = new BufferPool(); - - static BufferHandler pool() { - return pool; - } -*/ // Main loop for this client's selector private final static class SelectorManager extends Thread { - private static final long NODEADLINE = 3000L; + // For testing purposes we have an internal System property that + // can control the frequency at which the selector manager will wake + // up when there are no pending operations. + // Increasing the frequency (shorter delays) might allow the selector + // to observe that the facade is no longer referenced and might allow + // the selector thread to terminate more timely - for when nothing is + // ongoing it will only check for that condition every NODEADLINE ms. + // To avoid misuse of the property, the delay that can be specified + // is comprised between [MIN_NODEADLINE, MAX_NODEADLINE], and its default + // value if unspecified (or <= 0) is DEF_NODEADLINE = 3000ms + // The property is -Djdk.httpclient.internal.selector.timeout=<millis> + private static final int MIN_NODEADLINE = 1000; // ms + private static final int MAX_NODEADLINE = 1000 * 1200; // ms + private static final int DEF_NODEADLINE = 3000; // ms + private static final long NODEADLINE; // default is DEF_NODEADLINE ms + static { + // ensure NODEADLINE is initialized with some valid value. + long deadline = Utils.getIntegerNetProperty( + "jdk.httpclient.internal.selector.timeout", + DEF_NODEADLINE); // millis + if (deadline <= 0) deadline = DEF_NODEADLINE; + deadline = Math.max(deadline, MIN_NODEADLINE); + NODEADLINE = Math.min(deadline, MAX_NODEADLINE); + } + private final Selector selector; private volatile boolean closed; - private final List<AsyncEvent> readyList; private final List<AsyncEvent> registrations; - - // Uses a weak reference to the HttpClient owning this - // selector: a strong reference prevents its garbage - // collection while the thread is running. - // We want the thread to exit gracefully when the - // HttpClient that owns it gets GC'ed. - WeakReference<HttpClientImpl> ownerRef; + private final System.Logger debug; + private final System.Logger debugtimeout; + HttpClientImpl owner; + ConnectionPool pool; SelectorManager(HttpClientImpl ref) throws IOException { - super(null, null, "SelectorManager", 0, false); - ownerRef = new WeakReference<>(ref); - readyList = new ArrayList<>(); + super(null, null, "HttpClient-" + ref.id + "-SelectorManager", 0, false); + owner = ref; + debug = ref.debug; + debugtimeout = ref.debugtimeout; + pool = ref.connectionPool(); registrations = new ArrayList<>(); selector = Selector.open(); } + void eventUpdated(AsyncEvent e) throws ClosedChannelException { + if (Thread.currentThread() == this) { + SelectionKey key = e.channel().keyFor(selector); + SelectorAttachment sa = (SelectorAttachment) key.attachment(); + if (sa != null) sa.register(e); + } else { + register(e); + } + } + // This returns immediately. So caller not allowed to send/receive // on connection. - - synchronized void register(AsyncEvent e) throws IOException { + synchronized void register(AsyncEvent e) { registrations.add(e); selector.wakeup(); } @@ -326,23 +574,34 @@ class HttpClientImpl extends HttpClient { } synchronized void shutdown() { + debug.log(Level.DEBUG, "SelectorManager shutting down"); closed = true; try { selector.close(); - } catch (IOException ignored) { } + } catch (IOException ignored) { + } finally { + owner.stop(); + } } @Override public void run() { + List<Pair<AsyncEvent,IOException>> errorList = new ArrayList<>(); + List<AsyncEvent> readyList = new ArrayList<>(); try { while (!Thread.currentThread().isInterrupted()) { - HttpClientImpl client; synchronized (this) { - for (AsyncEvent exchange : registrations) { - SelectableChannel c = exchange.channel(); + assert errorList.isEmpty(); + assert readyList.isEmpty(); + for (AsyncEvent event : registrations) { + if (event instanceof AsyncTriggerEvent) { + readyList.add(event); + continue; + } + SelectableChannel chan = event.channel(); + SelectionKey key = null; try { - c.configureBlocking(false); - SelectionKey key = c.keyFor(selector); + key = chan.keyFor(selector); SelectorAttachment sa; if (key == null || !key.isValid()) { if (key != null) { @@ -351,91 +610,163 @@ class HttpClientImpl extends HttpClient { // before registering the new event. selector.selectNow(); } - sa = new SelectorAttachment(c, selector); + sa = new SelectorAttachment(chan, selector); } else { sa = (SelectorAttachment) key.attachment(); } - sa.register(exchange); + // may throw IOE if channel closed: that's OK + sa.register(event); + if (!chan.isOpen()) { + throw new IOException("Channel closed"); + } } catch (IOException e) { - Log.logError("HttpClientImpl: " + e); - c.close(); - // let the exchange deal with it - handleEvent(exchange); + Log.logTrace("HttpClientImpl: " + e); + debug.log(Level.DEBUG, () -> + "Got " + e.getClass().getName() + + " while handling" + + " registration events"); + chan.close(); + // let the event abort deal with it + errorList.add(new Pair<>(event, e)); + if (key != null) { + key.cancel(); + selector.selectNow(); + } } } registrations.clear(); + selector.selectedKeys().clear(); } + for (AsyncEvent event : readyList) { + assert event instanceof AsyncTriggerEvent; + event.handle(); + } + readyList.clear(); + + for (Pair<AsyncEvent,IOException> error : errorList) { + // an IOException was raised and the channel closed. + handleEvent(error.first, error.second); + } + errorList.clear(); + // Check whether client is still alive, and if not, // gracefully stop this thread - if ((client = ownerRef.get()) == null) { + if (!owner.isReferenced()) { Log.logTrace("HttpClient no longer referenced. Exiting..."); return; } - long millis = client.purgeTimeoutsAndReturnNextDeadline(); - client = null; // don't hold onto the client ref - //debugPrint(selector); + // Timeouts will have milliseconds granularity. It is important + // to handle them in a timely fashion. + long nextTimeout = owner.purgeTimeoutsAndReturnNextDeadline(); + debugtimeout.log(Level.DEBUG, "next timeout: %d", nextTimeout); + + // Keep-alive have seconds granularity. It's not really an + // issue if we keep connections linger a bit more in the keep + // alive cache. + long nextExpiry = pool.purgeExpiredConnectionsAndReturnNextDeadline(); + debugtimeout.log(Level.DEBUG, "next expired: %d", nextExpiry); + + assert nextTimeout >= 0; + assert nextExpiry >= 0; + // Don't wait for ever as it might prevent the thread to // stop gracefully. millis will be 0 if no deadline was found. + if (nextTimeout <= 0) nextTimeout = NODEADLINE; + + // Clip nextExpiry at NODEADLINE limit. The default + // keep alive is 1200 seconds (half an hour) - we don't + // want to wait that long. + if (nextExpiry <= 0) nextExpiry = NODEADLINE; + else nextExpiry = Math.min(NODEADLINE, nextExpiry); + + // takes the least of the two. + long millis = Math.min(nextExpiry, nextTimeout); + + debugtimeout.log(Level.DEBUG, "Next deadline is %d", + (millis == 0 ? NODEADLINE : millis)); + //debugPrint(selector); int n = selector.select(millis == 0 ? NODEADLINE : millis); if (n == 0) { // Check whether client is still alive, and if not, // gracefully stop this thread - if ((client = ownerRef.get()) == null) { + if (!owner.isReferenced()) { Log.logTrace("HttpClient no longer referenced. Exiting..."); return; } - client.purgeTimeoutsAndReturnNextDeadline(); - client = null; // don't hold onto the client ref + owner.purgeTimeoutsAndReturnNextDeadline(); continue; } Set<SelectionKey> keys = selector.selectedKeys(); + assert errorList.isEmpty(); for (SelectionKey key : keys) { SelectorAttachment sa = (SelectorAttachment) key.attachment(); - int eventsOccurred = key.readyOps(); + if (!key.isValid()) { + IOException ex = sa.chan.isOpen() + ? new IOException("Invalid key") + : new ClosedChannelException(); + sa.pending.forEach(e -> errorList.add(new Pair<>(e,ex))); + sa.pending.clear(); + continue; + } + + int eventsOccurred; + try { + eventsOccurred = key.readyOps(); + } catch (CancelledKeyException ex) { + IOException io = Utils.getIOException(ex); + sa.pending.forEach(e -> errorList.add(new Pair<>(e,io))); + sa.pending.clear(); + continue; + } sa.events(eventsOccurred).forEach(readyList::add); sa.resetInterestOps(eventsOccurred); } selector.selectNow(); // complete cancellation selector.selectedKeys().clear(); - for (AsyncEvent exchange : readyList) { - if (exchange.blocking()) { - exchange.channel().configureBlocking(true); - } - handleEvent(exchange); // will be delegated to executor + for (AsyncEvent event : readyList) { + handleEvent(event, null); // will be delegated to executor } readyList.clear(); + errorList.forEach((p) -> handleEvent(p.first, p.second)); + errorList.clear(); } } catch (Throwable e) { + //e.printStackTrace(); if (!closed) { // This terminates thread. So, better just print stack trace String err = Utils.stackTrace(e); Log.logError("HttpClientImpl: fatal error: " + err); } + debug.log(Level.DEBUG, "shutting down", e); + if (Utils.ASSERTIONSENABLED && !debug.isLoggable(Level.DEBUG)) { + e.printStackTrace(System.err); // always print the stack + } } finally { shutdown(); } } - void debugPrint(Selector selector) { - System.err.println("Selector: debugprint start"); - Set<SelectionKey> keys = selector.keys(); - for (SelectionKey key : keys) { - SelectableChannel c = key.channel(); - int ops = key.interestOps(); - System.err.printf("selector chan:%s ops:%d\n", c, ops); - } - System.err.println("Selector: debugprint end"); - } +// void debugPrint(Selector selector) { +// System.err.println("Selector: debugprint start"); +// Set<SelectionKey> keys = selector.keys(); +// for (SelectionKey key : keys) { +// SelectableChannel c = key.channel(); +// int ops = key.interestOps(); +// System.err.printf("selector chan:%s ops:%d\n", c, ops); +// } +// System.err.println("Selector: debugprint end"); +// } - void handleEvent(AsyncEvent e) { - if (closed) { - e.abort(); + /** Handles the given event. The given ioe may be null. */ + void handleEvent(AsyncEvent event, IOException ioe) { + if (closed || ioe != null) { + event.abort(ioe); } else { - e.handle(); + event.handle(); } } } @@ -453,11 +784,13 @@ class HttpClientImpl extends HttpClient { private static class SelectorAttachment { private final SelectableChannel chan; private final Selector selector; - private final ArrayList<AsyncEvent> pending; + private final Set<AsyncEvent> pending; + private final static System.Logger debug = + Utils.getDebugLogger("SelectorAttachment"::toString, DEBUG); private int interestOps; SelectorAttachment(SelectableChannel chan, Selector selector) { - this.pending = new ArrayList<>(); + this.pending = new HashSet<>(); this.chan = chan; this.selector = selector; } @@ -506,23 +839,43 @@ class HttpClientImpl extends HttpClient { this.interestOps = newOps; SelectionKey key = chan.keyFor(selector); - if (newOps == 0) { + if (newOps == 0 && pending.isEmpty()) { key.cancel(); } else { - key.interestOps(newOps); + try { + key.interestOps(newOps); + } catch (CancelledKeyException x) { + // channel may have been closed + debug.log(Level.DEBUG, "key cancelled for " + chan); + abortPending(x); + } + } + } + + void abortPending(Throwable x) { + if (!pending.isEmpty()) { + AsyncEvent[] evts = pending.toArray(new AsyncEvent[0]); + pending.clear(); + IOException io = Utils.getIOException(x); + for (AsyncEvent event : evts) { + event.abort(io); + } } } } - @Override - public SSLContext sslContext() { - Utils.checkNetPermission("getSSLContext"); + /*package-private*/ SSLContext theSSLContext() { return sslContext; } @Override - public Optional<SSLParameters> sslParameters() { - return Optional.ofNullable(sslParams); + public SSLContext sslContext() { + return sslContext; + } + + @Override + public SSLParameters sslParameters() { + return Utils.copySSLParameters(sslParams); } @Override @@ -530,11 +883,15 @@ class HttpClientImpl extends HttpClient { return Optional.ofNullable(authenticator); } - @Override - public Executor executor() { + /*package-private*/ final Executor theExecutor() { return executor; } + @Override + public final Optional<Executor> executor() { + return isDefaultExecutor ? Optional.empty() : Optional.of(executor); + } + ConnectionPool connectionPool() { return connections; } @@ -546,19 +903,28 @@ class HttpClientImpl extends HttpClient { @Override - public Optional<CookieManager> cookieManager() { - return Optional.ofNullable(cookieManager); + public Optional<CookieHandler> cookieHandler() { + return Optional.ofNullable(cookieHandler); } @Override public Optional<ProxySelector> proxy() { - return Optional.ofNullable(this.proxySelector); + return this.userProxySelector; + } + + // Return the effective proxy that this client uses. + ProxySelector proxySelector() { + return proxySelector; } @Override - public WebSocket.Builder newWebSocketBuilder(URI uri, - WebSocket.Listener listener) { - return new BuilderImpl(this, uri, listener); + public WebSocket.Builder newWebSocketBuilder() { + // Make sure to pass the HttpClientFacade to the WebSocket builder. + // This will ensure that the facade is not released before the + // WebSocket has been created, at which point the pendingOperationCount + // will have been incremented by the DetachedConnectionChannel + // (see PlainHttpConnection.detachChannel()) + return new BuilderImpl(this.facade(), proxySelector); } @Override @@ -566,16 +932,21 @@ class HttpClientImpl extends HttpClient { return version; } - //private final HashMap<String, Boolean> http2NotSupported = new HashMap<>(); + String dbgString() { + return dbgTag; + } - boolean getHttp2Allowed() { - return version.equals(Version.HTTP_2); + @Override + public String toString() { + // Used by tests to get the client's id and compute the + // name of the SelectorManager thread. + return super.toString() + ("(" + id + ")"); } private void initFilters() { addFilter(AuthenticationFilter.class); addFilter(RedirectFilter.class); - if (this.cookieManager != null) { + if (this.cookieHandler != null) { addFilter(CookieFilter.class); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java index d81d3950374..66542f57587 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java @@ -25,15 +25,27 @@ package jdk.incubator.http; -import javax.net.ssl.SSLParameters; import java.io.Closeable; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; - -import jdk.incubator.http.internal.common.ByteBufferReference; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Flow; +import jdk.incubator.http.HttpClient.Version; +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter; +import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.Utils; +import static jdk.incubator.http.HttpClient.Version.HTTP_2; /** * Wraps socket channel layer and takes care of SSL also. @@ -42,75 +54,151 @@ import jdk.incubator.http.internal.common.ByteBufferReference; * PlainHttpConnection: regular direct TCP connection to server * PlainProxyConnection: plain text proxy connection * PlainTunnelingConnection: opens plain text (CONNECT) tunnel to server - * SSLConnection: TLS channel direct to server - * SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel + * AsyncSSLConnection: TLS channel direct to server + * AsyncSSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel */ abstract class HttpConnection implements Closeable { - enum Mode { - BLOCKING, - NON_BLOCKING, - ASYNC - } + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final static System.Logger DEBUG_LOGGER = Utils.getDebugLogger( + () -> "HttpConnection(SocketTube(?))", DEBUG); - protected Mode mode; - - // address we are connected to. Could be a server or a proxy + /** The address this connection is connected to. Could be a server or a proxy. */ final InetSocketAddress address; - final HttpClientImpl client; + private final HttpClientImpl client; + private final TrailingOperations trailingOperations; HttpConnection(InetSocketAddress address, HttpClientImpl client) { this.address = address; this.client = client; + trailingOperations = new TrailingOperations(); } - /** - * Public API to this class. addr is the ultimate destination. Any proxies - * etc are figured out from the request. Returns an instance of one of the - * following - * PlainHttpConnection - * PlainTunnelingConnection - * SSLConnection - * SSLTunnelConnection - * - * When object returned, connect() or connectAsync() must be called, which - * when it returns/completes, the connection is usable for requests. - */ - public static HttpConnection getConnection( - InetSocketAddress addr, HttpClientImpl client, HttpRequestImpl request) - { - return getConnectionImpl(addr, client, request, false); + private static final class TrailingOperations { + private final Map<CompletionStage<?>, Boolean> operations = + new IdentityHashMap<>(); + void add(CompletionStage<?> cf) { + synchronized(operations) { + cf.whenComplete((r,t)-> remove(cf)); + operations.put(cf, Boolean.TRUE); + } + } + boolean remove(CompletionStage<?> cf) { + synchronized(operations) { + return operations.remove(cf); + } + } } - /** - * Called specifically to get an async connection for HTTP/2 over SSL. - */ - public static HttpConnection getConnection(InetSocketAddress addr, - HttpClientImpl client, HttpRequestImpl request, boolean isHttp2) { - - return getConnectionImpl(addr, client, request, isHttp2); + final void addTrailingOperation(CompletionStage<?> cf) { + trailingOperations.add(cf); } - public abstract void connect() throws IOException, InterruptedException; +// final void removeTrailingOperation(CompletableFuture<?> cf) { +// trailingOperations.remove(cf); +// } + + final HttpClientImpl client() { + return client; + } + + //public abstract void connect() throws IOException, InterruptedException; public abstract CompletableFuture<Void> connectAsync(); - /** - * Returns whether this connection is connected to its destination - */ + /** Tells whether, or not, this connection is connected to its destination. */ abstract boolean connected(); + /** Tells whether, or not, this connection is secure ( over SSL ) */ abstract boolean isSecure(); + /** Tells whether, or not, this connection is proxied. */ abstract boolean isProxied(); - /** - * Completes when the first byte of the response is available to be read. - */ - abstract CompletableFuture<Void> whenReceivingResponse(); - + /** Tells whether, or not, this connection is open. */ final boolean isOpen() { - return channel().isOpen(); + return channel().isOpen() && + (connected() ? !getConnectionFlow().isFinished() : true); + } + + interface HttpPublisher extends FlowTube.TubePublisher { + void enqueue(List<ByteBuffer> buffers) throws IOException; + void enqueueUnordered(List<ByteBuffer> buffers) throws IOException; + void signalEnqueued() throws IOException; + } + + /** + * Returns the HTTP publisher associated with this connection. May be null + * if invoked before connecting. + */ + abstract HttpPublisher publisher(); + + /** + * Factory for retrieving HttpConnections. A connection can be retrieved + * from the connection pool, or a new one created if none available. + * + * The given {@code addr} is the ultimate destination. Any proxies, + * etc, are determined from the request. Returns a concrete instance which + * is one of the following: + * {@link PlainHttpConnection} + * {@link PlainTunnelingConnection} + * + * The returned connection, if not from the connection pool, must have its, + * connect() or connectAsync() method invoked, which ( when it completes + * successfully ) renders the connection usable for requests. + */ + public static HttpConnection getConnection(InetSocketAddress addr, + HttpClientImpl client, + HttpRequestImpl request, + Version version) { + HttpConnection c = null; + InetSocketAddress proxy = request.proxy(); + if (proxy != null && proxy.isUnresolved()) { + // The default proxy selector may select a proxy whose address is + // unresolved. We must resolve the address before connecting to it. + proxy = new InetSocketAddress(proxy.getHostString(), proxy.getPort()); + } + boolean secure = request.secure(); + ConnectionPool pool = client.connectionPool(); + + if (!secure) { + c = pool.getConnection(false, addr, proxy); + if (c != null && c.isOpen() /* may have been eof/closed when in the pool */) { + final HttpConnection conn = c; + DEBUG_LOGGER.log(Level.DEBUG, () -> conn.getConnectionFlow() + + ": plain connection retrieved from HTTP/1.1 pool"); + return c; + } else { + return getPlainConnection(addr, proxy, request, client); + } + } else { // secure + if (version != HTTP_2) { // only HTTP/1.1 connections are in the pool + c = pool.getConnection(true, addr, proxy); + } + if (c != null && c.isOpen()) { + final HttpConnection conn = c; + DEBUG_LOGGER.log(Level.DEBUG, () -> conn.getConnectionFlow() + + ": SSL connection retrieved from HTTP/1.1 pool"); + return c; + } else { + String[] alpn = null; + if (version == HTTP_2) { + alpn = new String[] { "h2", "http/1.1" }; + } + return getSSLConnection(addr, proxy, alpn, client); + } + } + } + + private static HttpConnection getSSLConnection(InetSocketAddress addr, + InetSocketAddress proxy, + String[] alpn, + HttpClientImpl client) { + if (proxy != null) + return new AsyncSSLTunnelConnection(addr, client, alpn, proxy); + else + return new AsyncSSLConnection(addr, client, alpn); } /* Returns either a plain HTTP connection or a plain tunnelling connection @@ -119,192 +207,54 @@ abstract class HttpConnection implements Closeable { InetSocketAddress proxy, HttpRequestImpl request, HttpClientImpl client) { - if (request.isWebSocket() && proxy != null) { + if (request.isWebSocket() && proxy != null) return new PlainTunnelingConnection(addr, proxy, client); - } else { - if (proxy == null) { - return new PlainHttpConnection(addr, client); - } else { - return new PlainProxyConnection(proxy, client); - } - } + + if (proxy == null) + return new PlainHttpConnection(addr, client); + else + return new PlainProxyConnection(proxy, client); } - private static HttpConnection getSSLConnection(InetSocketAddress addr, - InetSocketAddress proxy, HttpRequestImpl request, - String[] alpn, boolean isHttp2, HttpClientImpl client) - { - if (proxy != null) { - if (!isHttp2) { - return new SSLTunnelConnection(addr, client, proxy); - } else { - return new AsyncSSLTunnelConnection(addr, client, alpn, proxy); - } - } else if (!isHttp2) { - return new SSLConnection(addr, client, alpn); - } else { - return new AsyncSSLConnection(addr, client, alpn); - } - } - - /** - * Main factory method. Gets a HttpConnection, either cached or new if - * none available. - */ - private static HttpConnection getConnectionImpl(InetSocketAddress addr, - HttpClientImpl client, - HttpRequestImpl request, boolean isHttp2) - { - HttpConnection c = null; - InetSocketAddress proxy = request.proxy(client); - if (proxy != null && proxy.isUnresolved()) { - // The default proxy selector may select a proxy whose - // address is unresolved. We must resolve the address - // before using it to connect. - proxy = new InetSocketAddress(proxy.getHostString(), proxy.getPort()); - } - boolean secure = request.secure(); - ConnectionPool pool = client.connectionPool(); - String[] alpn = null; - - if (secure && isHttp2) { - alpn = new String[2]; - alpn[0] = "h2"; - alpn[1] = "http/1.1"; - } - - if (!secure) { - c = pool.getConnection(false, addr, proxy); - if (c != null) { - return c; - } else { - return getPlainConnection(addr, proxy, request, client); - } - } else { - if (!isHttp2) { // if http2 we don't cache connections - c = pool.getConnection(true, addr, proxy); - } - if (c != null) { - return c; - } else { - return getSSLConnection(addr, proxy, request, alpn, isHttp2, client); - } - } - } - - void returnToCache(HttpHeaders hdrs) { + void closeOrReturnToCache(HttpHeaders hdrs) { if (hdrs == null) { - // the connection was closed by server + // the connection was closed by server, eof close(); return; } if (!isOpen()) { return; } + HttpClientImpl client = client(); + if (client == null) { + close(); + return; + } ConnectionPool pool = client.connectionPool(); boolean keepAlive = hdrs.firstValue("Connection") .map((s) -> !s.equalsIgnoreCase("close")) .orElse(true); if (keepAlive) { + Log.logTrace("Returning connection to the pool: {0}", this); pool.returnToPool(this); } else { close(); } } - /** - * Also check that the number of bytes written is what was expected. This - * could be different if the buffer is user-supplied and its internal - * pointers were manipulated in a race condition. - */ - final void checkWrite(long expected, ByteBuffer buffer) throws IOException { - long written = write(buffer); - if (written != expected) { - throw new IOException("incorrect number of bytes written"); - } - } - - final void checkWrite(long expected, - ByteBuffer[] buffers, - int start, - int length) - throws IOException - { - long written = write(buffers, start, length); - if (written != expected) { - throw new IOException("incorrect number of bytes written"); - } - } - abstract SocketChannel channel(); final InetSocketAddress address() { return address; } - synchronized void configureMode(Mode mode) throws IOException { - this.mode = mode; - if (mode == Mode.BLOCKING) { - channel().configureBlocking(true); - } else { - channel().configureBlocking(false); - } - } - - synchronized Mode getMode() { - return mode; - } - abstract ConnectionPool.CacheKey cacheKey(); - // overridden in SSL only - SSLParameters sslParameters() { - return null; - } - - // Methods to be implemented for Plain TCP and SSL - - abstract long write(ByteBuffer[] buffers, int start, int number) - throws IOException; - - abstract long write(ByteBuffer buffer) throws IOException; - - // Methods to be implemented for Plain TCP (async mode) and AsyncSSL - - /** - * In {@linkplain Mode#ASYNC async mode}, this method puts buffers at the - * end of the send queue; Otherwise, it is equivalent to {@link - * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}. - * When in async mode, calling this method should later be followed by - * subsequent flushAsync invocation. - * That allows multiple threads to put buffers into the queue while some other - * thread is writing. - */ - abstract void writeAsync(ByteBufferReference[] buffers) throws IOException; - - /** - * In {@linkplain Mode#ASYNC async mode}, this method may put - * buffers at the beginning of send queue, breaking frames sequence and - * allowing to write these buffers before other buffers in the queue; - * Otherwise, it is equivalent to {@link - * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}. - * When in async mode, calling this method should later be followed by - * subsequent flushAsync invocation. - * That allows multiple threads to put buffers into the queue while some other - * thread is writing. - */ - abstract void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException; - - /** - * This method should be called after any writeAsync/writeAsyncUnordered - * invocation. - * If there is a race to flushAsync from several threads one thread - * (race winner) capture flush operation and write the whole queue content. - * Other threads (race losers) exits from the method (not blocking) - * and continue execution. - */ - abstract void flushAsync() throws IOException; +// // overridden in SSL only +// SSLParameters sslParameters() { +// return null; +// } /** * Closes this connection, by returning the socket to its connection pool. @@ -316,32 +266,142 @@ abstract class HttpConnection implements Closeable { abstract void shutdownOutput() throws IOException; - /** - * Puts position to limit and limit to capacity so we can resume reading - * into this buffer, but if required > 0 then limit may be reduced so that - * no more than required bytes are read next time. - */ - static void resumeChannelRead(ByteBuffer buf, int required) { - int limit = buf.limit(); - buf.position(limit); - int capacity = buf.capacity() - limit; - if (required > 0 && required < capacity) { - buf.limit(limit + required); - } else { - buf.limit(buf.capacity()); + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. + abstract static class DetachedConnectionChannel implements Closeable { + DetachedConnectionChannel() {} + abstract SocketChannel channel(); + abstract long write(ByteBuffer[] buffers, int start, int number) + throws IOException; + abstract void shutdownInput() throws IOException; + abstract void shutdownOutput() throws IOException; + abstract ByteBuffer read() throws IOException; + @Override + public abstract void close(); + @Override + public String toString() { + return this.getClass().getSimpleName() + ": " + channel().toString(); } } - final ByteBuffer read() throws IOException { - ByteBuffer b = readImpl(); - return b; + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. + abstract DetachedConnectionChannel detachChannel(); + + abstract FlowTube getConnectionFlow(); + + /** + * A publisher that makes it possible to publish (write) + * ordered (normal priority) and unordered (high priority) + * buffers downstream. + */ + final class PlainHttpPublisher implements HttpPublisher { + final Object reading; + PlainHttpPublisher() { + this(new Object()); + } + PlainHttpPublisher(Object readingLock) { + this.reading = readingLock; + } + final ConcurrentLinkedDeque<List<ByteBuffer>> queue = new ConcurrentLinkedDeque<>(); + volatile Flow.Subscriber<? super List<ByteBuffer>> subscriber; + volatile HttpWriteSubscription subscription; + final SequentialScheduler writeScheduler = + new SequentialScheduler(this::flushTask); + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + synchronized (reading) { + //assert this.subscription == null; + //assert this.subscriber == null; + if (subscription == null) { + subscription = new HttpWriteSubscription(); + } + this.subscriber = subscriber; + } + // TODO: should we do this in the flow? + subscriber.onSubscribe(subscription); + signal(); + } + + void flushTask(DeferredCompleter completer) { + try { + HttpWriteSubscription sub = subscription; + if (sub != null) sub.flush(); + } finally { + completer.complete(); + } + } + + void signal() { + writeScheduler.runOrSchedule(); + } + + final class HttpWriteSubscription implements Flow.Subscription { + final Demand demand = new Demand(); + + @Override + public void request(long n) { + if (n <= 0) throw new IllegalArgumentException("non-positive request"); + demand.increase(n); + debug.log(Level.DEBUG, () -> "HttpPublisher: got request of " + + n + " from " + + getConnectionFlow()); + writeScheduler.runOrSchedule(); + } + + @Override + public void cancel() { + debug.log(Level.DEBUG, () -> "HttpPublisher: cancelled by " + + getConnectionFlow()); + } + + void flush() { + while (!queue.isEmpty() && demand.tryDecrement()) { + List<ByteBuffer> elem = queue.poll(); + debug.log(Level.DEBUG, () -> "HttpPublisher: sending " + + Utils.remaining(elem) + " bytes (" + + elem.size() + " buffers) to " + + getConnectionFlow()); + subscriber.onNext(elem); + } + } + } + + @Override + public void enqueue(List<ByteBuffer> buffers) throws IOException { + queue.add(buffers); + int bytes = buffers.stream().mapToInt(ByteBuffer::remaining).sum(); + debug.log(Level.DEBUG, "added %d bytes to the write queue", bytes); + } + + @Override + public void enqueueUnordered(List<ByteBuffer> buffers) throws IOException { + // Unordered frames are sent before existing frames. + int bytes = buffers.stream().mapToInt(ByteBuffer::remaining).sum(); + queue.addFirst(buffers); + debug.log(Level.DEBUG, "inserted %d bytes in the write queue", bytes); + } + + @Override + public void signalEnqueued() throws IOException { + debug.log(Level.DEBUG, "signalling the publisher of the write queue"); + signal(); + } } - /* - * Returns a ByteBuffer with the data available at the moment, or null if - * reached EOF. - */ - protected abstract ByteBuffer readImpl() throws IOException; + String dbgTag = null; + final String dbgString() { + FlowTube flow = getConnectionFlow(); + String tag = dbgTag; + if (tag == null && flow != null) { + dbgTag = tag = this.getClass().getSimpleName() + "(" + flow + ")"; + } else if (tag == null) { + tag = this.getClass().getSimpleName() + "(?)"; + } + return tag; + } @Override public String toString() { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java index b443dd07aa8..a66908dcc1c 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -29,55 +29,136 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalLong; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.Objects.requireNonNull; /** * A read-only view of a set of HTTP headers. * {@Incubating} * + * <p> The methods of this class ( that accept a String header name ), and the + * Map returned by the {@linkplain #map() map} method, operate without regard to + * case when retrieving the header value. + * + * <p> HttpHeaders instances are immutable. + * * @since 9 */ -public interface HttpHeaders { +public abstract class HttpHeaders { /** - * Returns an {@link java.util.Optional} containing the first value of the - * given named (and possibly multi-valued) header. If the header is not - * present, then the returned {@code Optional} is empty. + * Creates an HttpHeaders. + */ + protected HttpHeaders() {} + + /** + * Returns an {@link Optional} containing the first value of the given named + * (and possibly multi-valued) header. If the header is not present, then + * the returned {@code Optional} is empty. + * + * @implSpec + * The default implementation invokes + * {@code allValues(name).stream().findFirst()} * * @param name the header name * @return an {@code Optional<String>} for the first named value */ - public Optional<String> firstValue(String name); + public Optional<String> firstValue(String name) { + return allValues(name).stream().findFirst(); + } /** - * Returns an {@link java.util.OptionalLong} containing the first value of the - * named header field. If the header is not - * present, then the Optional is empty. If the header is present but - * contains a value that does not parse as a {@code Long} value, then an - * exception is thrown. + * Returns an {@link OptionalLong} containing the first value of the + * named header field. If the header is not present, then the Optional is + * empty. If the header is present but contains a value that does not parse + * as a {@code Long} value, then an exception is thrown. + * + * @implSpec + * The default implementation invokes + * {@code allValues(name).stream().mapToLong(Long::valueOf).findFirst()} * * @param name the header name * @return an {@code OptionalLong} * @throws NumberFormatException if a value is found, but does not parse as * a Long */ - public OptionalLong firstValueAsLong(String name); + public OptionalLong firstValueAsLong(String name) { + return allValues(name).stream().mapToLong(Long::valueOf).findFirst(); + } /** * Returns an unmodifiable List of all of the values of the given named * header. Always returns a List, which may be empty if the header is not * present. * + * @implSpec + * The default implementation invokes, among other things, the + * {@code map().get(name)} to retrieve the list of header values. + * * @param name the header name * @return a List of String values */ - public List<String> allValues(String name); + public List<String> allValues(String name) { + requireNonNull(name); + List<String> values = map().get(name); + // Making unmodifiable list out of empty in order to make a list which + // throws UOE unconditionally + return values != null ? values : unmodifiableList(emptyList()); + } /** - * Returns an unmodifiable multi Map view of this HttpHeaders. This - * interface should only be used when it is required to iterate over the - * entire set of headers. + * Returns an unmodifiable multi Map view of this HttpHeaders. * * @return the Map */ - public Map<String,List<String>> map(); + public abstract Map<String, List<String>> map(); + + /** + * Tests this HTTP headers instance for equality with the given object. + * + * <p> If the given object is not an {@code HttpHeaders} then this + * method returns {@code false}. Two HTTP headers are equal if each + * of their corresponding {@linkplain #map() maps} are equal. + * + * <p> This method satisfies the general contract of the {@link + * Object#equals(Object) Object.equals} method. + * + * @param obj the object to which this object is to be compared + * @return {@code true} if, and only if, the given object is an {@code + * HttpHeaders} that is equal to this HTTP headers + */ + public final boolean equals(Object obj) { + if (!(obj instanceof HttpHeaders)) + return false; + HttpHeaders that = (HttpHeaders)obj; + return this.map().equals(that.map()); + } + + /** + * Computes a hash code for this HTTP headers instance. + * + * <p> The hash code is based upon the components of the HTTP headers + * {@linkplain #map() map}, and satisfies the general contract of the + * {@link Object#hashCode Object.hashCode} method. + * + * @return the hash-code value for this HTTP headers + */ + public final int hashCode() { + return map().hashCode(); + } + + /** + * Returns this HTTP headers as a string. + * + * @return a string describing the HTTP headers + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" "); + sb.append(map()); + sb.append(" }"); + return sb.toString(); + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java index 8226cc2ac69..c1636d9f8f8 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java @@ -28,34 +28,41 @@ package jdk.incubator.http; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URI; +import java.net.URLPermission; import java.nio.ByteBuffer; -import java.nio.charset.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.time.Duration; import java.util.Iterator; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.function.Supplier; +import static java.nio.charset.StandardCharsets.UTF_8; /** * Represents one HTTP request which can be sent to a server. * {@Incubating } * - * <p> {@code HttpRequest}s are built from {@code HttpRequest} - * {@link HttpRequest.Builder builder}s. {@code HttpRequest} builders are - * obtained by calling {@link HttpRequest#newBuilder(java.net.URI) - * HttpRequest.newBuilder}. - * A request's {@link java.net.URI}, headers and body can be set. Request bodies - * are provided through a {@link BodyProcessor} object supplied to the - * {@link Builder#DELETE(jdk.incubator.http.HttpRequest.BodyProcessor) DELETE}, - * {@link Builder#POST(jdk.incubator.http.HttpRequest.BodyProcessor) POST} or - * {@link Builder#PUT(jdk.incubator.http.HttpRequest.BodyProcessor) PUT} methods. + * <p> {@code HttpRequest} instances are built from {@code HttpRequest} + * {@linkplain HttpRequest.Builder builders}. {@code HttpRequest} builders + * are obtained by calling {@link HttpRequest#newBuilder(URI) HttpRequest.newBuilder}. + * A request's {@linkplain URI}, headers and body can be set. Request bodies are + * provided through a {@link BodyPublisher} object supplied to the + * {@link Builder#DELETE(BodyPublisher) DELETE}, + * {@link Builder#POST(BodyPublisher) POST} or + * {@link Builder#PUT(BodyPublisher) PUT} methods. * {@link Builder#GET() GET} does not take a body. Once all required * parameters have been set in the builder, {@link Builder#build() } is called - * to return the {@code HttpRequest}. Builders can also be copied - * and modified multiple times in order to build multiple related requests that - * differ in some parameters. + * to return the {@code HttpRequest}. Builders can also be copied and modified + * multiple times in order to build multiple related requests that differ in + * some parameters. * * <p> Two simple, example HTTP interactions are shown below: * <pre> @@ -79,7 +86,7 @@ import java.util.function.Supplier; * HttpRequest * .newBuilder(new URI("http://www.foo.com/")) * .headers("Foo", "foovalue", "Bar", "barvalue") - * .POST(BodyProcessor.fromString("Hello world")) + * .POST(BodyPublisher.fromString("Hello world")) * .build(), * BodyHandler.asFile(Paths.get("/path")) * ); @@ -87,6 +94,7 @@ import java.util.function.Supplier; * Path body = response.body(); // should be "/path" * } * </pre> + * * <p> The request is sent and the response obtained by calling one of the * following methods in {@link HttpClient}. * <ul><li>{@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler)} blocks @@ -95,7 +103,7 @@ import java.util.function.Supplier; * request and receives the response asynchronously. Returns immediately with a * {@link java.util.concurrent.CompletableFuture CompletableFuture}<{@link * HttpResponse}>.</li> - * <li>{@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor) } + * <li>{@link HttpClient#sendAsync(HttpRequest, HttpResponse.MultiSubscriber) } * sends the request asynchronously, expecting multiple responses. This * capability is of most relevance to HTTP/2 server push, but can be used for * single responses (HTTP/1.1 or HTTP/2) also.</li> @@ -109,34 +117,36 @@ import java.util.function.Supplier; * * <p> <b>Request bodies</b> * - * <p> Request bodies are sent using one of the request processor implementations - * below provided in {@link HttpRequest.BodyProcessor}, or else a custom implementation can be - * used. + * <p> Request bodies can be sent using one of the convenience request publisher + * implementations below, provided in {@link BodyPublisher}. Alternatively, a + * custom Publisher implementation can be used. * <ul> - * <li>{@link BodyProcessor#fromByteArray(byte[]) fromByteArray(byte[])} from byte array</li> - * <li>{@link BodyProcessor#fromByteArrays(Iterable) fromByteArrays(Iterable)} + * <li>{@link BodyPublisher#fromByteArray(byte[]) fromByteArray(byte[])} from byte array</li> + * <li>{@link BodyPublisher#fromByteArrays(Iterable) fromByteArrays(Iterable)} * from an Iterable of byte arrays</li> - * <li>{@link BodyProcessor#fromFile(java.nio.file.Path) fromFile(Path)} from the file located + * <li>{@link BodyPublisher#fromFile(java.nio.file.Path) fromFile(Path)} from the file located * at the given Path</li> - * <li>{@link BodyProcessor#fromString(java.lang.String) fromString(String)} from a String </li> - * <li>{@link BodyProcessor#fromInputStream(Supplier) fromInputStream}({@link Supplier}< + * <li>{@link BodyPublisher#fromString(java.lang.String) fromString(String)} from a String </li> + * <li>{@link BodyPublisher#fromInputStream(Supplier) fromInputStream}({@link Supplier}< * {@link InputStream}>) from an InputStream obtained from a Supplier</li> - * <li>{@link BodyProcessor#noBody() } no request body is sent</li> + * <li>{@link BodyPublisher#noBody() } no request body is sent</li> * </ul> * * <p> <b>Response bodies</b> * - * <p>Responses bodies are handled at two levels. When sending the request, - * a response body handler is specified. This is a function ({@link HttpResponse.BodyHandler}) - * which will be called with the response status code and headers, once these are received. This - * function is then expected to return a {@link HttpResponse.BodyProcessor} - * {@code <T>} which is then used to read the response body converting it - * into an instance of T. After this occurs, the response becomes - * available in a {@link HttpResponse} and {@link HttpResponse#body()} can then - * be called to obtain the body. Some implementations and examples of usage of both {@link - * HttpResponse.BodyProcessor} and {@link HttpResponse.BodyHandler} - * are provided in {@link HttpResponse}: - * <p><b>Some of the pre-defined body handlers</b><br> + * <p> Responses bodies are handled at two levels. When sending the request, + * a response body handler is specified. This is a function ({@linkplain + * HttpResponse.BodyHandler}) which will be called with the response status code + * and headers, once they are received. This function is then expected to return + * a {@link HttpResponse.BodySubscriber}{@code <T>} which is then used to read + * the response body, converting it into an instance of T. After this occurs, + * the response becomes available in a {@link HttpResponse}, and {@link + * HttpResponse#body()} can then be called to obtain the actual body. Some + * implementations and examples of usage of both {@link + * HttpResponse.BodySubscriber} and {@link HttpResponse.BodyHandler} are + * provided in {@link HttpResponse}: + * + * <p> <b>Some of the pre-defined body handlers</b><br> * <ul> * <li>{@link HttpResponse.BodyHandler#asByteArray() BodyHandler.asByteArray()} * stores the body in a byte array</li> @@ -152,8 +162,8 @@ import java.util.function.Supplier; * * <p> With HTTP/2 it is possible for a server to return a main response and zero * or more additional responses (known as server pushes) to a client-initiated - * request. These are handled using a special response processor called {@link - * HttpResponse.MultiProcessor}. + * request. These are handled using a special response subscriber called {@link + * HttpResponse.MultiSubscriber}. * * <p> <b>Blocking/asynchronous behavior and thread usage</b> * @@ -161,29 +171,38 @@ import java.util.function.Supplier; * <i>asynchronous</i>. {@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler) } * blocks the calling thread until the request has been sent and the response received. * - * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)} is asynchronous and returns - * immediately with a {@link java.util.concurrent.CompletableFuture}<{@link - * HttpResponse}> and when this object completes (in a background thread) the - * response has been received. + * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)} is + * asynchronous and returns immediately with a {@link CompletableFuture}<{@link + * HttpResponse}> and when this object completes (possibly in a different + * thread) the response has been received. * - * <p> {@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor)} + * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.MultiSubscriber)} * is the variant for multi responses and is also asynchronous. * - * <p> {@code CompletableFuture}s can be combined in different ways to declare the - * dependencies among several asynchronous tasks, while allowing for the maximum - * level of parallelism to be utilized. + * <p> Instances of {@code CompletableFuture} can be combined in different ways + * to declare the dependencies among several asynchronous tasks, while allowing + * for the maximum level of parallelism to be utilized. * - * <p> <b>Security checks</b> + * <p> <a id="securitychecks"></a><b>Security checks</b></a> * * <p> If a security manager is present then security checks are performed by - * the sending methods. A {@link java.net.URLPermission} or {@link java.net.SocketPermission} is required to - * access any destination origin server and proxy server utilised. {@code URLPermission}s - * should be preferred in policy files over {@code SocketPermission}s given the more - * limited scope of {@code URLPermission}. Permission is always implicitly granted to a - * system's default proxies. The {@code URLPermission} form used to access proxies uses - * a method parameter of {@code "CONNECT"} (for all kinds of proxying) and a url string - * of the form {@code "socket://host:port"} where host and port specify the proxy's - * address. + * the HTTP Client's sending methods. An appropriate {@link URLPermission} is + * required to access the destination server, and proxy server if one has + * been configured. The {@code URLPermission} form used to access proxies uses a + * method parameter of {@code "CONNECT"} (for all kinds of proxying) and a URL + * string of the form {@code "socket://host:port"} where host and port specify + * the proxy's address. + * + * <p> In this implementation, if an explicit {@linkplain + * HttpClient.Builder#executor(Executor) executor} has not been set for an + * {@code HttpClient}, and a security manager has been installed, then the + * default executor will execute asynchronous and dependent tasks in a context + * that is granted no permissions. Custom {@linkplain HttpRequest.BodyPublisher + * request body publishers}, {@linkplain HttpResponse.BodyHandler response body + * handlers}, {@linkplain HttpResponse.BodySubscriber response body subscribers}, + * and {@linkplain WebSocket.Listener WebSocket Listeners}, if executing + * operations that require privileges, should do so within an appropriate + * {@linkplain AccessController#doPrivileged(PrivilegedAction) privileged context}. * * <p> <b>Examples</b> * <pre>{@code @@ -193,7 +212,7 @@ import java.util.function.Supplier; * * HttpRequest request = HttpRequest * .newBuilder(new URI("http://www.foo.com/")) - * .POST(BodyProcessor.fromString("Hello world")) + * .POST(BodyPublisher.fromString("Hello world")) * .build(); * * HttpResponse<Path> response = @@ -239,8 +258,8 @@ import java.util.function.Supplier; * // Use File.exists() to check whether file was successfully downloaded * } * </pre> - * <p> - * Unless otherwise stated, {@code null} parameter values will cause methods + * + * <p> Unless otherwise stated, {@code null} parameter values will cause methods * of this class to throw {@code NullPointerException}. * * @since 9 @@ -253,17 +272,18 @@ public abstract class HttpRequest { protected HttpRequest() {} /** - * A builder of {@link HttpRequest}s. + * A builder of {@linkplain HttpRequest HTTP Requests}. * {@Incubating} * - * <p> {@code HttpRequest.Builder}s are created by calling {@link + * <p> Instances of {@code HttpRequest.Builder} are created by calling {@link * HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}. * * <p> Each of the setter methods in this class modifies the state of the * builder and returns <i>this</i> (ie. the same instance). The methods are * not synchronized and should not be called from multiple threads without * external synchronization. - * <p>Note, that not all request headers may be set by user code. Some are + * + * <p> Note, that not all request headers may be set by user code. Some are * restricted for security reasons and others such as the headers relating * to authentication, redirection and cookie management are managed by * specific APIs rather than through directly user set headers. @@ -286,16 +306,17 @@ public abstract class HttpRequest { * @param uri the request URI * @return this request builder * @throws IllegalArgumentException if the {@code URI} scheme is not - * supported. + * supported */ public abstract Builder uri(URI uri); /** - * Request server to acknowledge request before sending request - * body. This is disabled by default. If enabled, the server is requested - * to send an error response or a {@code 100 Continue} response before the client - * sends the request body. This means the request processor for the - * request will not be invoked until this interim response is received. + * Requests the server to acknowledge the request before sending the + * body. This is disabled by default. If enabled, the server is + * requested to send an error response or a {@code 100 Continue} + * response before the client sends the request body. This means the + * request publisher for the request will not be invoked until this + * interim response is received. * * @param enable {@code true} if Expect continue to be sent * @return this request builder @@ -303,11 +324,12 @@ public abstract class HttpRequest { public abstract Builder expectContinue(boolean enable); /** - * Sets the preferred {@link HttpClient.Version} for this - * request. The corresponding {@link HttpResponse} should be checked - * for the version that was used. If the version is not set - * in a request, then the version requested will be that of the - * sending {@link HttpClient}. + * Sets the preferred {@link HttpClient.Version} for this request. + * + * <p> The corresponding {@link HttpResponse} should be checked for the + * version that was actually used. If the version is not set in a + * request, then the version requested will be that of the sending + * {@link HttpClient}. * * @param version the HTTP protocol version requested * @return this request builder @@ -316,33 +338,31 @@ public abstract class HttpRequest { /** * Adds the given name value pair to the set of headers for this request. + * The given value is added to the list of values for that name. * * @param name the header name * @param value the header value * @return this request builder + * @throws IllegalArgumentException if the header name or value is not + * valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.2"> + * RFC 7230 section-3.2</a> */ public abstract Builder header(String name, String value); -// /** -// * Overrides the {@code ProxySelector} set on the request's client for this -// * request. -// * -// * @param proxy the ProxySelector to use -// * @return this request builder -// */ -// public abstract Builder proxy(ProxySelector proxy); - /** * Adds the given name value pairs to the set of headers for this - * request. The supplied {@code String}s must alternate as names and values. + * request. The supplied {@code String} instances must alternate as + * header names and header values. + * To add several values to the same name then the same name must + * be supplied with each new value. * - * @param headers the list of String name value pairs + * @param headers the list of name value pairs * @return this request builder - * @throws IllegalArgumentException if there is an odd number of - * parameters + * @throws IllegalArgumentException if there are an odd number of + * parameters, or if a header name or value is not valid, see + * <a href="https://tools.ietf.org/html/rfc7230#section-3.2"> + * RFC 7230 section-3.2</a> */ - // TODO (spec): consider signature change - // public abstract Builder headers(java.util.Map.Entry<String,String>... headers); public abstract Builder headers(String... headers); /** @@ -358,6 +378,7 @@ public abstract class HttpRequest { * * @param duration the timeout duration * @return this request builder + * @throws IllegalArgumentException if the duration is non-positive */ public abstract Builder timeout(Duration duration); @@ -368,11 +389,15 @@ public abstract class HttpRequest { * @param name the header name * @param value the header value * @return this request builder + * @throws IllegalArgumentException if the header name or value is not valid, + * see <a href="https://tools.ietf.org/html/rfc7230#section-3.2"> + * RFC 7230 section-3.2</a> */ public abstract Builder setHeader(String name, String value); /** * Sets the request method of this builder to GET. + * This is the default. * * @return a {@code HttpRequest} */ @@ -380,57 +405,62 @@ public abstract class HttpRequest { /** * Sets the request method of this builder to POST and sets its - * request body processor to the given value. + * request body publisher to the given value. * - * @param body the body processor + * @param bodyPublisher the body publisher * * @return a {@code HttpRequest} */ - public abstract Builder POST(BodyProcessor body); + public abstract Builder POST(BodyPublisher bodyPublisher); /** * Sets the request method of this builder to PUT and sets its - * request body processor to the given value. + * request body publisher to the given value. * - * @param body the body processor + * @param bodyPublisher the body publisher * * @return a {@code HttpRequest} */ - public abstract Builder PUT(BodyProcessor body); + public abstract Builder PUT(BodyPublisher bodyPublisher); /** * Sets the request method of this builder to DELETE and sets its - * request body processor to the given value. + * request body publisher to the given value. * - * @param body the body processor + * @param bodyPublisher the body publisher * * @return a {@code HttpRequest} */ - public abstract Builder DELETE(BodyProcessor body); + public abstract Builder DELETE(BodyPublisher bodyPublisher); /** * Sets the request method and request body of this builder to the * given values. * - * @param body the body processor + * @apiNote The {@linkplain BodyPublisher#noBody() noBody} request + * body publisher can be used where no request body is required or + * appropriate. + * * @param method the method to use + * @param bodyPublisher the body publisher * @return a {@code HttpRequest} - * @throws IllegalArgumentException if an unrecognized method is used + * @throws IllegalArgumentException if the method is unrecognised */ - public abstract Builder method(String method, BodyProcessor body); + public abstract Builder method(String method, BodyPublisher bodyPublisher); /** * Builds and returns a {@link HttpRequest}. * * @return the request + * @throws IllegalStateException if a URI has not been set */ public abstract HttpRequest build(); /** - * Returns an exact duplicate copy of this {@code Builder} based on current - * state. The new builder can then be modified independently of this - * builder. + * Returns an exact duplicate copy of this {@code Builder} based on + * current state. The new builder can then be modified independently of + * this builder. * * @return an exact copy of this Builder */ @@ -458,14 +488,13 @@ public abstract class HttpRequest { } /** - * Returns an {@code Optional} containing the {@link BodyProcessor} - * set on this request. If no {@code BodyProcessor} was set in the - * requests's builder, then the {@code Optional} is empty. + * Returns an {@code Optional} containing the {@link BodyPublisher} set on + * this request. If no {@code BodyPublisher} was set in the requests's + * builder, then the {@code Optional} is empty. * - * @return an {@code Optional} containing this request's - * {@code BodyProcessor} + * @return an {@code Optional} containing this request's {@code BodyPublisher} */ - public abstract Optional<BodyProcessor> bodyProcessor(); + public abstract Optional<BodyPublisher> bodyPublisher(); /** * Returns the request method for this request. If not set explicitly, @@ -476,11 +505,13 @@ public abstract class HttpRequest { public abstract String method(); /** - * Returns the duration for this request. + * Returns an {@code Optional} containing this request's timeout duration. + * If the timeout duration was not set in the request's builder, then the + * {@code Optional} is empty. * - * @return this requests duration + * @return an {@code Optional} containing this request's timeout duration */ - public abstract Duration duration(); + public abstract Optional<Duration> timeout(); /** * Returns this request's {@link HttpRequest.Builder#expectContinue(boolean) @@ -517,140 +548,203 @@ public abstract class HttpRequest { */ public abstract HttpHeaders headers(); - /** - * A request body handler which sends no request body. + * Tests this HTTP request instance for equality with the given object. * - * @return a BodyProcessor + * <p> If the given object is not an {@code HttpRequest} then this + * method returns {@code false}. Two HTTP requests are equal if their URI, + * method, and headers fields are all equal. + * + * <p> This method satisfies the general contract of the {@link + * Object#equals(Object) Object.equals} method. + * + * @param obj the object to which this object is to be compared + * @return {@code true} if, and only if, the given object is an {@code + * HttpRequest} that is equal to this HTTP request */ - public static BodyProcessor noBody() { - return new RequestProcessors.EmptyProcessor(); + @Override + public final boolean equals(Object obj) { + if (! (obj instanceof HttpRequest)) + return false; + HttpRequest that = (HttpRequest)obj; + if (!that.method().equals(this.method())) + return false; + if (!that.uri().equals(this.uri())) + return false; + if (!that.headers().equals(this.headers())) + return false; + return true; } /** - * A processor which converts high level Java objects into flows of - * {@link java.nio.ByteBuffer}s suitable for sending as request bodies. + * Computes a hash code for this HTTP request instance. + * + * <p> The hash code is based upon the HTTP request's URI, method, and + * header components, and satisfies the general contract of the + * {@link Object#hashCode Object.hashCode} method. + * + * @return the hash-code value for this HTTP request + */ + public final int hashCode() { + return method().hashCode() + + uri().hashCode() + + headers().hashCode(); + } + + /** + * A Publisher which converts high level Java objects into flows of + * byte buffers suitable for sending as request bodies. * {@Incubating} - * <p> - * {@code BodyProcessor}s implement {@link Flow.Publisher} which means they - * act as a publisher of byte buffers. - * <p> - * The HTTP client implementation subscribes to the processor in - * order to receive the flow of outgoing data buffers. The normal semantics - * of {@link Flow.Subscriber} and {@link Flow.Publisher} are implemented - * by the library and expected from processor implementations. - * Each outgoing request results in one {@code Subscriber} subscribing to the - * {@code Publisher} in order to provide the sequence of {@code ByteBuffer}s containing - * the request body. {@code ByteBuffer}s must be allocated by the processor, - * and must not be accessed after being handed over to the library. - * These subscriptions complete normally when the request is fully - * sent, and can be canceled or terminated early through error. If a request + * + * <p> The {@code BodyPublisher} class implements {@link Flow.Publisher + * Flow.Publisher<ByteBuffer>} which means that a {@code BodyPublisher} + * acts as a publisher of {@linkplain ByteBuffer byte buffers}. + * + * <p> The HTTP client implementation subscribes to the publisher in order + * to receive the flow of outgoing data buffers. The normal semantics of + * {@link Flow.Subscriber} and {@link Flow.Publisher} are implemented by the + * library and are expected from publisher implementations. Each outgoing + * request results in one {@code Subscriber} subscribing to the {@code + * BodyPublisher} in order to provide the sequence of byte buffers + * containing the request body. + * Instances of {@code ByteBuffer} published by the publisher must be + * allocated by the publisher, and must not be accessed after being handed + * over to the library. + * These subscriptions complete normally when the request is fully sent, + * and can be canceled or terminated early through error. If a request * needs to be resent for any reason, then a new subscription is created * which is expected to generate the same data as before. + * + * <p> A publisher that reports a {@linkplain #contentLength() content + * length} of {@code 0} may not be subscribed to by the HTTP client + * implementation, as it has effectively no data to publish. */ - public interface BodyProcessor extends Flow.Publisher<ByteBuffer> { + public interface BodyPublisher extends Flow.Publisher<ByteBuffer> { /** - * Returns a request body processor whose body is the given {@code String}, - * converted using the {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} + * Returns a request body publisher whose body is the given {@code + * String}, converted using the {@link StandardCharsets#UTF_8 UTF_8} * character set. * * @param body the String containing the body - * @return a BodyProcessor + * @return a BodyPublisher */ - static BodyProcessor fromString(String body) { - return fromString(body, StandardCharsets.UTF_8); + static BodyPublisher fromString(String body) { + return fromString(body, UTF_8); } /** - * Returns a request body processor whose body is the given {@code String}, converted - * using the given character set. + * Returns a request body publisher whose body is the given {@code + * String}, converted using the given character set. * * @param s the String containing the body * @param charset the character set to convert the string to bytes - * @return a BodyProcessor + * @return a BodyPublisher */ - static BodyProcessor fromString(String s, Charset charset) { - return new RequestProcessors.StringProcessor(s, charset); + static BodyPublisher fromString(String s, Charset charset) { + return new RequestPublishers.StringPublisher(s, charset); } /** - * A request body processor that reads its data from an {@link java.io.InputStream}. - * A {@link Supplier} of {@code InputStream} is used in case the request needs - * to be sent again as the content is not buffered. The {@code Supplier} may return - * {@code null} on subsequent attempts in which case, the request fails. + * A request body publisher that reads its data from an {@link + * InputStream}. A {@link Supplier} of {@code InputStream} is used in + * case the request needs to be repeated, as the content is not buffered. + * The {@code Supplier} may return {@code null} on subsequent attempts, + * in which case the request fails. * * @param streamSupplier a Supplier of open InputStreams - * @return a BodyProcessor + * @return a BodyPublisher */ // TODO (spec): specify that the stream will be closed - static BodyProcessor fromInputStream(Supplier<? extends InputStream> streamSupplier) { - return new RequestProcessors.InputStreamProcessor(streamSupplier); + static BodyPublisher fromInputStream(Supplier<? extends InputStream> streamSupplier) { + return new RequestPublishers.InputStreamPublisher(streamSupplier); } /** - * Returns a request body processor whose body is the given byte array. + * Returns a request body publisher whose body is the given byte array. * * @param buf the byte array containing the body - * @return a BodyProcessor + * @return a BodyPublisher */ - static BodyProcessor fromByteArray(byte[] buf) { - return new RequestProcessors.ByteArrayProcessor(buf); + static BodyPublisher fromByteArray(byte[] buf) { + return new RequestPublishers.ByteArrayPublisher(buf); } /** - * Returns a request body processor whose body is the content of the given byte - * array of {@code length} bytes starting from the specified + * Returns a request body publisher whose body is the content of the + * given byte array of {@code length} bytes starting from the specified * {@code offset}. * * @param buf the byte array containing the body * @param offset the offset of the first byte * @param length the number of bytes to use - * @return a BodyProcessor + * @return a BodyPublisher + * @throws IndexOutOfBoundsException if the sub-range is defined to be + * out-of-bounds */ - static BodyProcessor fromByteArray(byte[] buf, int offset, int length) { - return new RequestProcessors.ByteArrayProcessor(buf, offset, length); + static BodyPublisher fromByteArray(byte[] buf, int offset, int length) { + Objects.checkFromIndexSize(offset, length, buf.length); + return new RequestPublishers.ByteArrayPublisher(buf, offset, length); + } + + private static String pathForSecurityCheck(Path path) { + return path.toFile().getPath(); } /** - * A request body processor that takes data from the contents of a File. + * A request body publisher that takes data from the contents of a File. * * @param path the path to the file containing the body - * @return a BodyProcessor - * @throws java.io.FileNotFoundException if path not found + * @return a BodyPublisher + * @throws java.io.FileNotFoundException if the path is not found + * @throws SecurityException if a security manager has been installed + * and it denies {@link SecurityManager#checkRead(String) + * read access} to the given file */ - static BodyProcessor fromFile(Path path) throws FileNotFoundException { - return new RequestProcessors.FileProcessor(path); + static BodyPublisher fromFile(Path path) throws FileNotFoundException { + Objects.requireNonNull(path); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkRead(pathForSecurityCheck(path)); + if (Files.notExists(path)) + throw new FileNotFoundException(path + " not found"); + return new RequestPublishers.FilePublisher(path); } /** - * A request body processor that takes data from an {@code Iterable} of byte arrays. - * An {@link Iterable} is provided which supplies {@link Iterator} instances. - * Each attempt to send the request results in one invocation of the - * {@code Iterable} + * A request body publisher that takes data from an {@code Iterable} + * of byte arrays. An {@link Iterable} is provided which supplies + * {@link Iterator} instances. Each attempt to send the request results + * in one invocation of the {@code Iterable}. * * @param iter an Iterable of byte arrays - * @return a BodyProcessor + * @return a BodyPublisher */ - static BodyProcessor fromByteArrays(Iterable<byte[]> iter) { - return new RequestProcessors.IterableProcessor(iter); + static BodyPublisher fromByteArrays(Iterable<byte[]> iter) { + return new RequestPublishers.IterablePublisher(iter); } + + /** + * A request body publisher which sends no request body. + * + * @return a BodyPublisher which completes immediately and sends + * no request body. + */ + static BodyPublisher noBody() { + return new RequestPublishers.EmptyPublisher(); + } + /** * Returns the content length for this request body. May be zero - * if no request content being sent, greater than zero for a fixed - * length content, and less than zero for an unknown content length. + * if no request body being sent, greater than zero for a fixed + * length content, or less than zero for an unknown content length. * - * @return the content length for this request body if known + * This method may be invoked before the publisher is subscribed to. + * This method may be invoked more than once by the HTTP client + * implementation, and MUST return the same constant value each time. + * + * @return the content length for this request body, if known */ long contentLength(); - -// /** -// * Returns a used {@code ByteBuffer} to this request processor. When the -// * HTTP implementation has finished sending the contents of a buffer, -// * this method is called to return it to the processor for re-use. -// * -// * @param buffer a used ByteBuffer -// */ - //void returnBuffer(ByteBuffer buffer); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java index ed0426943b2..44d47beb672 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,11 +26,12 @@ package jdk.incubator.http; import java.net.URI; -import jdk.incubator.http.HttpRequest.BodyProcessor; import java.time.Duration; import java.util.Optional; -import static java.util.Objects.requireNonNull; +import jdk.incubator.http.HttpRequest.BodyPublisher; import jdk.incubator.http.internal.common.HttpHeadersImpl; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; import static jdk.incubator.http.internal.common.Utils.isValidName; import static jdk.incubator.http.internal.common.Utils.isValidValue; @@ -39,16 +40,13 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { private HttpHeadersImpl userHeaders; private URI uri; private String method; - //private HttpClient.Redirect followRedirects; private boolean expectContinue; - private HttpRequest.BodyProcessor body; + private BodyPublisher bodyPublisher; private volatile Optional<HttpClient.Version> version; - //private final HttpClientImpl client; - //private ProxySelector proxy; private Duration duration; public HttpRequestBuilderImpl(URI uri) { - //this.client = client; + requireNonNull(uri, "uri must be non-null"); checkURI(uri); this.uri = uri; this.userHeaders = new HttpHeadersImpl(); @@ -58,31 +56,66 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { public HttpRequestBuilderImpl() { this.userHeaders = new HttpHeadersImpl(); + this.method = "GET"; // default, as per spec this.version = Optional.empty(); } @Override public HttpRequestBuilderImpl uri(URI uri) { - requireNonNull(uri); + requireNonNull(uri, "uri must be non-null"); checkURI(uri); this.uri = uri; return this; } + private static IllegalArgumentException newIAE(String message, Object... args) { + return new IllegalArgumentException(format(message, args)); + } + private static void checkURI(URI uri) { - String scheme = uri.getScheme().toLowerCase(); - if (!scheme.equals("https") && !scheme.equals("http")) { - throw new IllegalArgumentException("invalid URI scheme"); + String scheme = uri.getScheme(); + if (scheme == null) + throw newIAE("URI with undefined scheme"); + scheme = scheme.toLowerCase(); + if (!(scheme.equals("https") || scheme.equals("http"))) { + throw newIAE("invalid URI scheme %s", scheme); + } + if (uri.getHost() == null) { + throw newIAE("unsupported URI %s", uri); } } -/* + @Override - public HttpRequestBuilderImpl followRedirects(HttpClient.Redirect follow) { - requireNonNull(follow); - this.followRedirects = follow; + public HttpRequestBuilderImpl copy() { + HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(this.uri); + b.userHeaders = this.userHeaders.deepCopy(); + b.method = this.method; + b.expectContinue = this.expectContinue; + b.bodyPublisher = bodyPublisher; + b.uri = uri; + b.duration = duration; + b.version = version; + return b; + } + + private void checkNameAndValue(String name, String value) { + requireNonNull(name, "name"); + requireNonNull(value, "value"); + if (!isValidName(name)) { + throw newIAE("invalid header name:", name); + } + if (!isValidValue(value)) { + throw newIAE("invalid header value:%s", value); + } + } + + @Override + public HttpRequestBuilderImpl setHeader(String name, String value) { + checkNameAndValue(name, value); + userHeaders.setHeader(name, value); return this; } -*/ + @Override public HttpRequestBuilderImpl header(String name, String value) { checkNameAndValue(name, value); @@ -93,8 +126,8 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { @Override public HttpRequestBuilderImpl headers(String... params) { requireNonNull(params); - if (params.length % 2 != 0) { - throw new IllegalArgumentException("wrong number of parameters"); + if (params.length == 0 || params.length % 2 != 0) { + throw newIAE("wrong number, %d, of parameters", params.length); } for (int i = 0; i < params.length; i += 2) { String name = params[i]; @@ -104,45 +137,6 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { return this; } - /* - @Override - public HttpRequestBuilderImpl proxy(ProxySelector proxy) { - requireNonNull(proxy); - this.proxy = proxy; - return this; - } -*/ - @Override - public HttpRequestBuilderImpl copy() { - HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(this.uri); - b.userHeaders = this.userHeaders.deepCopy(); - b.method = this.method; - //b.followRedirects = this.followRedirects; - b.expectContinue = this.expectContinue; - b.body = body; - b.uri = uri; - //b.proxy = proxy; - return b; - } - - @Override - public HttpRequestBuilderImpl setHeader(String name, String value) { - checkNameAndValue(name, value); - userHeaders.setHeader(name, value); - return this; - } - - private void checkNameAndValue(String name, String value) { - requireNonNull(name, "name"); - requireNonNull(value, "value"); - if (!isValidName(name)) { - throw new IllegalArgumentException("invalid header name"); - } - if (!isValidValue(value)) { - throw new IllegalArgumentException("invalid header value"); - } - } - @Override public HttpRequestBuilderImpl expectContinue(boolean enable) { expectContinue = enable; @@ -158,49 +152,60 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { HttpHeadersImpl headers() { return userHeaders; } - //HttpClientImpl client() {return client;} - URI uri() { return uri; } String method() { return method; } - //HttpClient.Redirect followRedirects() { return followRedirects; } - - //ProxySelector proxy() { return proxy; } - boolean expectContinue() { return expectContinue; } - public HttpRequest.BodyProcessor body() { return body; } + BodyPublisher bodyPublisher() { return bodyPublisher; } Optional<HttpClient.Version> version() { return version; } @Override - public HttpRequest.Builder GET() { return method("GET", null); } - - @Override - public HttpRequest.Builder POST(BodyProcessor body) { - return method("POST", body); + public HttpRequest.Builder GET() { + return method0("GET", null); } @Override - public HttpRequest.Builder DELETE(BodyProcessor body) { - return method("DELETE", body); + public HttpRequest.Builder POST(BodyPublisher body) { + return method0("POST", requireNonNull(body)); } @Override - public HttpRequest.Builder PUT(BodyProcessor body) { - return method("PUT", body); + public HttpRequest.Builder DELETE(BodyPublisher body) { + return method0("DELETE", requireNonNull(body)); } @Override - public HttpRequest.Builder method(String method, BodyProcessor body) { - this.method = requireNonNull(method); - this.body = body; + public HttpRequest.Builder PUT(BodyPublisher body) { + return method0("PUT", requireNonNull(body)); + } + + @Override + public HttpRequest.Builder method(String method, BodyPublisher body) { + requireNonNull(method); + if (method.equals("")) + throw newIAE("illegal method <empty string>"); + if (method.equals("CONNECT")) + throw newIAE("method CONNECT is not supported"); + return method0(method, requireNonNull(body)); + } + + private HttpRequest.Builder method0(String method, BodyPublisher body) { + assert method != null; + assert !method.equals("GET") ? body != null : true; + assert !method.equals(""); + this.method = method; + this.bodyPublisher = body; return this; } @Override public HttpRequest build() { + if (uri == null) + throw new IllegalStateException("uri is null"); + assert method != null; return new HttpRequestImpl(this); } @@ -213,6 +218,6 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder { return this; } - Duration duration() { return duration; } + Duration timeout() { return duration; } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java index eb6a1cdfd4e..9ebb29c5e24 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java @@ -30,10 +30,14 @@ import jdk.incubator.http.internal.websocket.WebSocketRequest; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.ProxySelector; import java.net.URI; import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.time.Duration; +import java.util.List; import java.util.Locale; import java.util.Optional; @@ -44,42 +48,50 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { private final HttpHeaders userHeaders; private final HttpHeadersImpl systemHeaders; private final URI uri; + private Proxy proxy; private InetSocketAddress authority; // only used when URI not specified private final String method; - final BodyProcessor requestProcessor; + final BodyPublisher requestPublisher; final boolean secure; final boolean expectContinue; private boolean isWebSocket; private AccessControlContext acc; - private final Duration duration; + private final Duration timeout; // may be null private final Optional<HttpClient.Version> version; + private static String userAgent() { + PrivilegedAction<String> pa = () -> System.getProperty("java.version"); + String version = AccessController.doPrivileged(pa); + return "Java-http-client/" + version; + } + + /** The value of the User-Agent header for all requests sent by the client. */ + public static final String USER_AGENT = userAgent(); + /** * Creates an HttpRequestImpl from the given builder. */ public HttpRequestImpl(HttpRequestBuilderImpl builder) { String method = builder.method(); - this.method = method == null? "GET" : method; + this.method = method == null ? "GET" : method; this.userHeaders = ImmutableHeaders.of(builder.headers().map(), ALLOWED_HEADERS); this.systemHeaders = new HttpHeadersImpl(); this.uri = builder.uri(); + assert uri != null; + this.proxy = null; this.expectContinue = builder.expectContinue(); this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); - if (builder.body() == null) { - this.requestProcessor = HttpRequest.noBody(); - } else { - this.requestProcessor = builder.body(); - } - this.duration = builder.duration(); + this.requestPublisher = builder.bodyPublisher(); // may be null + this.timeout = builder.timeout(); this.version = builder.version(); } /** * Creates an HttpRequestImpl from the given request. */ - public HttpRequestImpl(HttpRequest request) { + public HttpRequestImpl(HttpRequest request, ProxySelector ps, AccessControlContext acc) { String method = request.method(); - this.method = method == null? "GET" : method; + this.method = method == null ? "GET" : method; this.userHeaders = request.headers(); if (request instanceof HttpRequestImpl) { this.systemHeaders = ((HttpRequestImpl) request).systemHeaders; @@ -87,15 +99,25 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { } else { this.systemHeaders = new HttpHeadersImpl(); } + this.systemHeaders.setHeader("User-Agent", USER_AGENT); this.uri = request.uri(); + if (isWebSocket) { + // WebSocket determines and sets the proxy itself + this.proxy = ((HttpRequestImpl) request).proxy; + } else { + if (ps != null) + this.proxy = retrieveProxy(ps, uri); + else + this.proxy = null; + } this.expectContinue = request.expectContinue(); this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); - if (!request.bodyProcessor().isPresent()) { - this.requestProcessor = HttpRequest.noBody(); - } else { - this.requestProcessor = request.bodyProcessor().get(); + this.requestPublisher = request.bodyPublisher().orElse(null); + if (acc != null && requestPublisher instanceof RequestPublishers.FilePublisher) { + // Restricts the file publisher with the senders ACC, if any + ((RequestPublishers.FilePublisher)requestPublisher).setAccessControlContext(acc); } - this.duration = request.duration(); + this.timeout = request.timeout().orElse(null); this.version = request.version(); } @@ -108,30 +130,38 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { this.isWebSocket = other.isWebSocket; this.systemHeaders = other.systemHeaders; this.uri = uri; + this.proxy = other.proxy; this.expectContinue = other.expectContinue; this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); - this.requestProcessor = other.requestProcessor; + this.requestPublisher = other.requestPublisher; // may be null this.acc = other.acc; - this.duration = other.duration; + this.timeout = other.timeout; this.version = other.version(); } /* used for creating CONNECT requests */ - HttpRequestImpl(String method, HttpClientImpl client, - InetSocketAddress authority) { + HttpRequestImpl(String method, InetSocketAddress authority) { // TODO: isWebSocket flag is not specified, but the assumption is that // such a request will never be made on a connection that will be returned // to the connection pool (we might need to revisit this constructor later) this.method = method; this.systemHeaders = new HttpHeadersImpl(); this.userHeaders = ImmutableHeaders.empty(); - this.uri = URI.create("socket://" + authority.getHostString() + ":" + Integer.toString(authority.getPort()) + "/"); - this.requestProcessor = HttpRequest.noBody(); + this.uri = URI.create("socket://" + authority.getHostString() + ":" + + Integer.toString(authority.getPort()) + "/"); + this.proxy = null; + this.requestPublisher = null; this.authority = authority; this.secure = false; this.expectContinue = false; - this.duration = null; - this.version = Optional.of(client.version()); + this.timeout = null; + // The CONNECT request sent for tunneling is only used in two cases: + // 1. websocket, which only supports HTTP/1.1 + // 2. SSL tunneling through a HTTP/1.1 proxy + // In either case we do not want to upgrade the connection to the proxy. + // What we want to possibly upgrade is the tunneled connection to the + // target server (so not the CONNECT request itself) + this.version = Optional.of(HttpClient.Version.HTTP_1_1); } /** @@ -161,14 +191,14 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { StringBuilder sb = new StringBuilder(); sb.append(scheme).append("://").append(authority).append(path); this.uri = URI.create(sb.toString()); - + this.proxy = null; this.userHeaders = ImmutableHeaders.of(headers.map(), ALLOWED_HEADERS); this.systemHeaders = parent.systemHeaders; this.expectContinue = parent.expectContinue; this.secure = parent.secure; - this.requestProcessor = parent.requestProcessor; + this.requestPublisher = parent.requestPublisher; this.acc = parent.acc; - this.duration = parent.duration; + this.timeout = parent.timeout; this.version = parent.version; } @@ -193,19 +223,34 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { @Override public boolean expectContinue() { return expectContinue; } - InetSocketAddress proxy(HttpClientImpl client) { - ProxySelector ps = client.proxy().orElse(null); - if (ps == null) { - ps = client.proxy().orElse(null); + /** Retrieves the proxy, from the given ProxySelector, if there is one. */ + private static Proxy retrieveProxy(ProxySelector ps, URI uri) { + Proxy proxy = null; + List<Proxy> pl = ps.select(uri); + if (!pl.isEmpty()) { + Proxy p = pl.get(0); + if (p.type() == Proxy.Type.HTTP) + proxy = p; } - if (ps == null || method.equalsIgnoreCase("CONNECT")) { + return proxy; + } + + InetSocketAddress proxy() { + if (proxy == null || proxy.type() != Proxy.Type.HTTP + || method.equalsIgnoreCase("CONNECT")) { return null; } - return (InetSocketAddress)ps.select(uri).get(0).address(); + return (InetSocketAddress)proxy.address(); } boolean secure() { return secure; } + @Override + public void setProxy(Proxy proxy) { + assert isWebSocket; + this.proxy = proxy; + } + @Override public void isWebSocket(boolean is) { isWebSocket = is; @@ -215,15 +260,10 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { return isWebSocket; } -// /** Returns the follow-redirects setting for this request. */ -// @Override -// public jdk.incubator.http.HttpClient.Redirect followRedirects() { -// return followRedirects; -// } - @Override - public Optional<BodyProcessor> bodyProcessor() { - return Optional.of(requestProcessor); + public Optional<BodyPublisher> bodyPublisher() { + return requestPublisher == null ? Optional.empty() + : Optional.of(requestPublisher); } /** @@ -237,14 +277,10 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { public URI uri() { return uri; } @Override - public Duration duration() { - return duration; + public Optional<Duration> timeout() { + return timeout == null ? Optional.empty() : Optional.of(timeout); } -// HttpClientImpl client() { -// return client; -// } - HttpHeaders getUserHeaders() { return userHeaders; } HttpHeadersImpl getSystemHeaders() { return systemHeaders; } @@ -261,57 +297,24 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest { systemHeaders.setHeader(name, value); } -// @Override -// public <T> HttpResponse<T> -// response(HttpResponse.BodyHandler<T> responseHandler) -// throws IOException, InterruptedException -// { -// if (!sent.compareAndSet(false, true)) { -// throw new IllegalStateException("request already sent"); -// } -// MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler); -// return mex.response(); -// } -// -// @Override -// public <T> CompletableFuture<HttpResponse<T>> -// responseAsync(HttpResponse.BodyHandler<T> responseHandler) -// { -// if (!sent.compareAndSet(false, true)) { -// throw new IllegalStateException("request already sent"); -// } -// MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler); -// return mex.responseAsync(null) -// .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b); -// } -// -// @Override -// public <U, T> CompletableFuture<U> -// multiResponseAsync(HttpResponse.MultiProcessor<U, T> responseHandler) -// { -// if (!sent.compareAndSet(false, true)) { -// throw new IllegalStateException("request already sent"); -// } -// MultiExchange<U,T> mex = new MultiExchange<>(this, responseHandler); -// return mex.multiResponseAsync(); -// } - - public InetSocketAddress getAddress(HttpClientImpl client) { + InetSocketAddress getAddress() { URI uri = uri(); if (uri == null) { return authority(); } - int port = uri.getPort(); - if (port == -1) { + int p = uri.getPort(); + if (p == -1) { if (uri.getScheme().equalsIgnoreCase("https")) { - port = 443; + p = 443; } else { - port = 80; + p = 80; } } - String host = uri.getHost(); - if (proxy(client) == null) { - return new InetSocketAddress(host, port); + final String host = uri.getHost(); + final int port = p; + if (proxy() == null) { + PrivilegedAction<InetSocketAddress> pa = () -> new InetSocketAddress(host, port); + return AccessController.doPrivileged(pa); } else { return InetSocketAddress.createUnresolved(host, port); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java index f8aa46cef0d..70fa5d25273 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java @@ -26,20 +26,22 @@ package jdk.incubator.http; import java.io.IOException; -import java.io.UncheckedIOException; +import java.io.InputStream; import java.net.URI; -import jdk.incubator.http.ResponseProcessors.MultiFile; -import jdk.incubator.http.ResponseProcessors.MultiProcessorImpl; +import jdk.incubator.http.ResponseSubscribers.MultiSubscriberImpl; import static jdk.incubator.http.internal.common.Utils.unchecked; import static jdk.incubator.http.internal.common.Utils.charsetFrom; import java.nio.ByteBuffer; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; +import java.nio.channels.FileChannel; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.Map; +import java.security.AccessControlContext; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -52,7 +54,7 @@ import javax.net.ssl.SSLParameters; * Represents a response to a {@link HttpRequest}. * {@Incubating} * - * <p>A {@code HttpResponse} is available when the response status code and + * <p> A {@code HttpResponse} is available when the response status code and * headers have been received, and typically after the response body has also * been received. This depends on the response body handler provided when * sending the request. In all cases, the response body handler is invoked @@ -61,23 +63,24 @@ import javax.net.ssl.SSLParameters; * * <p> Methods are provided in this class for accessing the response headers, * and response body. - * <p> - * <b>Response handlers and processors</b> - * <p> - * Response bodies are handled at two levels. Application code supplies a response - * handler ({@link BodyHandler}) which may examine the response status code - * and headers, and which then returns a {@link BodyProcessor} to actually read - * (or discard) the body and convert it into some useful Java object type. The handler - * can return one of the pre-defined processor types, or a custom processor, or - * if the body is to be discarded, it can call {@link BodyProcessor#discard(Object) - * BodyProcessor.discard()} and return a processor which discards the response body. - * Static implementations of both handlers and processors are provided in - * {@link BodyHandler BodyHandler} and {@link BodyProcessor BodyProcessor} respectively. - * In all cases, the handler functions provided are convenience implementations - * which ignore the supplied status code and - * headers and return the relevant pre-defined {@code BodyProcessor}. - * <p> - * See {@link BodyHandler} for example usage. + * + * <p><b>Response handlers and subscribers</b> + * + * <p> Response bodies are handled at two levels. Application code supplies a + * response handler ({@link BodyHandler}) which may examine the response status + * code and headers, and which then returns a {@link BodySubscriber} to actually + * read (or discard) the body and convert it into some useful Java object type. + * The handler can return one of the pre-defined subscriber types, or a custom + * subscriber, or if the body is to be discarded it can call {@link + * BodySubscriber#discard(Object) discard} and return a subscriber which + * discards the response body. Static implementations of both handlers and + * subscribers are provided in {@linkplain BodyHandler BodyHandler} and + * {@linkplain BodySubscriber BodySubscriber} respectively. In all cases, the + * handler functions provided are convenience implementations which ignore the + * supplied status code and headers and return the relevant pre-defined {@code + * BodySubscriber}. + * + * <p> See {@link BodyHandler} for example usage. * * @param <T> the response body type * @since 9 @@ -97,19 +100,26 @@ public abstract class HttpResponse<T> { public abstract int statusCode(); /** - * Returns the initial {@link HttpRequest} that initiated the exchange. + * Returns the {@link HttpRequest} corresponding to this response. + * + * <p> This may not be the original request provided by the caller, + * for example, if that request was redirected. + * + * @see #previousResponse() * * @return the request */ public abstract HttpRequest request(); /** - * Returns the final {@link HttpRequest} that was sent on the wire for the - * exchange ( may, or may not, be the same as the initial request ). + * Returns an {@code Optional} containing the previous intermediate response + * if one was received. An intermediate response is one that is received + * as a result of redirection or authentication. If no previous response + * was received then an empty {@code Optional} is returned. * - * @return the request + * @return an Optional containing the HttpResponse, if any. */ - public abstract HttpRequest finalRequest(); + public abstract Optional<HttpResponse<T>> previousResponse(); /** * Returns the received response headers. @@ -119,21 +129,14 @@ public abstract class HttpResponse<T> { public abstract HttpHeaders headers(); /** - * Returns the received response trailers, if there are any, when they - * become available. For many response processor types this will be at the same - * time as the {@code HttpResponse} itself is available. In such cases, the - * returned {@code CompletableFuture} will be already completed. - * - * @return a CompletableFuture of the response trailers (may be empty) - */ - public abstract CompletableFuture<HttpHeaders> trailers(); - - /** - * Returns the body. Depending on the type of {@code T}, the returned body may - * represent the body after it was read (such as {@code byte[]}, or + * Returns the body. Depending on the type of {@code T}, the returned body + * may represent the body after it was read (such as {@code byte[]}, or * {@code String}, or {@code Path}) or it may represent an object with * which the body is read, such as an {@link java.io.InputStream}. * + * <p> If this {@code HttpResponse} was returned from an invocation of + * {@link #previousResponse()} then this method returns {@code null} + * * @return the body */ public abstract T body(); @@ -161,36 +164,124 @@ public abstract class HttpResponse<T> { */ public abstract HttpClient.Version version(); + + private static String pathForSecurityCheck(Path path) { + return path.toFile().getPath(); + } + + /** A body handler that is further restricted by a given ACC. */ + interface UntrustedBodyHandler<T> extends BodyHandler<T> { + void setAccessControlContext(AccessControlContext acc); + } + + /** + * A Path body handler. + * + * Note: Exists mainly too allow setting of the senders ACC post creation of + * the handler. + */ + static class PathBodyHandler implements UntrustedBodyHandler<Path> { + private final Path file; + private final OpenOption[]openOptions; + private volatile AccessControlContext acc; + + PathBodyHandler(Path file, OpenOption... openOptions) { + this.file = file; + this.openOptions = openOptions; + } + + @Override + public void setAccessControlContext(AccessControlContext acc) { + this.acc = acc; + } + + @Override + public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) { + ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber) + BodySubscriber.asFileImpl(file, openOptions); + bs.setAccessControlContext(acc); + return bs; + } + } + + // Similar to Path body handler, but for file download. Supports setting ACC. + static class FileDownloadBodyHandler implements UntrustedBodyHandler<Path> { + private final Path directory; + private final OpenOption[]openOptions; + private volatile AccessControlContext acc; + + FileDownloadBodyHandler(Path directory, OpenOption... openOptions) { + this.directory = directory; + this.openOptions = openOptions; + } + + @Override + public void setAccessControlContext(AccessControlContext acc) { + this.acc = acc; + } + + @Override + public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) { + String dispoHeader = headers.firstValue("Content-Disposition") + .orElseThrow(() -> unchecked(new IOException("No Content-Disposition"))); + if (!dispoHeader.startsWith("attachment;")) { + throw unchecked(new IOException("Unknown Content-Disposition type")); + } + int n = dispoHeader.indexOf("filename="); + if (n == -1) { + throw unchecked(new IOException("Bad Content-Disposition type")); + } + int lastsemi = dispoHeader.lastIndexOf(';'); + String disposition; + if (lastsemi < n) { + disposition = dispoHeader.substring(n + 9); + } else { + disposition = dispoHeader.substring(n + 9, lastsemi); + } + Path file = Paths.get(directory.toString(), disposition); + + ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber) + BodySubscriber.asFileImpl(file, openOptions); + bs.setAccessControlContext(acc); + return bs; + } + } + /** * A handler for response bodies. * {@Incubating} - * <p> - * This is a function that takes two parameters: the response status code, - * and the response headers, and which returns a {@link BodyProcessor}. + * + * <p> This is a function that takes two parameters: the response status code, + * and the response headers, and which returns a {@linkplain BodySubscriber}. * The function is always called just before the response body is read. Its * implementation may examine the status code or headers and must decide, * whether to accept the response body or discard it, and if accepting it, * exactly how to handle it. - * <p> - * Some pre-defined implementations which do not utilize the status code + * + * <p> Some pre-defined implementations which do not utilize the status code * or headers (meaning the body is always accepted) are defined: * <ul><li>{@link #asByteArray() }</li> * <li>{@link #asByteArrayConsumer(java.util.function.Consumer) * asByteArrayConsumer(Consumer)}</li> + * <li>{@link #asString(java.nio.charset.Charset) asString(Charset)}</li> + * <li>{@link #asFile(Path, OpenOption...) + * asFile(Path,OpenOption...)}</li> * <li>{@link #asFileDownload(java.nio.file.Path,OpenOption...) * asFileDownload(Path,OpenOption...)}</li> + * <li>{@link #asInputStream() asInputStream()}</li> * <li>{@link #discard(Object) }</li> - * <li>{@link #asString(java.nio.charset.Charset) - * asString(Charset)}</li></ul> - * <p> - * These implementations return the equivalent {@link BodyProcessor}. + * <li>{@link #buffering(BodyHandler, int) + * buffering(BodyHandler,int)}</li> + * </ul> + * + * <p> These implementations return the equivalent {@link BodySubscriber}. * Alternatively, the handler can be used to examine the status code - * or headers and return different body processors as appropriate. - * <p> - * <b>Examples of handler usage</b> - * <p> - * The first example uses one of the predefined handler functions which - * ignore the response headers and status, and always process the response + * or headers and return different body subscribers as appropriate. + * + * <p><b>Examples of handler usage</b> + * + * <p> The first example uses one of the predefined handler functions which + * ignores the response headers and status, and always process the response * body in the same way. * <pre> * {@code @@ -201,11 +292,11 @@ public abstract class HttpResponse<T> { * } * </pre> * Note, that even though these pre-defined handlers ignore the status code - * and headers, this information is still accessible from the {@code HttpResponse} - * when it is returned. - * <p> - * In the second example, the function returns a different processor depending - * on the status code. + * and headers, this information is still accessible from the + * {@code HttpResponse} when it is returned. + * + * <p> In the second example, the function returns a different subscriber + * depending on the status code. * <pre> * {@code * HttpResponse<Path> resp1 = HttpRequest @@ -213,93 +304,134 @@ public abstract class HttpResponse<T> { * .GET() * .response( * (status, headers) -> status == 200 - * ? BodyProcessor.asFile(Paths.get("/tmp/f")) - * : BodyProcessor.discard(Paths.get("/NULL"))); + * ? BodySubscriber.asFile(Paths.get("/tmp/f")) + * : BodySubscriber.discard(Paths.get("/NULL"))); * } * </pre> * - * @param <T> the response body type. + * @param <T> the response body type */ @FunctionalInterface public interface BodyHandler<T> { /** - * Returns a {@link BodyProcessor BodyProcessor} considering the given response status - * code and headers. This method is always called before the body is read - * and its implementation can decide to keep the body and store it somewhere - * or else discard it, by returning the {@code BodyProcessor} returned - * from {@link BodyProcessor#discard(java.lang.Object) discard()}. + * Returns a {@link BodySubscriber BodySubscriber} considering the given + * response status code and headers. This method is always called before + * the body is read and its implementation can decide to keep the body + * and store it somewhere, or else discard it by returning the {@code + * BodySubscriber} returned from {@link BodySubscriber#discard(Object) + * discard}. * * @param statusCode the HTTP status code received * @param responseHeaders the response headers received - * @return a response body handler + * @return a body subscriber */ - public BodyProcessor<T> apply(int statusCode, HttpHeaders responseHeaders); + public BodySubscriber<T> apply(int statusCode, HttpHeaders responseHeaders); /** * Returns a response body handler which discards the response body and * uses the given value as a replacement for it. * * @param <U> the response body type - * @param value the value of U to return as the body + * @param value the value of U to return as the body, may be {@code null} * @return a response body handler */ public static <U> BodyHandler<U> discard(U value) { - return (status, headers) -> BodyProcessor.discard(value); + return (status, headers) -> BodySubscriber.discard(value); } /** * Returns a {@code BodyHandler<String>} that returns a - * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from - * {@link BodyProcessor#asString(java.nio.charset.Charset) - * BodyProcessor.asString(Charset)}. If a charset is provided, the - * body is decoded using it. If charset is {@code null} then the processor - * tries to determine the character set from the {@code Content-encoding} - * header. If that charset is not supported then - * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used. + * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from + * {@link BodySubscriber#asString(Charset) BodySubscriber.asString(Charset)}. + * If a charset is provided, the body is decoded using it. If charset is + * {@code null} then the handler tries to determine the character set + * from the {@code Content-encoding} header. If that charset is not + * supported then {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} + * is used. * - * @param charset the name of the charset to interpret the body as. If - * {@code null} then charset determined from Content-encoding header + * @param charset The name of the charset to interpret the body as. If + * {@code null} then the charset is determined from the + * <i>Content-encoding</i> header. * @return a response body handler */ public static BodyHandler<String> asString(Charset charset) { return (status, headers) -> { if (charset != null) { - return BodyProcessor.asString(charset); + return BodySubscriber.asString(charset); } - return BodyProcessor.asString(charsetFrom(headers)); + return BodySubscriber.asString(charsetFrom(headers)); }; } - /** * Returns a {@code BodyHandler<Path>} that returns a - * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from - * {@link BodyProcessor#asFile(Path) BodyProcessor.asFile(Path)}. - * <p> - * When the {@code HttpResponse} object is returned, the body has been completely - * written to the file, and {@link #body()} returns a reference to its - * {@link Path}. + * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from + * {@link BodySubscriber#asFile(Path, OpenOption...) + * BodySubscriber.asFile(Path,OpenOption...)}. * - * @param file the file to store the body in + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the file, and {@link #body()} returns a + * reference to its {@link Path}. + * + * @param file the filename to store the body in + * @param openOptions any options to use when opening/creating the file * @return a response body handler + * @throws SecurityException If a security manager has been installed + * and it denies {@link SecurityManager#checkWrite(String) + * write access} to the file. The {@link + * SecurityManager#checkDelete(String) checkDelete} method is + * invoked to check delete access if the file is opened with + * the {@code DELETE_ON_CLOSE} option. */ - public static BodyHandler<Path> asFile(Path file) { - return (status, headers) -> BodyProcessor.asFile(file); + public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) { + Objects.requireNonNull(file); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String fn = pathForSecurityCheck(file); + sm.checkWrite(fn); + List<OpenOption> opts = Arrays.asList(openOptions); + if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) + sm.checkDelete(fn); + if (opts.contains(StandardOpenOption.READ)) + sm.checkRead(fn); + } + return new PathBodyHandler(file, openOptions); } /** * Returns a {@code BodyHandler<Path>} that returns a - * {@link BodyProcessor BodyProcessor}<{@link Path}> + * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from + * {@link BodySubscriber#asFile(Path) BodySubscriber.asFile(Path)}. + * + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the file, and {@link #body()} returns a + * reference to its {@link Path}. + * + * @param file the file to store the body in + * @return a response body handler + * @throws SecurityException if a security manager has been installed + * and it denies {@link SecurityManager#checkWrite(String) + * write access} to the file + */ + public static BodyHandler<Path> asFile(Path file) { + return BodyHandler.asFile(file, StandardOpenOption.CREATE, + StandardOpenOption.WRITE); + } + + /** + * Returns a {@code BodyHandler<Path>} that returns a + * {@link BodySubscriber BodySubscriber}<{@link Path}> * where the download directory is specified, but the filename is * obtained from the {@code Content-Disposition} response header. The - * {@code Content-Disposition} header must specify the <i>attachment</i> type - * and must also contain a - * <i>filename</i> parameter. If the filename specifies multiple path - * components only the final component is used as the filename (with the - * given directory name). When the {@code HttpResponse} object is - * returned, the body has been completely written to the file and {@link - * #body()} returns a {@code Path} object for the file. The returned {@code Path} is the + * {@code Content-Disposition} header must specify the <i>attachment</i> + * type and must also contain a <i>filename</i> parameter. If the + * filename specifies multiple path components only the final component + * is used as the filename (with the given directory name). + * + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the file and {@link #body()} returns a + * {@code Path} object for the file. The returned {@code Path} is the * combination of the supplied directory name and the file name supplied * by the server. If the destination directory does not exist or cannot * be written to, then the response will fail with an {@link IOException}. @@ -307,245 +439,355 @@ public abstract class HttpResponse<T> { * @param directory the directory to store the file in * @param openOptions open options * @return a response body handler + * @throws SecurityException If a security manager has been installed + * and it denies {@link SecurityManager#checkWrite(String) + * write access} to the file. The {@link + * SecurityManager#checkDelete(String) checkDelete} method is + * invoked to check delete access if the file is opened with + * the {@code DELETE_ON_CLOSE} option. */ - public static BodyHandler<Path> asFileDownload(Path directory, OpenOption... openOptions) { - return (status, headers) -> { - String dispoHeader = headers.firstValue("Content-Disposition") - .orElseThrow(() -> unchecked(new IOException("No Content-Disposition"))); - if (!dispoHeader.startsWith("attachment;")) { - throw unchecked(new IOException("Unknown Content-Disposition type")); - } - int n = dispoHeader.indexOf("filename="); - if (n == -1) { - throw unchecked(new IOException("Bad Content-Disposition type")); - } - int lastsemi = dispoHeader.lastIndexOf(';'); - String disposition; - if (lastsemi < n) { - disposition = dispoHeader.substring(n + 9); - } else { - disposition = dispoHeader.substring(n + 9, lastsemi); - } - Path file = Paths.get(directory.toString(), disposition); - return BodyProcessor.asFile(file, openOptions); - }; + //####: check if the dir exists and is writable?? + public static BodyHandler<Path> asFileDownload(Path directory, + OpenOption... openOptions) { + Objects.requireNonNull(directory); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String fn = pathForSecurityCheck(directory); + sm.checkWrite(fn); + List<OpenOption> opts = Arrays.asList(openOptions); + if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) + sm.checkDelete(fn); + if (opts.contains(StandardOpenOption.READ)) + sm.checkRead(fn); + } + return new FileDownloadBodyHandler(directory, openOptions); } /** - * Returns a {@code BodyHandler<Path>} that returns a - * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from - * {@link BodyProcessor#asFile(java.nio.file.Path, java.nio.file.OpenOption...) - * BodyProcessor.asFile(Path,OpenOption...)}. - * <p> - * When the {@code HttpResponse} object is returned, the body has been completely - * written to the file, and {@link #body()} returns a reference to its - * {@link Path}. + * Returns a {@code BodyHandler<InputStream>} that returns a + * {@link BodySubscriber BodySubscriber}{@code <InputStream>} obtained + * from {@link BodySubscriber#asInputStream() BodySubscriber.asInputStream}. + * + * <p> When the {@code HttpResponse} object is returned, the response + * headers will have been completely read, but the body may not have + * been fully received yet. The {@link #body()} method returns an + * {@link InputStream} from which the body can be read as it is received. + * + * @apiNote See {@link BodySubscriber#asInputStream()} for more information. * - * @param file the filename to store the body in - * @param openOptions any options to use when opening/creating the file * @return a response body handler */ - public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) { - return (status, headers) -> BodyProcessor.asFile(file, openOptions); + public static BodyHandler<InputStream> asInputStream() { + return (status, headers) -> BodySubscriber.asInputStream(); } /** * Returns a {@code BodyHandler<Void>} that returns a - * {@link BodyProcessor BodyProcessor}{@code <Void>} obtained from - * {@link BodyProcessor#asByteArrayConsumer(java.util.function.Consumer) - * BodyProcessor.asByteArrayConsumer(Consumer)}. - * <p> - * When the {@code HttpResponse} object is returned, the body has been completely - * written to the consumer. + * {@link BodySubscriber BodySubscriber}{@code <Void>} obtained from + * {@link BodySubscriber#asByteArrayConsumer(Consumer) + * BodySubscriber.asByteArrayConsumer(Consumer)}. + * + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the consumer. * * @param consumer a Consumer to accept the response body * @return a response body handler */ public static BodyHandler<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) { - return (status, headers) -> BodyProcessor.asByteArrayConsumer(consumer); + return (status, headers) -> BodySubscriber.asByteArrayConsumer(consumer); } /** * Returns a {@code BodyHandler<byte[]>} that returns a - * {@link BodyProcessor BodyProcessor}<{@code byte[]}> obtained - * from {@link BodyProcessor#asByteArray() BodyProcessor.asByteArray()}. - * <p> - * When the {@code HttpResponse} object is returned, the body has been completely - * written to the byte array. + * {@link BodySubscriber BodySubscriber}<{@code byte[]}> obtained + * from {@link BodySubscriber#asByteArray() BodySubscriber.asByteArray()}. + * + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the byte array. * * @return a response body handler */ public static BodyHandler<byte[]> asByteArray() { - return (status, headers) -> BodyProcessor.asByteArray(); + return (status, headers) -> BodySubscriber.asByteArray(); } /** * Returns a {@code BodyHandler<String>} that returns a - * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from - * {@link BodyProcessor#asString(java.nio.charset.Charset) - * BodyProcessor.asString(Charset)}. The body is + * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from + * {@link BodySubscriber#asString(java.nio.charset.Charset) + * BodySubscriber.asString(Charset)}. The body is * decoded using the character set specified in * the {@code Content-encoding} response header. If there is no such * header, or the character set is not supported, then * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used. - * <p> - * When the {@code HttpResponse} object is returned, the body has been completely - * written to the string. + * + * <p> When the {@code HttpResponse} object is returned, the body has + * been completely written to the string. * * @return a response body handler */ public static BodyHandler<String> asString() { - return (status, headers) -> BodyProcessor.asString(charsetFrom(headers)); + return (status, headers) -> BodySubscriber.asString(charsetFrom(headers)); } + + /** + * Returns a {@code BodyHandler} which, when invoked, returns a {@linkplain + * BodySubscriber#buffering(BodySubscriber,int) buffering BodySubscriber} + * that buffers data before delivering it to the downstream subscriber. + * These {@code BodySubscriber} instances are created by calling + * {@linkplain BodySubscriber#buffering(BodySubscriber,int) + * BodySubscriber.buffering} with a subscriber obtained from the given + * downstream handler and the {@code bufferSize} parameter. + * + * @param downstreamHandler the downstream handler + * @param bufferSize the buffer size parameter passed to {@linkplain + * BodySubscriber#buffering(BodySubscriber,int) BodySubscriber.buffering} + * @return a body handler + * @throws IllegalArgumentException if {@code bufferSize <= 0} + */ + public static <T> BodyHandler<T> buffering(BodyHandler<T> downstreamHandler, + int bufferSize) { + if (bufferSize <= 0) + throw new IllegalArgumentException("must be greater than 0"); + return (status, headers) -> BodySubscriber + .buffering(downstreamHandler.apply(status, headers), + bufferSize); + } } /** - * A processor for response bodies. + * A subscriber for response bodies. * {@Incubating} - * <p> - * The object acts as a {@link Flow.Subscriber}<{@link ByteBuffer}> to - * the HTTP client implementation which publishes ByteBuffers containing the - * response body. The processor converts the incoming buffers of data to - * some user-defined object type {@code T}. - * <p> - * The {@link #getBody()} method returns a {@link CompletionStage}{@code <T>} - * that provides the response body object. The {@code CompletionStage} must - * be obtainable at any time. When it completes depends on the nature - * of type {@code T}. In many cases, when {@code T} represents the entire body after being - * read then it completes after the body has been read. If {@code T} is a streaming - * type such as {@link java.io.InputStream} then it completes before the - * body has been read, because the calling code uses it to consume the data. + * + * <p> The object acts as a {@link Flow.Subscriber}<{@link List}<{@link + * ByteBuffer}>> to the HTTP client implementation, which publishes + * unmodifiable lists of ByteBuffers containing the response body. The Flow + * of data, as well as the order of ByteBuffers in the Flow lists, is a + * strictly ordered representation of the response body. Both the Lists and + * the ByteBuffers, once passed to the subscriber, are no longer used by the + * HTTP client. The subscriber converts the incoming buffers of data to some + * user-defined object type {@code T}. + * + * <p> The {@link #getBody()} method returns a {@link CompletionStage}{@code + * <T>} that provides the response body object. The {@code CompletionStage} + * must be obtainable at any time. When it completes depends on the nature + * of type {@code T}. In many cases, when {@code T} represents the entire + * body after being read then it completes after the body has been read. If + * {@code T} is a streaming type such as {@link java.io.InputStream} then it + * completes before the body has been read, because the calling code uses it + * to consume the data. + * + * @apiNote To ensure that all resources associated with the + * corresponding exchange are properly released, an implementation + * of {@code BodySubscriber} must ensure to {@linkplain + * Flow.Subscription#request request} more data until {@link + * #onComplete() onComplete} or {@link #onError(Throwable) onError} + * are signalled, or {@linkplain Flow.Subscription#request cancel} its + * {@linkplain #onSubscribe(Flow.Subscription) subscription} + * if unable or unwilling to do so. + * Calling {@code cancel} before exhausting the data may cause + * the underlying HTTP connection to be closed and prevent it + * from being reused for subsequent operations. * * @param <T> the response body type */ - public interface BodyProcessor<T> - extends Flow.Subscriber<ByteBuffer> { + public interface BodySubscriber<T> + extends Flow.Subscriber<List<ByteBuffer>> { /** - * Returns a {@code CompletionStage} which when completed will return the - * response body object. + * Returns a {@code CompletionStage} which when completed will return + * the response body object. * * @return a CompletionStage for the response body */ public CompletionStage<T> getBody(); /** - * Returns a body processor which stores the response body as a {@code + * Returns a body subscriber which stores the response body as a {@code * String} converted using the given {@code Charset}. - * <p> - * The {@link HttpResponse} using this processor is available after the - * entire response has been read. + * + * <p> The {@link HttpResponse} using this subscriber is available after + * the entire response has been read. * * @param charset the character set to convert the String with - * @return a body processor + * @return a body subscriber */ - public static BodyProcessor<String> asString(Charset charset) { - return new ResponseProcessors.ByteArrayProcessor<>( + public static BodySubscriber<String> asString(Charset charset) { + return new ResponseSubscribers.ByteArraySubscriber<>( bytes -> new String(bytes, charset) ); } /** - * Returns a {@code BodyProcessor} which stores the response body as a + * Returns a {@code BodySubscriber} which stores the response body as a * byte array. - * <p> - * The {@link HttpResponse} using this processor is available after the - * entire response has been read. * - * @return a body processor + * <p> The {@link HttpResponse} using this subscriber is available after + * the entire response has been read. + * + * @return a body subscriber */ - public static BodyProcessor<byte[]> asByteArray() { - return new ResponseProcessors.ByteArrayProcessor<>( + public static BodySubscriber<byte[]> asByteArray() { + return new ResponseSubscribers.ByteArraySubscriber<>( Function.identity() // no conversion ); } + // no security check + private static BodySubscriber<Path> asFileImpl(Path file, OpenOption... openOptions) { + return new ResponseSubscribers.PathSubscriber(file, openOptions); + } + /** - * Returns a {@code BodyProcessor} which stores the response body in a + * Returns a {@code BodySubscriber} which stores the response body in a * file opened with the given options and name. The file will be opened - * with the given options using - * {@link java.nio.channels.FileChannel#open(java.nio.file.Path,java.nio.file.OpenOption...) - * FileChannel.open} just before the body is read. Any exception thrown will be returned - * or thrown from {@link HttpClient#send(jdk.incubator.http.HttpRequest, - * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::send} - * or {@link HttpClient#sendAsync(jdk.incubator.http.HttpRequest, - * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::sendAsync} - * as appropriate. - * <p> - * The {@link HttpResponse} using this processor is available after the - * entire response has been read. + * with the given options using {@link FileChannel#open(Path,OpenOption...) + * FileChannel.open} just before the body is read. Any exception thrown + * will be returned or thrown from {@link HttpClient#send(HttpRequest, + * BodyHandler) HttpClient::send} or {@link HttpClient#sendAsync(HttpRequest, + * BodyHandler) HttpClient::sendAsync} as appropriate. + * + * <p> The {@link HttpResponse} using this subscriber is available after + * the entire response has been read. * * @param file the file to store the body in * @param openOptions the list of options to open the file with - * @return a body processor + * @return a body subscriber + * @throws SecurityException If a security manager has been installed + * and it denies {@link SecurityManager#checkWrite(String) + * write access} to the file. The {@link + * SecurityManager#checkDelete(String) checkDelete} method is + * invoked to check delete access if the file is opened with the + * {@code DELETE_ON_CLOSE} option. */ - public static BodyProcessor<Path> asFile(Path file, OpenOption... openOptions) { - return new ResponseProcessors.PathProcessor(file, openOptions); + public static BodySubscriber<Path> asFile(Path file, OpenOption... openOptions) { + Objects.requireNonNull(file); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String fn = pathForSecurityCheck(file); + sm.checkWrite(fn); + List<OpenOption> opts = Arrays.asList(openOptions); + if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE)) + sm.checkDelete(fn); + if (opts.contains(StandardOpenOption.READ)) + sm.checkRead(fn); + } + return asFileImpl(file, openOptions); } /** - * Returns a {@code BodyProcessor} which provides the incoming body - * data to the provided Consumer of {@code Optional<byte[]>}. Each - * call to {@link Consumer#accept(java.lang.Object) Consumer.accept()} - * will contain a non empty {@code Optional}, except for the final invocation after - * all body data has been read, when the {@code Optional} will be empty. - * <p> - * The {@link HttpResponse} using this processor is available after the - * entire response has been read. - * - * @param consumer a Consumer of byte arrays - * @return a BodyProcessor - */ - public static BodyProcessor<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) { - return new ResponseProcessors.ConsumerProcessor(consumer); - } - - /** - * Returns a {@code BodyProcessor} which stores the response body in a + * Returns a {@code BodySubscriber} which stores the response body in a * file opened with the given name. Has the same effect as calling - * {@link #asFile(java.nio.file.Path, java.nio.file.OpenOption...) asFile} - * with the standard open options {@code CREATE} and {@code WRITE} - * <p> - * The {@link HttpResponse} using this processor is available after the - * entire response has been read. + * {@link #asFile(Path, OpenOption...) asFile} with the standard open + * options {@code CREATE} and {@code WRITE} + * + * <p> The {@link HttpResponse} using this subscriber is available after + * the entire response has been read. * * @param file the file to store the body in - * @return a body processor + * @return a body subscriber + * @throws SecurityException if a security manager has been installed + * and it denies {@link SecurityManager#checkWrite(String) + * write access} to the file */ - public static BodyProcessor<Path> asFile(Path file) { - return new ResponseProcessors.PathProcessor( - file, - StandardOpenOption.CREATE, StandardOpenOption.WRITE); + public static BodySubscriber<Path> asFile(Path file) { + return asFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE); } /** - * Returns a response processor which discards the response body. The + * Returns a {@code BodySubscriber} which provides the incoming body + * data to the provided Consumer of {@code Optional<byte[]>}. Each + * call to {@link Consumer#accept(java.lang.Object) Consumer.accept()} + * will contain a non empty {@code Optional}, except for the final + * invocation after all body data has been read, when the {@code + * Optional} will be empty. + * + * <p> The {@link HttpResponse} using this subscriber is available after + * the entire response has been read. + * + * @param consumer a Consumer of byte arrays + * @return a BodySubscriber + */ + public static BodySubscriber<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) { + return new ResponseSubscribers.ConsumerSubscriber(consumer); + } + + /** + * Returns a {@code BodySubscriber} which streams the response body as + * an {@link InputStream}. + * + * <p> The {@link HttpResponse} using this subscriber is available + * immediately after the response headers have been read, without + * requiring to wait for the entire body to be processed. The response + * body can then be read directly from the {@link InputStream}. + * + * @apiNote To ensure that all resources associated with the + * corresponding exchange are properly released the caller must + * ensure to either read all bytes until EOF is reached, or call + * {@link InputStream#close} if it is unable or unwilling to do so. + * Calling {@code close} before exhausting the stream may cause + * the underlying HTTP connection to be closed and prevent it + * from being reused for subsequent operations. + * + * @return a body subscriber that streams the response body as an + * {@link InputStream}. + */ + public static BodySubscriber<InputStream> asInputStream() { + return new ResponseSubscribers.HttpResponseInputStream(); + } + + /** + * Returns a response subscriber which discards the response body. The * supplied value is the value that will be returned from * {@link HttpResponse#body()}. * * @param <U> The type of the response body - * @param value the value to return from HttpResponse.body() - * @return a {@code BodyProcessor} + * @param value the value to return from HttpResponse.body(), may be {@code null} + * @return a {@code BodySubscriber} */ - public static <U> BodyProcessor<U> discard(U value) { - return new ResponseProcessors.NullProcessor<>(Optional.ofNullable(value)); + public static <U> BodySubscriber<U> discard(U value) { + return new ResponseSubscribers.NullSubscriber<>(Optional.ofNullable(value)); } + + /** + * Returns a {@code BodySubscriber} which buffers data before delivering + * it to the given downstream subscriber. The subscriber guarantees to + * deliver {@code buffersize} bytes of data to each invocation of the + * downstream's {@linkplain #onNext(Object) onNext} method, except for + * the final invocation, just before {@linkplain #onComplete() onComplete} + * is invoked. The final invocation of {@code onNext} may contain fewer + * than {@code buffersize} bytes. + * + * <p> The returned subscriber delegates its {@link #getBody()} method + * to the downstream subscriber. + * + * @param downstream the downstream subscriber + * @param bufferSize the buffer size + * @return a buffering body subscriber + * @throws IllegalArgumentException if {@code bufferSize <= 0} + */ + public static <T> BodySubscriber<T> buffering(BodySubscriber<T> downstream, + int bufferSize) { + if (bufferSize <= 0) + throw new IllegalArgumentException("must be greater than 0"); + return new BufferingSubscriber<T>(downstream, bufferSize); + } } /** - * A response processor for a HTTP/2 multi response. + * A response subscriber for a HTTP/2 multi response. * {@Incubating} - * <p> - * A multi response comprises a main response, and zero or more additional + * + * <p> A multi response comprises a main response, and zero or more additional * responses. Each additional response is sent by the server in response to - * requests that the server also generates. Additional responses are + * requests (PUSH_PROMISEs) that the server also generates. Additional responses are * typically resources that the server expects the client will need which * are related to the initial request. * <p> * Note. Instead of implementing this interface, applications should consider * first using the mechanism (built on this interface) provided by - * {@link MultiProcessor#asMap(java.util.function.Function, boolean) - * MultiProcessor.asMap()} which is a slightly simplified, but + * {@link MultiSubscriber#asMap(java.util.function.Function, boolean) + * MultiSubscriber.asMap()} which is a slightly simplified, but also * general purpose interface. * <p> * The server generated requests are also known as <i>push promises</i>. @@ -556,7 +798,7 @@ public abstract class HttpResponse<T> { * the server does not wait for any acknowledgment before sending the * response, this must be done quickly to avoid unnecessary data transmission. * - * <p> {@code MultiProcessor}s are parameterized with a type {@code U} which + * <p> {@code MultiSubscriber}s are parameterized with a type {@code U} which * represents some meaningful aggregate of the responses received. This * would typically be a collection of response or response body objects. * @@ -565,29 +807,43 @@ public abstract class HttpResponse<T> { * * @since 9 */ - public interface MultiProcessor<U,T> { + public interface MultiSubscriber<U,T> { /** - * Called for the main request and each push promise that is received. - * The first call will always be for the main request that was sent - * by the caller. This {@link HttpRequest} parameter - * represents the initial request or subsequent PUSH_PROMISE. The - * implementation must return an {@code Optional} of {@link BodyHandler} for - * the response body. Different handlers (of the same type) can be returned - * for different pushes within the same multi send. If no handler - * (an empty {@code Optional}) is returned, then the push will be canceled. It is - * an error to not return a valid {@code BodyHandler} for the initial (main) request. + * Called for the main request from the user. This {@link HttpRequest} + * parameter is the request that was supplied to {@link + * HttpClient#sendAsync(HttpRequest, MultiSubscriber)}. The + * implementation must return an {@link BodyHandler} for the response + * body. * - * @param request the main request or subsequent push promise + * @param request the request * * @return an optional body handler */ - Optional<BodyHandler<T>> onRequest(HttpRequest request); + BodyHandler<T> onRequest(HttpRequest request); + + /** + * Called for each push promise that is received. The {@link HttpRequest} + * parameter represents the PUSH_PROMISE. The implementation must return + * an {@code Optional} of {@link BodyHandler} for the response body. + * Different handlers (of the same type) can be returned for different + * pushes within the same multi send. If no handler (an empty {@code + * Optional}) is returned, then the push will be canceled. If required, + * the {@code CompletableFuture<Void>} supplied to the {@code + * onFinalPushPromise} parameter of {@link + * #completion(CompletableFuture, CompletableFuture)} can be used to + * determine when the final PUSH_PROMISE is received. + * + * @param pushPromise the push promise + * + * @return an optional body handler + */ + Optional<BodyHandler<T>> onPushPromise(HttpRequest pushPromise); /** * Called for each response received. For each request either one of * onResponse() or onError() is guaranteed to be called, but not both. * - * [Note] The reason for switching to this callback interface rather + * <p> Note: The reason for switching to this callback interface rather * than using CompletableFutures supplied to onRequest() is that there * is a subtle interaction between those CFs and the CF returned from * completion() (or when onComplete() was called formerly). The completion() @@ -615,9 +871,11 @@ public abstract class HttpResponse<T> { * Returns a {@link java.util.concurrent.CompletableFuture}{@code <U>} * which completes when the aggregate result object itself is available. * It is expected that the returned {@code CompletableFuture} will depend - * on one of the given {@code CompletableFuture<Void}s which themselves complete - * after all individual responses associated with the multi response - * have completed, or after all push promises have been received. + * on one of the given {@code CompletableFuture<Void}s which themselves + * complete after all individual responses associated with the multi + * response have completed, or after all push promises have been received. + * This method is called after {@link #onRequest(HttpRequest)} but + * before any other methods. * * @implNote Implementations might follow the pattern shown below * <pre> @@ -653,47 +911,50 @@ public abstract class HttpResponse<T> { * generated push promise) is returned as a key of the map. The value * corresponding to each key is a * {@code CompletableFuture<HttpResponse<V>>}. - * <p> - * There are two ways to use these handlers, depending on the value of - * the <i>completion</I> parameter. If completion is true, then the + * + * <p> There are two ways to use these handlers, depending on the value + * of the <i>completion</I> parameter. If completion is true, then the * aggregated result will be available after all responses have * themselves completed. If <i>completion</i> is false, then the * aggregated result will be available immediately after the last push * promise was received. In the former case, this implies that all the * CompletableFutures in the map values will have completed. In the * latter case, they may or may not have completed yet. - * <p> - * The simplest way to use these handlers is to set completion to + * + * <p> The simplest way to use these handlers is to set completion to * {@code true}, and then all (results) values in the Map will be * accessible without blocking. * <p> - * See {@link #asMap(java.util.function.Function, boolean) - * } + * See {@link #asMap(java.util.function.Function, boolean)} * for a code sample of using this interface. * - * @param <V> the body type used for all responses - * @param pushHandler a function invoked for each request or push - * promise - * @param completion {@code true} if the aggregate CompletableFuture - * completes after all responses have been received, or {@code false} - * after all push promises received. + * <p> See {@link #asMap(Function, boolean)} for a code sample of using + * this interface. * - * @return a MultiProcessor + * @param <V> the body type used for all responses + * @param reqHandler a function invoked for the user's request and each + * push promise + * @param completion {@code true} if the aggregate CompletableFuture + * completes after all responses have been received, + * or {@code false} after all push promises received + * + * @return a MultiSubscriber */ - public static <V> MultiProcessor<MultiMapResult<V>,V> asMap( - Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler, + public static <V> MultiSubscriber<MultiMapResult<V>,V> asMap( + Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> reqHandler, boolean completion) { - - return new MultiProcessorImpl<V>(pushHandler, completion); + return new MultiSubscriberImpl<V>(reqHandler.andThen(optv -> optv.get()), + reqHandler, + completion); } /** * Returns a general purpose handler for multi responses. This is a - * convenience method which invokes {@link #asMap(java.util.function.Function,boolean) + * convenience method which invokes {@link #asMap(Function,boolean) * asMap(Function, true)} meaning that the aggregate result * object completes after all responses have been received. - * <p> - * <b>Example usage:</b> + * + * <p><b>Example usage:</b> * <br> * <pre> * {@code @@ -705,26 +966,26 @@ public abstract class HttpResponse<T> { * HttpClient client = HttpClient.newHttpClient(); * * Map<HttpRequest,CompletableFuture<HttpResponse<String>>> results = client - * .sendAsync(request, MultiProcessor.asMap( + * .sendAsync(request, MultiSubscriber.asMap( * (req) -> Optional.of(HttpResponse.BodyHandler.asString()))) * .join(); * }</pre> - * <p> - * The lambda in this example is the simplest possible implementation, + * + * <p> The lambda in this example is the simplest possible implementation, * where neither the incoming requests are examined, nor the response * headers, and every push that the server sends is accepted. When the * join() call returns, all {@code HttpResponse}s and their associated * body objects are available. * * @param <V> the body type used for all responses - * @param pushHandler a function invoked for each request or push - * promise - * @return a MultiProcessor + * @param reqHandler a function invoked for each push promise and the + * main request + * @return a MultiSubscriber */ - public static <V> MultiProcessor<MultiMapResult<V>,V> asMap( - Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler) { + public static <V> MultiSubscriber<MultiMapResult<V>,V> asMap( + Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> reqHandler) { - return asMap(pushHandler, true); + return asMap(reqHandler, true); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java index c2a3c2ac8a5..589eec2f533 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -28,9 +28,10 @@ package jdk.incubator.http; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; import javax.net.ssl.SSLParameters; -import jdk.incubator.http.internal.common.Log; import jdk.incubator.http.internal.websocket.RawChannel; /** @@ -41,13 +42,11 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider final int responseCode; final Exchange<T> exchange; final HttpRequest initialRequest; - final HttpRequestImpl finalRequest; + final Optional<HttpResponse<T>> previousResponse; final HttpHeaders headers; - //final HttpHeaders trailers; final SSLParameters sslParameters; final URI uri; final HttpClient.Version version; - //final AccessControlContext acc; RawChannel rawchan; final HttpConnection connection; final Stream<T> stream; @@ -55,41 +54,43 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider public HttpResponseImpl(HttpRequest initialRequest, Response response, - T body, Exchange<T> exch) { + HttpResponse<T> previousResponse, + T body, + Exchange<T> exch) { this.responseCode = response.statusCode(); this.exchange = exch; this.initialRequest = initialRequest; - this.finalRequest = exchange.request(); + this.previousResponse = Optional.ofNullable(previousResponse); this.headers = response.headers(); //this.trailers = trailers; - this.sslParameters = exch.client().sslParameters().orElse(null); - this.uri = finalRequest.uri(); + this.sslParameters = exch.client().sslParameters(); + this.uri = response.request().uri(); this.version = response.version(); this.connection = exch.exchImpl.connection(); this.stream = null; this.body = body; } - // A response to a PUSH_PROMISE - public HttpResponseImpl(Response response, - HttpRequestImpl pushRequest, - ImmutableHeaders headers, - Stream<T> stream, - SSLParameters sslParameters, - T body) { - this.responseCode = response.statusCode(); - this.exchange = null; - this.initialRequest = null; // ## fix this - this.finalRequest = pushRequest; - this.headers = headers; - //this.trailers = null; - this.sslParameters = sslParameters; - this.uri = finalRequest.uri(); // TODO: take from headers - this.version = HttpClient.Version.HTTP_2; - this.connection = stream.connection(); - this.stream = stream; - this.body = body; - } +// // A response to a PUSH_PROMISE +// public HttpResponseImpl(Response response, +// HttpRequestImpl pushRequest, +// ImmutableHeaders headers, +// Stream<T> stream, +// SSLParameters sslParameters, +// T body) { +// this.responseCode = response.statusCode(); +// this.exchange = null; +// this.initialRequest = null; // ## fix this +// this.finalRequest = pushRequest; +// this.headers = headers; +// //this.trailers = null; +// this.sslParameters = sslParameters; +// this.uri = finalRequest.uri(); // TODO: take from headers +// this.version = HttpClient.Version.HTTP_2; +// this.connection = stream.connection(); +// this.stream = stream; +// this.body = body; +// } private ExchangeImpl<?> exchangeImpl() { return exchange != null ? exchange.exchImpl : stream; @@ -106,8 +107,8 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider } @Override - public HttpRequest finalRequest() { - return finalRequest; + public Optional<HttpResponse<T>> previousResponse() { + return previousResponse; } @Override @@ -162,31 +163,24 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider } // Http1Exchange may have some remaining bytes in its // internal buffer. - final ByteBuffer remaining =((Http1Exchange<?>)exchImpl).getBuffer(); - rawchan = new RawChannelImpl(exchange.client(), connection, remaining); + Supplier<ByteBuffer> initial = ((Http1Exchange<?>)exchImpl)::drainLeftOverBytes; + rawchan = new RawChannelImpl(exchange.client(), connection, initial); } return rawchan; } @Override - public CompletableFuture<HttpHeaders> trailers() { - throw new UnsupportedOperationException("Not supported yet."); - } - - static void logResponse(Response r) { - if (!Log.requests()) { - return; - } + public String toString() { StringBuilder sb = new StringBuilder(); - String method = r.request().method(); - URI uri = r.request().uri(); + String method = request().method(); + URI uri = request().uri(); String uristring = uri == null ? "" : uri.toString(); sb.append('(') - .append(method) - .append(" ") - .append(uristring) - .append(") ") - .append(r.statusCode()); - Log.logResponse(sb.toString()); + .append(method) + .append(" ") + .append(uristring) + .append(") ") + .append(statusCode()); + return sb.toString(); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java index eaeb0233676..84160fe8f13 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java index 20644d0f9ff..0c23f8d4ab8 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -28,17 +28,14 @@ package jdk.incubator.http; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.OptionalLong; import java.util.TreeMap; import java.util.function.Predicate; -import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; -final class ImmutableHeaders implements HttpHeaders { +final class ImmutableHeaders extends HttpHeaders { private final Map<String, List<String>> map; @@ -71,25 +68,6 @@ final class ImmutableHeaders implements HttpHeaders { this.map = unmodifiableMap(m); } - @Override - public Optional<String> firstValue(String name) { - return allValues(name).stream().findFirst(); - } - - @Override - public OptionalLong firstValueAsLong(String name) { - return allValues(name).stream().mapToLong(Long::valueOf).findFirst(); - } - - @Override - public List<String> allValues(String name) { - requireNonNull(name); - List<String> values = map.get(name); - // Making unmodifiable list out of empty in order to make a list which - // throws UOE unconditionally - return values != null ? values : unmodifiableList(emptyList()); - } - @Override public Map<String, List<String>> map() { return map; diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java index 44e34eef5b9..4c5da8ccf4a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java @@ -26,22 +26,23 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.time.Duration; import java.util.List; import java.security.AccessControlContext; -import java.security.AccessController; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -import java.util.function.BiFunction; import java.util.concurrent.Executor; -import java.util.function.UnaryOperator; - +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import jdk.incubator.http.HttpResponse.UntrustedBodyHandler; import jdk.incubator.http.internal.common.Log; import jdk.incubator.http.internal.common.MinimalFuture; -import jdk.incubator.http.internal.common.Pair; +import jdk.incubator.http.internal.common.ConnectionExpiredException; import jdk.incubator.http.internal.common.Utils; -import static jdk.incubator.http.internal.common.Pair.pair; +import static jdk.incubator.http.internal.common.MinimalFuture.completedFuture; +import static jdk.incubator.http.internal.common.MinimalFuture.failedFuture; /** * Encapsulates multiple Exchanges belonging to one HttpRequestImpl. @@ -53,18 +54,25 @@ import static jdk.incubator.http.internal.common.Pair.pair; */ class MultiExchange<U,T> { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("MultiExchange"::toString, DEBUG); + private final HttpRequest userRequest; // the user request private final HttpRequestImpl request; // a copy of the user request final AccessControlContext acc; final HttpClientImpl client; final HttpResponse.BodyHandler<T> responseHandler; - final ExecutorWrapper execWrapper; final Executor executor; - final HttpResponse.MultiProcessor<U,T> multiResponseHandler; + final HttpResponse.MultiSubscriber<U,T> multiResponseSubscriber; + final AtomicInteger attempts = new AtomicInteger(); HttpRequestImpl currentreq; // used for async only Exchange<T> exchange; // the current exchange Exchange<T> previous; - int attempts; + volatile Throwable retryCause; + volatile boolean expiredOnce; + volatile HttpResponse<T> response = null; + // Maximum number of times a request will be retried/redirected // for any reason @@ -91,93 +99,55 @@ class MultiExchange<U,T> { /** * MultiExchange with one final response. */ - MultiExchange(HttpRequest req, + MultiExchange(HttpRequest userRequest, + HttpRequestImpl requestImpl, HttpClientImpl client, - HttpResponse.BodyHandler<T> responseHandler) { + HttpResponse.BodyHandler<T> responseHandler, + AccessControlContext acc) { this.previous = null; - this.userRequest = req; - this.request = new HttpRequestImpl(req); + this.userRequest = userRequest; + this.request = requestImpl; this.currentreq = request; - this.attempts = 0; this.client = client; this.filters = client.filterChain(); - if (System.getSecurityManager() != null) { - this.acc = AccessController.getContext(); - } else { - this.acc = null; - } - this.execWrapper = new ExecutorWrapper(client.executor(), acc); - this.executor = execWrapper.executor(); + this.acc = acc; + this.executor = client.theExecutor(); this.responseHandler = responseHandler; + if (acc != null) { + // Restricts the file publisher with the senders ACC, if any + if (responseHandler instanceof UntrustedBodyHandler) + ((UntrustedBodyHandler)this.responseHandler).setAccessControlContext(acc); + } this.exchange = new Exchange<>(request, this); - this.multiResponseHandler = null; + this.multiResponseSubscriber = null; this.pushGroup = null; } /** * MultiExchange with multiple responses (HTTP/2 server pushes). */ - MultiExchange(HttpRequest req, + MultiExchange(HttpRequest userRequest, + HttpRequestImpl requestImpl, HttpClientImpl client, - HttpResponse.MultiProcessor<U, T> multiResponseHandler) { + HttpResponse.MultiSubscriber<U, T> multiResponseSubscriber, + AccessControlContext acc) { this.previous = null; - this.userRequest = req; - this.request = new HttpRequestImpl(req); + this.userRequest = userRequest; + this.request = requestImpl; this.currentreq = request; - this.attempts = 0; this.client = client; this.filters = client.filterChain(); - if (System.getSecurityManager() != null) { - this.acc = AccessController.getContext(); - } else { - this.acc = null; - } - this.execWrapper = new ExecutorWrapper(client.executor(), acc); - this.executor = execWrapper.executor(); - this.multiResponseHandler = multiResponseHandler; - this.pushGroup = new PushGroup<>(multiResponseHandler, request); + this.acc = acc; + this.executor = client.theExecutor(); + this.multiResponseSubscriber = multiResponseSubscriber; + this.pushGroup = new PushGroup<>(multiResponseSubscriber, request, acc); this.exchange = new Exchange<>(request, this); this.responseHandler = pushGroup.mainResponseHandler(); } - public HttpResponseImpl<T> response() throws IOException, InterruptedException { - HttpRequestImpl r = request; - if (r.duration() != null) { - timedEvent = new TimedEvent(r.duration()); - client.registerTimer(timedEvent); - } - while (attempts < max_attempts) { - try { - attempts++; - Exchange<T> currExchange = getExchange(); - requestFilters(r); - Response response = currExchange.response(); - HttpRequestImpl newreq = responseFilters(response); - if (newreq == null) { - if (attempts > 1) { - Log.logError("Succeeded on attempt: " + attempts); - } - T body = currExchange.readBody(responseHandler); - cancelTimer(); - return new HttpResponseImpl<>(userRequest, response, body, currExchange); - } - //response.body(HttpResponse.ignoreBody()); - setExchange(new Exchange<>(newreq, this, acc)); - r = newreq; - } catch (IOException e) { - if (cancelled) { - throw new HttpTimeoutException("Request timed out"); - } - throw e; - } - } - cancelTimer(); - throw new IOException("Retry limit exceeded"); - } - - CompletableFuture<Void> multiCompletionCF() { - return pushGroup.groupResult(); - } +// CompletableFuture<Void> multiCompletionCF() { +// return pushGroup.groupResult(); +// } private synchronized Exchange<T> getExchange() { return exchange; @@ -187,15 +157,18 @@ class MultiExchange<U,T> { return client; } - HttpClient.Redirect followRedirects() { - return client.followRedirects(); - } +// HttpClient.Redirect followRedirects() { +// return client.followRedirects(); +// } HttpClient.Version version() { return request.version().orElse(client.version()); } private synchronized void setExchange(Exchange<T> exchange) { + if (this.exchange != null && exchange != this.exchange) { + this.exchange.released(); + } this.exchange = exchange; } @@ -229,114 +202,117 @@ class MultiExchange<U,T> { return null; } - public void cancel() { - cancelled = true; - getExchange().cancel(); - } +// public void cancel() { +// cancelled = true; +// getExchange().cancel(); +// } public void cancel(IOException cause) { cancelled = true; getExchange().cancel(cause); } - public CompletableFuture<HttpResponseImpl<T>> responseAsync() { + public CompletableFuture<HttpResponse<T>> responseAsync() { CompletableFuture<Void> start = new MinimalFuture<>(); - CompletableFuture<HttpResponseImpl<T>> cf = responseAsync0(start); + CompletableFuture<HttpResponse<T>> cf = responseAsync0(start); start.completeAsync( () -> null, executor); // trigger execution return cf; } - private CompletableFuture<HttpResponseImpl<T>> responseAsync0(CompletableFuture<Void> start) { + private CompletableFuture<HttpResponse<T>> + responseAsync0(CompletableFuture<Void> start) { return start.thenCompose( v -> responseAsyncImpl()) - .thenCompose((Response r) -> { - Exchange<T> exch = getExchange(); - return exch.readBodyAsync(responseHandler) - .thenApply((T body) -> new HttpResponseImpl<>(userRequest, r, body, exch)); - }); + .thenCompose((Response r) -> { + Exchange<T> exch = getExchange(); + return exch.readBodyAsync(responseHandler) + .thenApply((T body) -> { + this.response = + new HttpResponseImpl<>(userRequest, r, this.response, body, exch); + return this.response; + }); + }); } CompletableFuture<U> multiResponseAsync() { CompletableFuture<Void> start = new MinimalFuture<>(); - CompletableFuture<HttpResponseImpl<T>> cf = responseAsync0(start); + CompletableFuture<HttpResponse<T>> cf = responseAsync0(start); CompletableFuture<HttpResponse<T>> mainResponse = - cf.thenApply((HttpResponseImpl<T> b) -> { - multiResponseHandler.onResponse(b); - return (HttpResponse<T>)b; - }); - + cf.thenApply(b -> { + multiResponseSubscriber.onResponse(b); + pushGroup.noMorePushes(true); + return b; }); pushGroup.setMainResponse(mainResponse); - // set up house-keeping related to multi-response - mainResponse.thenAccept((r) -> { - // All push promises received by now. - pushGroup.noMorePushes(true); - }); - CompletableFuture<U> res = multiResponseHandler.completion(pushGroup.groupResult(), pushGroup.pushesCF()); + CompletableFuture<U> res = multiResponseSubscriber.completion(pushGroup.groupResult(), + pushGroup.pushesCF()); start.completeAsync( () -> null, executor); // trigger execution return res; } private CompletableFuture<Response> responseAsyncImpl() { CompletableFuture<Response> cf; - if (++attempts > max_attempts) { - cf = MinimalFuture.failedFuture(new IOException("Too many retries")); + if (attempts.incrementAndGet() > max_attempts) { + cf = failedFuture(new IOException("Too many retries", retryCause)); } else { - if (currentreq.duration() != null) { - timedEvent = new TimedEvent(currentreq.duration()); + if (currentreq.timeout().isPresent()) { + timedEvent = new TimedEvent(currentreq.timeout().get()); client.registerTimer(timedEvent); } try { - // 1. Apply request filters + // 1. apply request filters requestFilters(currentreq); } catch (IOException e) { - return MinimalFuture.failedFuture(e); + return failedFuture(e); } Exchange<T> exch = getExchange(); // 2. get response cf = exch.responseAsync() - .thenCompose((Response response) -> { - HttpRequestImpl newrequest = null; - try { - // 3. Apply response filters - newrequest = responseFilters(response); - } catch (IOException e) { - return MinimalFuture.failedFuture(e); - } - // 4. Check filter result and repeat or continue - if (newrequest == null) { - if (attempts > 1) { - Log.logError("Succeeded on attempt: " + attempts); + .thenCompose((Response response) -> { + HttpRequestImpl newrequest; + try { + // 3. apply response filters + newrequest = responseFilters(response); + } catch (IOException e) { + return failedFuture(e); } - return MinimalFuture.completedFuture(response); - } else { - currentreq = newrequest; - setExchange(new Exchange<>(currentreq, this, acc)); - //reads body off previous, and then waits for next response - return responseAsyncImpl(); - } - }) - // 5. Handle errors and cancel any timer set - .handle((response, ex) -> { - cancelTimer(); - if (ex == null) { - assert response != null; - return MinimalFuture.completedFuture(response); - } - // all exceptions thrown are handled here - CompletableFuture<Response> error = getExceptionalCF(ex); - if (error == null) { - return responseAsyncImpl(); - } else { - return error; - } - }) - .thenCompose(UnaryOperator.identity()); + // 4. check filter result and repeat or continue + if (newrequest == null) { + if (attempts.get() > 1) { + Log.logError("Succeeded on attempt: " + attempts); + } + return completedFuture(response); + } else { + this.response = + new HttpResponseImpl<>(currentreq, response, this.response, null, exch); + Exchange<T> oldExch = exch; + return exch.ignoreBody().handle((r,t) -> { + currentreq = newrequest; + expiredOnce = false; + setExchange(new Exchange<>(currentreq, this, acc)); + return responseAsyncImpl(); + }).thenCompose(Function.identity()); + } }) + .handle((response, ex) -> { + // 5. handle errors and cancel any timer set + cancelTimer(); + if (ex == null) { + assert response != null; + return completedFuture(response); + } + // all exceptions thrown are handled here + CompletableFuture<Response> errorCF = getExceptionalCF(ex); + if (errorCF == null) { + return responseAsyncImpl(); + } else { + return errorCF; + } }) + .thenCompose(Function.identity()); } return cf; } /** - * Take a Throwable and return a suitable CompletableFuture that is - * completed exceptionally. + * Takes a Throwable and returns a suitable CompletableFuture that is + * completed exceptionally, or null. */ private CompletableFuture<Response> getExceptionalCF(Throwable t) { if ((t instanceof CompletionException) || (t instanceof ExecutionException)) { @@ -346,8 +322,24 @@ class MultiExchange<U,T> { } if (cancelled && t instanceof IOException) { t = new HttpTimeoutException("request timed out"); + } else if (t instanceof ConnectionExpiredException) { + // allow the retry mechanism to do its work + // ####: method (GET,HEAD, not POST?), no bytes written or read ( differentiate? ) + if (t.getCause() != null) retryCause = t.getCause(); + if (!expiredOnce) { + DEBUG_LOGGER.log(Level.DEBUG, + "MultiExchange: ConnectionExpiredException (async): retrying...", + t); + expiredOnce = true; + return null; + } else { + DEBUG_LOGGER.log(Level.DEBUG, + "MultiExchange: ConnectionExpiredException (async): already retried once.", + t); + if (t.getCause() != null) t = t.getCause(); + } } - return MinimalFuture.failedFuture(t); + return failedFuture(t); } class TimedEvent extends TimeoutEvent { @@ -356,6 +348,9 @@ class MultiExchange<U,T> { } @Override public void handle() { + DEBUG_LOGGER.log(Level.DEBUG, + "Cancelling MultiExchange due to timeout for request %s", + request); cancel(new HttpTimeoutException("request timed out")); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java index 4676d867ef9..7396709e8b0 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -35,7 +35,7 @@ import java.util.concurrent.CompletableFuture; * {@Incubating} * <p> * This is one possible implementation of the aggregate result type {@code <U>} returned - * from {@link HttpClient#sendAsync(HttpRequest,MultiProcessor) }. + * from {@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiSubscriber) }. * The map is indexed by {@link HttpRequest} and each value is a * {@link java.util.concurrent.CompletableFuture}< * {@link HttpResponse}{@code <V>}> @@ -44,9 +44,9 @@ import java.util.concurrent.CompletableFuture; * <p> * {@link CompletableFuture}<{@code MultiMapResult<V>}> * {@link HttpClient#sendAsync(HttpRequest, - * HttpResponse.MultiProcessor) HttpClient.sendAsync(}{@link - * HttpResponse.MultiProcessor#asMap(java.util.function.Function) - * MultiProcessor.asMap(Function)}) + * HttpResponse.MultiSubscriber) HttpClient.sendAsync(}{@link + * HttpResponse.MultiSubscriber#asMap(java.util.function.Function) + * MultiSubscriber.asMap(Function)}) * * @param <V> the response body type for all responses */ @@ -117,4 +117,3 @@ public class MultiMapResult<V> implements Map<HttpRequest,CompletableFuture<Http return map.entrySet(); } } - diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java index 55bf82b29fe..38ecd4065d5 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java @@ -26,73 +26,42 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import jdk.incubator.http.internal.common.AsyncWriteQueue; -import jdk.incubator.http.internal.common.ByteBufferReference; +import jdk.incubator.http.internal.common.FlowTube; import jdk.incubator.http.internal.common.Log; import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Utils; /** - * Plain raw TCP connection direct to destination. 2 modes - * 1) Blocking used by http/1. In this case the connect is actually non - * blocking but the request is sent blocking. The first byte of a response - * is received non-blocking and the remainder of the response is received - * blocking - * 2) Non-blocking. In this case (for http/2) the connection is actually opened - * blocking but all reads and writes are done non-blocking under the - * control of a Http2Connection object. + * Plain raw TCP connection direct to destination. + * The connection operates in asynchronous non-blocking mode. + * All reads and writes are done non-blocking. */ -class PlainHttpConnection extends HttpConnection implements AsyncConnection { +class PlainHttpConnection extends HttpConnection { + private final Object reading = new Object(); protected final SocketChannel chan; + private final FlowTube tube; + private final PlainHttpPublisher writePublisher = new PlainHttpPublisher(reading); private volatile boolean connected; private boolean closed; // should be volatile to provide proper synchronization(visibility) action - private volatile Consumer<ByteBufferReference> asyncReceiver; - private volatile Consumer<Throwable> errorReceiver; - private volatile Supplier<ByteBufferReference> readBufferSupplier; - private boolean asyncReading; - private final AsyncWriteQueue asyncOutputQ = new AsyncWriteQueue(this::asyncOutput); - - private final Object reading = new Object(); - - @Override - public void startReading() { - try { - synchronized(reading) { - asyncReading = true; - } - client.registerEvent(new ReadEvent()); - } catch (IOException e) { - shutdown(); - } - } - - @Override - public void stopAsyncReading() { - synchronized(reading) { - asyncReading = false; - } - client.cancelRegistration(chan); - } - - class ConnectEvent extends AsyncEvent { - CompletableFuture<Void> cf; + final class ConnectEvent extends AsyncEvent { + private final CompletableFuture<Void> cf; ConnectEvent(CompletableFuture<Void> cf) { - super(AsyncEvent.BLOCKING); this.cf = cf; } @@ -109,38 +78,53 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { @Override public void handle() { try { - chan.finishConnect(); - } catch (IOException e) { - cf.completeExceptionally(e); - return; + assert !connected : "Already connected"; + assert !chan.isBlocking() : "Unexpected blocking channel"; + debug.log(Level.DEBUG, "ConnectEvent: finishing connect"); + boolean finished = chan.finishConnect(); + assert finished : "Expected channel to be connected"; + debug.log(Level.DEBUG, + "ConnectEvent: connect finished: %s", finished); + connected = true; + // complete async since the event runs on the SelectorManager thread + cf.completeAsync(() -> null, client().theExecutor()); + } catch (Throwable e) { + client().theExecutor().execute( () -> cf.completeExceptionally(e)); } - connected = true; - cf.complete(null); } @Override - public void abort() { + public void abort(IOException ioe) { close(); + client().theExecutor().execute( () -> cf.completeExceptionally(ioe)); } } @Override public CompletableFuture<Void> connectAsync() { - CompletableFuture<Void> plainFuture = new MinimalFuture<>(); + CompletableFuture<Void> cf = new MinimalFuture<>(); try { - chan.configureBlocking(false); - chan.connect(address); - client.registerEvent(new ConnectEvent(plainFuture)); - } catch (IOException e) { - plainFuture.completeExceptionally(e); + assert !connected : "Already connected"; + assert !chan.isBlocking() : "Unexpected blocking channel"; + boolean finished = false; + PrivilegedExceptionAction<Boolean> pa = () -> chan.connect(address); + try { + finished = AccessController.doPrivileged(pa); + } catch (PrivilegedActionException e) { + cf.completeExceptionally(e.getCause()); + } + if (finished) { + debug.log(Level.DEBUG, "connect finished without blocking"); + connected = true; + cf.complete(null); + } else { + debug.log(Level.DEBUG, "registering connect event"); + client().registerEvent(new ConnectEvent(cf)); + } + } catch (Throwable throwable) { + cf.completeExceptionally(throwable); } - return plainFuture; - } - - @Override - public void connect() throws IOException { - chan.connect(address); - connected = true; + return cf; } @Override @@ -148,106 +132,29 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { return chan; } + @Override + final FlowTube getConnectionFlow() { + return tube; + } + PlainHttpConnection(InetSocketAddress addr, HttpClientImpl client) { super(addr, client); try { this.chan = SocketChannel.open(); + chan.configureBlocking(false); int bufsize = client.getReceiveBufferSize(); chan.setOption(StandardSocketOptions.SO_RCVBUF, bufsize); chan.setOption(StandardSocketOptions.TCP_NODELAY, true); + // wrap the connected channel in a Tube for async reading and writing + tube = new SocketTube(client(), chan, Utils::getBuffer); } catch (IOException e) { throw new InternalError(e); } } @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - if (getMode() != Mode.ASYNC) { - return chan.write(buffers, start, number); - } - // async - buffers = Utils.reduce(buffers, start, number); - long n = Utils.remaining(buffers); - asyncOutputQ.put(ByteBufferReference.toReferences(buffers)); - flushAsync(); - return n; - } + HttpPublisher publisher() { return writePublisher; } - @Override - long write(ByteBuffer buffer) throws IOException { - if (getMode() != Mode.ASYNC) { - return chan.write(buffer); - } - // async - long n = buffer.remaining(); - asyncOutputQ.put(ByteBufferReference.toReferences(buffer)); - flushAsync(); - return n; - } - - // handle registered WriteEvent; invoked from SelectorManager thread - void flushRegistered() { - if (getMode() == Mode.ASYNC) { - try { - asyncOutputQ.flushDelayed(); - } catch (IOException e) { - // Only IOException caused by closed Queue is expected here - shutdown(); - } - } - } - - @Override - public void writeAsync(ByteBufferReference[] buffers) throws IOException { - if (getMode() != Mode.ASYNC) { - chan.write(ByteBufferReference.toBuffers(buffers)); - ByteBufferReference.clear(buffers); - } else { - asyncOutputQ.put(buffers); - } - } - - @Override - public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - if (getMode() != Mode.ASYNC) { - chan.write(ByteBufferReference.toBuffers(buffers)); - ByteBufferReference.clear(buffers); - } else { - // Unordered frames are sent before existing frames. - asyncOutputQ.putFirst(buffers); - } - } - - @Override - public void flushAsync() throws IOException { - if (getMode() == Mode.ASYNC) { - asyncOutputQ.flush(); - } - } - - @Override - public void enableCallback() { - // not used - assert false; - } - - boolean asyncOutput(ByteBufferReference[] refs, AsyncWriteQueue delayCallback) { - try { - ByteBuffer[] bufs = ByteBufferReference.toBuffers(refs); - while (Utils.remaining(bufs) > 0) { - long n = chan.write(bufs); - if (n == 0) { - delayCallback.setDelayed(refs); - client.registerEvent(new WriteEvent()); - return false; - } - } - ByteBufferReference.clear(refs); - } catch (IOException e) { - shutdown(); - } - return true; - } @Override public String toString() { @@ -255,7 +162,7 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { } /** - * Close this connection + * Closes this connection */ @Override public synchronized void close() { @@ -264,80 +171,23 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { } closed = true; try { - Log.logError("Closing: " + toString()); + Log.logTrace("Closing: " + toString()); chan.close(); } catch (IOException e) {} } @Override void shutdownInput() throws IOException { + debug.log(Level.DEBUG, "Shutting down input"); chan.shutdownInput(); } @Override void shutdownOutput() throws IOException { + debug.log(Level.DEBUG, "Shutting down output"); chan.shutdownOutput(); } - void shutdown() { - close(); - errorReceiver.accept(new IOException("Connection aborted")); - } - - void asyncRead() { - synchronized (reading) { - try { - while (asyncReading) { - ByteBufferReference buf = readBufferSupplier.get(); - int n = chan.read(buf.get()); - if (n == -1) { - throw new IOException(); - } - if (n == 0) { - buf.clear(); - return; - } - buf.get().flip(); - asyncReceiver.accept(buf); - } - } catch (IOException e) { - shutdown(); - } - } - } - - @Override - protected ByteBuffer readImpl() throws IOException { - ByteBuffer dst = ByteBuffer.allocate(8192); - int n = readImpl(dst); - if (n > 0) { - return dst; - } else if (n == 0) { - return Utils.EMPTY_BYTEBUFFER; - } else { - return null; - } - } - - private int readImpl(ByteBuffer buf) throws IOException { - int mark = buf.position(); - int n; - // FIXME: this hack works in conjunction with the corresponding change - // in jdk.incubator.http.RawChannel.registerEvent - //if ((n = buffer.remaining()) != 0) { - //buf.put(buffer); - //} else { - n = chan.read(buf); - //} - if (n == -1) { - return -1; - } - Utils.flipToMark(buf, mark); - // String s = "Receive (" + n + " bytes) "; - //debugPrint(s, buf); - return n; - } - @Override ConnectionPool.CacheKey cacheKey() { return new ConnectionPool.CacheKey(address, null); @@ -348,98 +198,6 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { return connected; } - // used for all output in HTTP/2 - class WriteEvent extends AsyncEvent { - WriteEvent() { - super(0); - } - - @Override - public SelectableChannel channel() { - return chan; - } - - @Override - public int interestOps() { - return SelectionKey.OP_WRITE; - } - - @Override - public void handle() { - flushRegistered(); - } - - @Override - public void abort() { - shutdown(); - } - } - - // used for all input in HTTP/2 - class ReadEvent extends AsyncEvent { - ReadEvent() { - super(AsyncEvent.REPEATING); // && !BLOCKING - } - - @Override - public SelectableChannel channel() { - return chan; - } - - @Override - public int interestOps() { - return SelectionKey.OP_READ; - } - - @Override - public void handle() { - asyncRead(); - } - - @Override - public void abort() { - shutdown(); - } - - @Override - public String toString() { - return super.toString() + "/" + chan; - } - } - - // used in blocking channels only - class ReceiveResponseEvent extends AsyncEvent { - CompletableFuture<Void> cf; - - ReceiveResponseEvent(CompletableFuture<Void> cf) { - super(AsyncEvent.BLOCKING); - this.cf = cf; - } - @Override - public SelectableChannel channel() { - return chan; - } - - @Override - public void handle() { - cf.complete(null); - } - - @Override - public int interestOps() { - return SelectionKey.OP_READ; - } - - @Override - public void abort() { - close(); - } - - @Override - public String toString() { - return super.toString() + "/" + chan; - } - } @Override boolean isSecure() { @@ -451,24 +209,91 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection { return false; } - @Override - public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier) { - this.asyncReceiver = asyncReceiver; - this.errorReceiver = errorReceiver; - this.readBufferSupplier = readBufferSupplier; + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. + private static final class PlainDetachedChannel + extends DetachedConnectionChannel { + final PlainHttpConnection plainConnection; + boolean closed; + PlainDetachedChannel(PlainHttpConnection conn) { + // We're handing the connection channel over to a web socket. + // We need the selector manager's thread to stay alive until + // the WebSocket is closed. + conn.client().webSocketOpen(); + this.plainConnection = conn; + } + + @Override + SocketChannel channel() { + return plainConnection.channel(); + } + + @Override + ByteBuffer read() throws IOException { + ByteBuffer dst = ByteBuffer.allocate(8192); + int n = readImpl(dst); + if (n > 0) { + return dst; + } else if (n == 0) { + return Utils.EMPTY_BYTEBUFFER; + } else { + return null; + } + } + + @Override + public void close() { + HttpClientImpl client = plainConnection.client(); + try { + plainConnection.close(); + } finally { + // notify the HttpClientImpl that the websocket is no + // no longer operating. + synchronized(this) { + if (closed == true) return; + closed = true; + } + client.webSocketClose(); + } + } + + @Override + public long write(ByteBuffer[] buffers, int start, int number) + throws IOException + { + return channel().write(buffers, start, number); + } + + @Override + public void shutdownInput() throws IOException { + plainConnection.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + plainConnection.shutdownOutput(); + } + + private int readImpl(ByteBuffer buf) throws IOException { + int mark = buf.position(); + int n; + n = channel().read(buf); + if (n == -1) { + return -1; + } + Utils.flipToMark(buf, mark); + return n; + } } + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. @Override - CompletableFuture<Void> whenReceivingResponse() { - CompletableFuture<Void> cf = new MinimalFuture<>(); - try { - ReceiveResponseEvent evt = new ReceiveResponseEvent(cf); - client.registerEvent(evt); - } catch (IOException e) { - cf.completeExceptionally(e); - } - return cf; + DetachedConnectionChannel detachChannel() { + client().cancelRegistration(channel()); + return new PlainDetachedChannel(this); } + } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java index fd2230241b3..97ed4d05165 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java index c167c53f2b3..98eea0c9818 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java @@ -25,71 +25,27 @@ package jdk.incubator.http; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.MinimalFuture; -import jdk.incubator.http.HttpResponse.BodyHandler; - import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.Supplier; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.MinimalFuture; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; /** * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not * encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy. * Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption. */ -class PlainTunnelingConnection extends HttpConnection implements AsyncConnection { +final class PlainTunnelingConnection extends HttpConnection { final PlainHttpConnection delegate; protected final InetSocketAddress proxyAddr; private volatile boolean connected; - @Override - public CompletableFuture<Void> connectAsync() { - return delegate.connectAsync() - .thenCompose((Void v) -> { - HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address); - MultiExchange<Void,Void> mconnectExchange = new MultiExchange<>(req, client, this::ignore); - return mconnectExchange.responseAsync() - .thenCompose((HttpResponseImpl<Void> resp) -> { - CompletableFuture<Void> cf = new MinimalFuture<>(); - if (resp.statusCode() != 200) { - cf.completeExceptionally(new IOException("Tunnel failed")); - } else { - connected = true; - cf.complete(null); - } - return cf; - }); - }); - } - - private HttpResponse.BodyProcessor<Void> ignore(int status, HttpHeaders hdrs) { - return HttpResponse.BodyProcessor.discard((Void)null); - } - - @Override - public void connect() throws IOException, InterruptedException { - delegate.connect(); - HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address); - MultiExchange<Void,Void> mul = new MultiExchange<>(req, client, BodyHandler.<Void>discard(null)); - Exchange<Void> connectExchange = new Exchange<>(req, mul); - Response r = connectExchange.responseImpl(delegate); - if (r.statusCode() != 200) { - throw new IOException("Tunnel failed"); - } - connected = true; - } - - @Override - boolean connected() { - return connected; - } - protected PlainTunnelingConnection(InetSocketAddress addr, InetSocketAddress proxy, HttpClientImpl client) { @@ -98,41 +54,62 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection delegate = new PlainHttpConnection(proxy, client); } + @Override + public CompletableFuture<Void> connectAsync() { + debug.log(Level.DEBUG, "Connecting plain connection"); + return delegate.connectAsync() + .thenCompose((Void v) -> { + debug.log(Level.DEBUG, "sending HTTP/1.1 CONNECT"); + HttpClientImpl client = client(); + assert client != null; + HttpRequestImpl req = new HttpRequestImpl("CONNECT", address); + MultiExchange<Void,Void> mulEx = new MultiExchange<>(null, req, client, discard(null), null); + Exchange<Void> connectExchange = new Exchange<>(req, mulEx); + + return connectExchange + .responseAsyncImpl(delegate) + .thenCompose((Response resp) -> { + CompletableFuture<Void> cf = new MinimalFuture<>(); + debug.log(Level.DEBUG, "got response: %d", resp.statusCode()); + if (resp.statusCode() != 200) { + cf.completeExceptionally(new IOException( + "Tunnel failed, got: "+ resp.statusCode())); + } else { + // get the initial/remaining bytes + ByteBuffer b = ((Http1Exchange<?>)connectExchange.exchImpl).drainLeftOverBytes(); + int remaining = b.remaining(); + assert remaining == 0: "Unexpected remaining: " + remaining; + connected = true; + cf.complete(null); + } + return cf; + }); + }); + } + + @Override + HttpPublisher publisher() { return delegate.publisher(); } + + @Override + boolean connected() { + return connected; + } + @Override SocketChannel channel() { return delegate.channel(); } + @Override + FlowTube getConnectionFlow() { + return delegate.getConnectionFlow(); + } + @Override ConnectionPool.CacheKey cacheKey() { return new ConnectionPool.CacheKey(null, proxyAddr); } - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - return delegate.write(buffers, start, number); - } - - @Override - long write(ByteBuffer buffer) throws IOException { - return delegate.write(buffer); - } - - @Override - public void writeAsync(ByteBufferReference[] buffers) throws IOException { - delegate.writeAsync(buffers); - } - - @Override - public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - delegate.writeAsyncUnordered(buffers); - } - - @Override - public void flushAsync() throws IOException { - delegate.flushAsync(); - } - @Override public void close() { delegate.close(); @@ -149,16 +126,6 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection delegate.shutdownOutput(); } - @Override - CompletableFuture<Void> whenReceivingResponse() { - return delegate.whenReceivingResponse(); - } - - @Override - protected ByteBuffer readImpl() throws IOException { - return delegate.readImpl(); - } - @Override boolean isSecure() { return false; @@ -169,31 +136,11 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection return true; } + // Support for WebSocket/RawChannelImpl which unfortunately + // still depends on synchronous read/writes. + // It should be removed when RawChannelImpl moves to using asynchronous APIs. @Override - public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, - Consumer<Throwable> errorReceiver, - Supplier<ByteBufferReference> readBufferSupplier) { - delegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); - } - - @Override - public void startReading() { - delegate.startReading(); - } - - @Override - public void stopAsyncReading() { - delegate.stopAsyncReading(); - } - - @Override - public void enableCallback() { - delegate.enableCallback(); - } - - @Override - synchronized void configureMode(Mode mode) throws IOException { - super.configureMode(mode); - delegate.configureMode(mode); + DetachedConnectionChannel detachChannel() { + return delegate.detachChannel(); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java new file mode 100644 index 00000000000..da91dc0ae09 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Executes tasks within a given access control context, and by a given executor. + */ +class PrivilegedExecutor implements Executor { + + /** The underlying executor. May be provided by the user. */ + final Executor executor; + /** The ACC to execute the tasks within. */ + final AccessControlContext acc; + + public PrivilegedExecutor(Executor executor, AccessControlContext acc) { + Objects.requireNonNull(executor); + Objects.requireNonNull(acc); + this.executor = executor; + this.acc = acc; + } + + private static class PrivilegedRunnable implements Runnable { + private final Runnable r; + private final AccessControlContext acc; + PrivilegedRunnable(Runnable r, AccessControlContext acc) { + this.r = r; + this.acc = acc; + } + @Override + public void run() { + PrivilegedAction<Void> pa = () -> { r.run(); return null; }; + AccessController.doPrivileged(pa, acc); + } + } + + @Override + public void execute(Runnable r) { + executor.execute(new PrivilegedRunnable(r, acc)); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java deleted file mode 100644 index 5945cd95c72..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.util.concurrent.Flow; -import java.util.concurrent.atomic.AtomicBoolean; - -// Completes the subscription on first request. Never calls onNext() - -class PseudoPublisher<T> implements Flow.Publisher<T> { - - private final Throwable throwable; - - PseudoPublisher() { - this(null); - } - - PseudoPublisher(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public void subscribe(Flow.Subscriber<? super T> subscriber) { - subscriber.onSubscribe(new Subscription(subscriber)); - } - - private class Subscription implements Flow.Subscription { - - private final Flow.Subscriber<? super T> subscriber; - private final AtomicBoolean done = new AtomicBoolean(); - - Subscription(Flow.Subscriber<? super T> subscriber) { - this.subscriber = subscriber; - } - - @Override - public void request(long n) { - if (done.compareAndSet(false, true)) { - if (n > 0) { - if (throwable == null) { - subscriber.onComplete(); - } else { - subscriber.onError(throwable); - } - } else { - subscriber.onError(new IllegalArgumentException("request(" + n + ")")); - } - } - } - - @Override - public void cancel() { - done.set(true); - } - - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java index e6880fb60cb..89ed515172a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -27,63 +27,112 @@ package jdk.incubator.http; import java.util.Iterator; import java.util.concurrent.Flow; +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.SequentialScheduler; /** - * A Publisher that is expected to run in same thread as subscriber. - * Items are obtained from Iterable. Each new subscription gets a new Iterator. + * A Publisher that publishes items obtained from the given Iterable. Each new + * subscription gets a new Iterator. */ class PullPublisher<T> implements Flow.Publisher<T> { + // Only one of `iterable` and `throwable` can be non-null. throwable is + // non-null when an error has been encountered, by the creator of + // PullPublisher, while subscribing the subscriber, but before subscribe has + // completed. private final Iterable<T> iterable; + private final Throwable throwable; + + PullPublisher(Iterable<T> iterable, Throwable throwable) { + this.iterable = iterable; + this.throwable = throwable; + } PullPublisher(Iterable<T> iterable) { - this.iterable = iterable; + this(iterable, null); } @Override public void subscribe(Flow.Subscriber<? super T> subscriber) { - subscriber.onSubscribe(new Subscription(subscriber, iterable.iterator())); + Subscription sub; + if (throwable != null) { + assert iterable == null : "non-null iterable: " + iterable; + sub = new Subscription(subscriber, null, throwable); + } else { + assert throwable == null : "non-null exception: " + throwable; + sub = new Subscription(subscriber, iterable.iterator(), null); + } + subscriber.onSubscribe(sub); + + if (throwable != null) { + sub.pullScheduler.runOrSchedule(); + } } private class Subscription implements Flow.Subscription { private final Flow.Subscriber<? super T> subscriber; private final Iterator<T> iter; - private boolean done = false; - private long demand = 0; - private int recursion = 0; + private volatile boolean completed; + private volatile boolean cancelled; + private volatile Throwable error; + final SequentialScheduler pullScheduler = new SequentialScheduler(new PullTask()); + private final Demand demand = new Demand(); - Subscription(Flow.Subscriber<? super T> subscriber, Iterator<T> iter) { + Subscription(Flow.Subscriber<? super T> subscriber, + Iterator<T> iter, + Throwable throwable) { this.subscriber = subscriber; this.iter = iter; + this.error = throwable; + } + + final class PullTask extends SequentialScheduler.CompleteRestartableTask { + @Override + protected void run() { + if (completed || cancelled) { + return; + } + + Throwable t = error; + if (t != null) { + completed = true; + pullScheduler.stop(); + subscriber.onError(t); + return; + } + + while (demand.tryDecrement() && !cancelled) { + if (!iter.hasNext()) { + break; + } else { + subscriber.onNext(iter.next()); + } + } + if (!iter.hasNext() && !cancelled) { + completed = true; + pullScheduler.stop(); + subscriber.onComplete(); + } + } } @Override public void request(long n) { - if (done) { - subscriber.onError(new IllegalArgumentException("request(" + n + ")")); - } - demand += n; - recursion ++; - if (recursion > 1) { - return; - } - while (demand > 0) { - done = !iter.hasNext(); - if (done) { - subscriber.onComplete(); - recursion --; - return; - } - subscriber.onNext(iter.next()); - demand --; + if (cancelled) + return; // no-op + + if (n <= 0) { + error = new IllegalArgumentException("illegal non-positive request:" + n); + } else { + demand.increase(n); } + pullScheduler.runOrSchedule(); } @Override public void cancel() { - done = true; + cancelled = true; } - } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java index 48d07481d12..0f38092a487 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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,7 +25,11 @@ package jdk.incubator.http; +import java.security.AccessControlContext; +import java.util.Optional; import java.util.concurrent.CompletableFuture; +import jdk.incubator.http.HttpResponse.BodyHandler; +import jdk.incubator.http.HttpResponse.UntrustedBodyHandler; import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Log; @@ -38,59 +42,82 @@ class PushGroup<U,T> { final CompletableFuture<Void> resultCF; final CompletableFuture<Void> noMorePushesCF; - volatile Throwable error; // any exception that occured during pushes + volatile Throwable error; // any exception that occurred during pushes // CF for main response final CompletableFuture<HttpResponse<T>> mainResponse; - // user's processor object - final HttpResponse.MultiProcessor<U, T> multiProcessor; + // user's subscriber object + final HttpResponse.MultiSubscriber<U, T> multiSubscriber; final HttpResponse.BodyHandler<T> mainBodyHandler; + private final AccessControlContext acc; + int numberOfPushes; int remainingPushes; boolean noMorePushes = false; - PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor, HttpRequestImpl req) { - this(multiProcessor, req, new MinimalFuture<>()); + PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber, + HttpRequestImpl req, + AccessControlContext acc) { + this(multiSubscriber, req, new MinimalFuture<>(), acc); } // Check mainBodyHandler before calling nested constructor. - private PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor, - HttpRequestImpl req, - CompletableFuture<HttpResponse<T>> mainResponse) { - this(multiProcessor, mainResponse, - multiProcessor.onRequest(req).orElseThrow( - () -> new IllegalArgumentException( - "A valid body processor for the main response is required"))); + private PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber, + HttpRequestImpl req, + CompletableFuture<HttpResponse<T>> mainResponse, + AccessControlContext acc) { + this(multiSubscriber, + mainResponse, + multiSubscriber.onRequest(req), + acc); } - // This private constructor is called after all parameters have been - // checked. - private PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor, + // This private constructor is called after all parameters have been checked. + private PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber, CompletableFuture<HttpResponse<T>> mainResponse, - HttpResponse.BodyHandler<T> mainBodyHandler) { + HttpResponse.BodyHandler<T> mainBodyHandler, + AccessControlContext acc) { assert mainResponse != null; // A new instance is created above assert mainBodyHandler != null; // should have been checked above this.resultCF = new MinimalFuture<>(); this.noMorePushesCF = new MinimalFuture<>(); - this.multiProcessor = multiProcessor; + this.multiSubscriber = multiSubscriber; this.mainResponse = mainResponse.thenApply(r -> { - multiProcessor.onResponse(r); + multiSubscriber.onResponse(r); return r; }); this.mainBodyHandler = mainBodyHandler; + if (acc != null) { + // Restricts the file publisher with the senders ACC, if any + if (mainBodyHandler instanceof UntrustedBodyHandler) + ((UntrustedBodyHandler)this.mainBodyHandler).setAccessControlContext(acc); + } + this.acc = acc; } CompletableFuture<Void> groupResult() { return resultCF; } - HttpResponse.MultiProcessor<U, T> processor() { - return multiProcessor; + HttpResponse.MultiSubscriber<U, T> subscriber() { + return multiSubscriber; + } + + Optional<BodyHandler<T>> handlerForPushRequest(HttpRequest ppRequest) { + Optional<BodyHandler<T>> bh = multiSubscriber.onPushPromise(ppRequest); + if (acc != null && bh.isPresent()) { + // Restricts the file publisher with the senders ACC, if any + BodyHandler<T> x = bh.get(); + if (x instanceof UntrustedBodyHandler) + ((UntrustedBodyHandler)x).setAccessControlContext(acc); + bh = Optional.of(x); + } + return bh; } HttpResponse.BodyHandler<T> mainResponseHandler() { @@ -106,18 +133,11 @@ class PushGroup<U,T> { }); } - synchronized CompletableFuture<HttpResponse<T>> mainResponse() { - return mainResponse; - } - synchronized void addPush() { numberOfPushes++; remainingPushes++; } - synchronized int numberOfPushes() { - return numberOfPushes; - } // This is called when the main body response completes because it means // no more PUSH_PROMISEs are possible diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java deleted file mode 100644 index c253a2fff74..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.util.Optional; -import java.util.concurrent.Flow; -import java.util.function.Consumer; - -/** - * A single threaded Publisher which runs in same thread as subscriber. - */ -class PushPublisher<T> extends AbstractPushPublisher<T> { - Subscription subscription; - Flow.Subscriber<? super T> subscriber; - SubscriptionState state; - long demand; - - /** - * Pushes/consumes the incoming objects. - * The consumer blocks until subscriber ready to receive. - */ - @Override - public void acceptData(Optional<T> item) { - SubscriptionState s = this.state; - - if (s == SubscriptionState.CANCELLED) return; - if (s == SubscriptionState.DONE) { - throw new IllegalStateException("subscription complete"); - } - - if (!item.isPresent()) { - subscriber.onComplete(); - this.state = SubscriptionState.DONE; - return; - } - if (demand == 0) { - throw new IllegalStateException("demand == 0"); - } - demand--; - subscriber.onNext(item.get()); - } - - @Override - public Consumer<Optional<T>> asDataConsumer() { - return this::acceptData; - } - - @Override - public void subscribe(Flow.Subscriber<? super T> subscriber) { - subscription = new Subscription(subscriber); - subscriber.onSubscribe(subscription); - } - - private class Subscription implements Flow.Subscription { - - Subscription(Flow.Subscriber<? super T> subscriber) { - PushPublisher.this.subscriber = subscriber; - } - - @Override - public void request(long n) { - demand += n; - } - - @Override - public void cancel() { - state = SubscriptionState.CANCELLED; - } - } - - @Override - public void acceptError(Throwable t) { - if (this.state == SubscriptionState.DONE) { - throw new IllegalStateException("subscription complete"); - } - this.state = SubscriptionState.CANCELLED; - subscriber.onError(t); - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java index 92814fcce8d..32c8f73c7e7 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -32,6 +32,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; +import java.util.function.Supplier; /* * Each RawChannel corresponds to a TCP connection (SocketChannel) but is @@ -41,17 +42,19 @@ import java.nio.channels.SocketChannel; final class RawChannelImpl implements RawChannel { private final HttpClientImpl client; - private final HttpConnection connection; + private final HttpConnection.DetachedConnectionChannel detachedChannel; private final Object initialLock = new Object(); - private ByteBuffer initial; + private Supplier<ByteBuffer> initial; RawChannelImpl(HttpClientImpl client, HttpConnection connection, - ByteBuffer initial) + Supplier<ByteBuffer> initial) throws IOException { this.client = client; - this.connection = connection; + this.detachedChannel = connection.detachChannel(); + this.initial = initial; + SocketChannel chan = connection.channel(); client.cancelRegistration(chan); // Constructing a RawChannel is supposed to have a "hand over" @@ -64,15 +67,11 @@ final class RawChannelImpl implements RawChannel { chan.close(); } catch (IOException e1) { e.addSuppressed(e1); + } finally { + detachedChannel.close(); } throw e; } - // empty the initial buffer into our own copy. - synchronized (initialLock) { - this.initial = initial.hasRemaining() - ? Utils.copy(initial) - : Utils.EMPTY_BYTEBUFFER; - } } private class NonBlockingRawAsyncEvent extends AsyncEvent { @@ -80,13 +79,13 @@ final class RawChannelImpl implements RawChannel { private final RawEvent re; NonBlockingRawAsyncEvent(RawEvent re) { - super(0); // !BLOCKING & !REPEATING + // !BLOCKING & !REPEATING this.re = re; } @Override public SelectableChannel channel() { - return connection.channel(); + return detachedChannel.channel(); } @Override @@ -100,7 +99,7 @@ final class RawChannelImpl implements RawChannel { } @Override - public void abort() { } + public void abort(IOException ioe) { } } @Override @@ -110,8 +109,9 @@ final class RawChannelImpl implements RawChannel { @Override public ByteBuffer read() throws IOException { - assert !connection.channel().isBlocking(); - return connection.read(); + assert !detachedChannel.channel().isBlocking(); + // connection.read() will no longer be available. + return detachedChannel.read(); } @Override @@ -120,7 +120,9 @@ final class RawChannelImpl implements RawChannel { if (initial == null) { throw new IllegalStateException(); } - ByteBuffer ref = initial; + ByteBuffer ref = initial.get(); + ref = ref.hasRemaining() ? Utils.copy(ref) + : Utils.EMPTY_BYTEBUFFER; initial = null; return ref; } @@ -128,21 +130,29 @@ final class RawChannelImpl implements RawChannel { @Override public long write(ByteBuffer[] src, int offset, int len) throws IOException { - return connection.write(src, offset, len); + // this makes the whitebox driver test fail. + return detachedChannel.write(src, offset, len); } @Override public void shutdownInput() throws IOException { - connection.shutdownInput(); + detachedChannel.shutdownInput(); } @Override public void shutdownOutput() throws IOException { - connection.shutdownOutput(); + detachedChannel.shutdownOutput(); } @Override public void close() throws IOException { - connection.close(); + detachedChannel.close(); } + + @Override + public String toString() { + return super.toString()+"("+ detachedChannel.toString() + ")"; + } + + } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java index 1b342f39fb6..e6bce3c5049 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -44,6 +44,9 @@ class RedirectFilter implements HeaderFilter { "jdk.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS ); + // A public no-arg constructor is required by FilterFactory + public RedirectFilter() {} + @Override public synchronized void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException { this.request = r; @@ -84,9 +87,8 @@ class RedirectFilter implements HeaderFilter { private URI getRedirectedURI(HttpHeaders headers) { URI redirectedURI; - String ss = headers.firstValue("Location").orElse("Not present"); redirectedURI = headers.firstValue("Location") - .map((s) -> URI.create(s)) + .map(URI::create) .orElseThrow(() -> new UncheckedIOException( new IOException("Invalid redirection"))); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java similarity index 67% rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java index 851ae4b5f9f..d79310d3549 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -27,44 +27,57 @@ package jdk.incubator.http; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.Path; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Flow; import java.util.function.Supplier; +import jdk.incubator.http.HttpRequest.BodyPublisher; import jdk.incubator.http.internal.common.Utils; -class RequestProcessors { +class RequestPublishers { - static class ByteArrayProcessor implements HttpRequest.BodyProcessor { + static class ByteArrayPublisher implements HttpRequest.BodyPublisher { private volatile Flow.Publisher<ByteBuffer> delegate; private final int length; private final byte[] content; private final int offset; + private final int bufSize; - ByteArrayProcessor(byte[] content) { + ByteArrayPublisher(byte[] content) { this(content, 0, content.length); } - ByteArrayProcessor(byte[] content, int offset, int length) { + ByteArrayPublisher(byte[] content, int offset, int length) { + this(content, offset, length, Utils.BUFSIZE); + } + + /* bufSize exposed for testing purposes */ + ByteArrayPublisher(byte[] content, int offset, int length, int bufSize) { this.content = content; this.offset = offset; this.length = length; + this.bufSize = bufSize; } List<ByteBuffer> copy(byte[] content, int offset, int length) { List<ByteBuffer> bufs = new ArrayList<>(); while (length > 0) { - ByteBuffer b = ByteBuffer.allocate(Math.min(Utils.BUFSIZE, length)); + ByteBuffer b = ByteBuffer.allocate(Math.min(bufSize, length)); int max = b.capacity(); int tocopy = Math.min(max, length); b.put(content, offset, tocopy); @@ -90,12 +103,12 @@ class RequestProcessors { } // This implementation has lots of room for improvement. - static class IterableProcessor implements HttpRequest.BodyProcessor { + static class IterablePublisher implements HttpRequest.BodyPublisher { private volatile Flow.Publisher<ByteBuffer> delegate; private final Iterable<byte[]> content; private volatile long contentLength; - IterableProcessor(Iterable<byte[]> content) { + IterablePublisher(Iterable<byte[]> content) { this.content = content; } @@ -179,14 +192,15 @@ class RequestProcessors { } } - static class StringProcessor extends ByteArrayProcessor { - public StringProcessor(String content, Charset charset) { + static class StringPublisher extends ByteArrayPublisher { + public StringPublisher(String content, Charset charset) { super(content.getBytes(charset)); } } - static class EmptyProcessor implements HttpRequest.BodyProcessor { - PseudoPublisher<ByteBuffer> delegate = new PseudoPublisher<>(); + static class EmptyPublisher implements HttpRequest.BodyPublisher { + private final Flow.Publisher<ByteBuffer> delegate = + new PullPublisher<ByteBuffer>(Collections.emptyList(), null); @Override public long contentLength() { @@ -199,26 +213,42 @@ class RequestProcessors { } } - static class FileProcessor extends InputStreamProcessor - implements HttpRequest.BodyProcessor - { - File file; + static class FilePublisher implements BodyPublisher { + private final File file; + private volatile AccessControlContext acc; - FileProcessor(Path name) { - super(() -> create(name)); + FilePublisher(Path name) { file = name.toFile(); } - static FileInputStream create(Path name) { - try { - return new FileInputStream(name.toFile()); - } catch (FileNotFoundException e) { - throw new UncheckedIOException(e); - } + void setAccessControlContext(AccessControlContext acc) { + this.acc = acc; } + + @Override + public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { + if (System.getSecurityManager() != null && acc == null) + throw new InternalError( + "Unexpected null acc when security manager has been installed"); + + InputStream is; + try { + PrivilegedExceptionAction<FileInputStream> pa = + () -> new FileInputStream(file); + is = AccessController.doPrivileged(pa, acc); + } catch (PrivilegedActionException pae) { + throw new UncheckedIOException((IOException)pae.getCause()); + } + PullPublisher<ByteBuffer> publisher = + new PullPublisher<>(() -> new StreamIterator(is)); + publisher.subscribe(subscriber); + } + @Override public long contentLength() { - return file.length(); + assert System.getSecurityManager() != null ? acc != null: true; + PrivilegedAction<Long> pa = () -> file.length(); + return AccessController.doPrivileged(pa, acc); } } @@ -227,21 +257,26 @@ class RequestProcessors { */ static class StreamIterator implements Iterator<ByteBuffer> { final InputStream is; - ByteBuffer nextBuffer; - boolean need2Read = true; - boolean haveNext; - Throwable error; + final Supplier<? extends ByteBuffer> bufSupplier; + volatile ByteBuffer nextBuffer; + volatile boolean need2Read = true; + volatile boolean haveNext; StreamIterator(InputStream is) { - this.is = is; + this(is, Utils::getBuffer); } - Throwable error() { - return error; + StreamIterator(InputStream is, Supplier<? extends ByteBuffer> bufSupplier) { + this.is = is; + this.bufSupplier = bufSupplier; } +// Throwable error() { +// return error; +// } + private int read() { - nextBuffer = Utils.getBuffer(); + nextBuffer = bufSupplier.get(); nextBuffer.clear(); byte[] buf = nextBuffer.array(); int offset = nextBuffer.arrayOffset(); @@ -257,7 +292,6 @@ class RequestProcessors { nextBuffer.position(0); return n; } catch (IOException ex) { - error = ex; return -1; } } @@ -285,23 +319,28 @@ class RequestProcessors { } - static class InputStreamProcessor implements HttpRequest.BodyProcessor { + static class InputStreamPublisher implements BodyPublisher { private final Supplier<? extends InputStream> streamSupplier; - private Flow.Publisher<ByteBuffer> delegate; - InputStreamProcessor(Supplier<? extends InputStream> streamSupplier) { + InputStreamPublisher(Supplier<? extends InputStream> streamSupplier) { this.streamSupplier = streamSupplier; } @Override - public synchronized void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { - + public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { + PullPublisher<ByteBuffer> publisher; InputStream is = streamSupplier.get(); if (is == null) { - throw new UncheckedIOException(new IOException("no inputstream supplied")); + Throwable t = new IOException("streamSupplier returned null"); + publisher = new PullPublisher<>(null, t); + } else { + publisher = new PullPublisher<>(iterableOf(is), null); } - this.delegate = new PullPublisher<>(() -> new StreamIterator(is)); - delegate.subscribe(subscriber); + publisher.subscribe(subscriber); + } + + protected Iterable<ByteBuffer> iterableOf(InputStream is) { + return () -> new StreamIterator(is); } @Override diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java index 9d317135288..817af717206 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 jdk.incubator.http; +import java.net.URI; + /** * Response headers and status code. */ @@ -59,11 +61,26 @@ class Response { return headers; } - Exchange<?> exchange() { - return exchange; - } +// Exchange<?> exchange() { +// return exchange; +// } int statusCode() { return statusCode; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + String method = request().method(); + URI uri = request().uri(); + String uristring = uri == null ? "" : uri.toString(); + sb.append('(') + .append(method) + .append(" ") + .append(uristring) + .append(") ") + .append(statusCode()); + return sb.toString(); + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java index 7641dcc3134..d95e51d3963 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java @@ -26,8 +26,10 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.nio.ByteBuffer; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; import jdk.incubator.http.internal.common.Utils; @@ -39,45 +41,35 @@ import jdk.incubator.http.internal.common.Utils; */ class ResponseContent { - final HttpResponse.BodyProcessor<?> pusher; - final HttpConnection connection; + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + + final HttpResponse.BodySubscriber<?> pusher; final int contentLength; - ByteBuffer buffer; - //ByteBuffer lastBufferUsed; - final ResponseHeaders headers; - private final Consumer<Optional<ByteBuffer>> dataConsumer; - private final Consumer<IOException> errorConsumer; - private final HttpClientImpl client; + final HttpHeaders headers; // this needs to run before we complete the body // so that connection can be returned to pool private final Runnable onFinished; + private final String dbgTag; ResponseContent(HttpConnection connection, int contentLength, - ResponseHeaders h, - HttpResponse.BodyProcessor<?> userProcessor, - Consumer<Optional<ByteBuffer>> dataConsumer, - Consumer<IOException> errorConsumer, + HttpHeaders h, + HttpResponse.BodySubscriber<?> userSubscriber, Runnable onFinished) { - this.pusher = (HttpResponse.BodyProcessor)userProcessor; - this.connection = connection; + this.pusher = userSubscriber; this.contentLength = contentLength; this.headers = h; - this.dataConsumer = dataConsumer; - this.errorConsumer = errorConsumer; - this.client = connection.client; this.onFinished = onFinished; + this.dbgTag = connection.dbgString() + "/ResponseContent"; } static final int LF = 10; static final int CR = 13; - static final int SP = 0x20; - static final int BUF_SIZE = 1024; - boolean chunkedContent, chunkedContentInitialized; + private boolean chunkedContent, chunkedContentInitialized; - private boolean contentChunked() throws IOException { + boolean contentChunked() throws IOException { if (chunkedContentInitialized) { return chunkedContent; } @@ -98,182 +90,375 @@ class ResponseContent { return chunkedContent; } - /** - * Entry point for pusher. b is an initial ByteBuffer that may - * have some data in it. When this method returns, the body - * has been fully processed. - */ - void pushBody(ByteBuffer b) { - try { - // TODO: check status - if (contentChunked()) { - pushBodyChunked(b); - } else { - pushBodyFixed(b); - } - } catch (IOException t) { - errorConsumer.accept(t); + interface BodyParser extends Consumer<ByteBuffer> { + void onSubscribe(AbstractSubscription sub); + } + + // Returns a parser that will take care of parsing the received byte + // buffers and forward them to the BodySubscriber. + // When the parser is done, it will call onComplete. + // If parsing was successful, the throwable parameter will be null. + // Otherwise it will be the exception that occurred + // Note: revisit: it might be better to use a CompletableFuture than + // a completion handler. + BodyParser getBodyParser(Consumer<Throwable> onComplete) + throws IOException { + if (contentChunked()) { + return new ChunkedBodyParser(onComplete); + } else { + return new FixedLengthBodyParser(contentLength, onComplete); } } - // reads and returns chunklen. Position of chunkbuf is first byte - // of chunk on return. chunklen includes the CR LF at end of chunk - int readChunkLen() throws IOException { - chunklen = 0; - boolean cr = false; - while (true) { - getHunk(); - int c = chunkbuf.get(); - if (cr) { - if (c == LF) { - return chunklen + 2; - } else { - throw new IOException("invalid chunk header"); - } - } - if (c == CR) { - cr = true; - } else { - int digit = toDigit(c); - chunklen = chunklen * 16 + digit; - } - } - } - int chunklen = -1; // number of bytes in chunk (fixed) - int bytesremaining; // number of bytes in chunk left to be read incl CRLF - int bytesread; - ByteBuffer chunkbuf; // initialise + static enum ChunkState {READING_LENGTH, READING_DATA, DONE} + class ChunkedBodyParser implements BodyParser { + final ByteBuffer READMORE = Utils.EMPTY_BYTEBUFFER; + final Consumer<Throwable> onComplete; + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final String dbgTag = ResponseContent.this.dbgTag + "/ChunkedBodyParser"; - // make sure we have at least 1 byte to look at - private void getHunk() throws IOException { - if (chunkbuf == null || !chunkbuf.hasRemaining()) { - chunkbuf = connection.read(); - } - } - - private void consumeBytes(int n) throws IOException { - getHunk(); - while (n > 0) { - int e = Math.min(chunkbuf.remaining(), n); - chunkbuf.position(chunkbuf.position() + e); - n -= e; - if (n > 0) { - getHunk(); - } - } - } - - /** - * Returns a ByteBuffer containing a chunk of data or a "hunk" of data - * (a chunk of a chunk if the chunk size is larger than our ByteBuffers). - * ByteBuffer returned is obtained from response processor. - */ - ByteBuffer readChunkedBuffer() throws IOException { - if (chunklen == -1) { - // new chunk - chunklen = readChunkLen() - 2; - bytesremaining = chunklen; - if (chunklen == 0) { - consumeBytes(2); - return null; - } + volatile Throwable closedExceptionally; + volatile int partialChunklen = 0; // partially read chunk len + volatile int chunklen = -1; // number of bytes in chunk + volatile int bytesremaining; // number of bytes in chunk left to be read incl CRLF + volatile boolean cr = false; // tryReadChunkLength has found CR + volatile int bytesToConsume; // number of bytes that still need to be consumed before proceeding + volatile ChunkState state = ChunkState.READING_LENGTH; // current state + volatile AbstractSubscription sub; + ChunkedBodyParser(Consumer<Throwable> onComplete) { + this.onComplete = onComplete; } - getHunk(); - bytesread = chunkbuf.remaining(); - ByteBuffer returnBuffer = Utils.getBuffer(); - int space = returnBuffer.remaining(); - - int bytes2Copy = Math.min(bytesread, Math.min(bytesremaining, space)); - Utils.copy(chunkbuf, returnBuffer, bytes2Copy); - returnBuffer.flip(); - bytesremaining -= bytes2Copy; - if (bytesremaining == 0) { - consumeBytes(2); - chunklen = -1; + String dbgString() { + return dbgTag; } - return returnBuffer; - } - ByteBuffer initialBuffer; - int fixedBytesReturned; + @Override + public void onSubscribe(AbstractSubscription sub) { + debug.log(Level.DEBUG, () -> "onSubscribe: " + + pusher.getClass().getName()); + pusher.onSubscribe(this.sub = sub); + } - //ByteBuffer getResidue() { - //return lastBufferUsed; - //} - - private void compactBuffer(ByteBuffer buf) { - buf.compact() - .flip(); - } - - /** - * Copies inbuf (numBytes from its position) to new buffer. The returned - * buffer's position is zero and limit is at end (numBytes) - */ - private ByteBuffer copyBuffer(ByteBuffer inbuf, int numBytes) { - ByteBuffer b1 = Utils.getBuffer(); - assert b1.remaining() >= numBytes; - byte[] b = b1.array(); - inbuf.get(b, 0, numBytes); - b1.limit(numBytes); - return b1; - } - - private void pushBodyChunked(ByteBuffer b) throws IOException { - chunkbuf = b; - while (true) { - ByteBuffer b1 = readChunkedBuffer(); - if (b1 != null) { - if (b1.hasRemaining()) { - dataConsumer.accept(Optional.of(b1)); - } - } else { - onFinished.run(); - dataConsumer.accept(Optional.empty()); + @Override + public void accept(ByteBuffer b) { + if (closedExceptionally != null) { + debug.log(Level.DEBUG, () -> "already closed: " + + closedExceptionally); return; } - } - } + boolean completed = false; + try { + List<ByteBuffer> out = new ArrayList<>(); + do { + if (tryPushOneHunk(b, out)) { + // We're done! (true if the final chunk was parsed). + if (!out.isEmpty()) { + // push what we have and complete + // only reduce demand if we actually push something. + // we would not have come here if there was no + // demand. + boolean hasDemand = sub.demand().tryDecrement(); + assert hasDemand; + pusher.onNext(out); + } + debug.log(Level.DEBUG, () -> "done!"); + assert closedExceptionally == null; + assert state == ChunkState.DONE; + onFinished.run(); + pusher.onComplete(); + completed = true; + onComplete.accept(closedExceptionally); // should be null + break; + } + // the buffer may contain several hunks, and therefore + // we must loop while it's not exhausted. + } while (b.hasRemaining()); - private int toDigit(int b) throws IOException { - if (b >= 0x30 && b <= 0x39) { - return b - 0x30; - } - if (b >= 0x41 && b <= 0x46) { - return b - 0x41 + 10; - } - if (b >= 0x61 && b <= 0x66) { - return b - 0x61 + 10; - } - throw new IOException("Invalid chunk header byte " + b); - } - - private void pushBodyFixed(ByteBuffer b) throws IOException { - int remaining = contentLength; - while (b.hasRemaining() && remaining > 0) { - ByteBuffer buffer = Utils.getBuffer(); - int amount = Math.min(b.remaining(), remaining); - Utils.copy(b, buffer, amount); - remaining -= amount; - buffer.flip(); - dataConsumer.accept(Optional.of(buffer)); - } - while (remaining > 0) { - ByteBuffer buffer = connection.read(); - if (buffer == null) - throw new IOException("connection closed"); - - int bytesread = buffer.remaining(); - // assume for now that pipelining not implemented - if (bytesread > remaining) { - throw new IOException("too many bytes read"); + if (!completed && !out.isEmpty()) { + // push what we have. + // only reduce demand if we actually push something. + // we would not have come here if there was no + // demand. + boolean hasDemand = sub.demand().tryDecrement(); + assert hasDemand; + pusher.onNext(out); + } + assert state == ChunkState.DONE || !b.hasRemaining(); + } catch(Throwable t) { + closedExceptionally = t; + if (!completed) onComplete.accept(t); + } + } + + // reads and returns chunklen. Position of chunkbuf is first byte + // of chunk on return. chunklen includes the CR LF at end of chunk + // returns -1 if needs more bytes + private int tryReadChunkLen(ByteBuffer chunkbuf) throws IOException { + assert state == ChunkState.READING_LENGTH; + while (chunkbuf.hasRemaining()) { + int c = chunkbuf.get(); + if (cr) { + if (c == LF) { + return partialChunklen; + } else { + throw new IOException("invalid chunk header"); + } + } + if (c == CR) { + cr = true; + } else { + int digit = toDigit(c); + partialChunklen = partialChunklen * 16 + digit; + } + } + return -1; + } + + + // try to consume as many bytes as specified by bytesToConsume. + // returns the number of bytes that still need to be consumed. + // In practice this method is only called to consume one CRLF pair + // with bytesToConsume set to 2, so it will only return 0 (if completed), + // 1, or 2 (if chunkbuf doesn't have the 2 chars). + private int tryConsumeBytes(ByteBuffer chunkbuf) throws IOException { + int n = bytesToConsume; + if (n > 0) { + int e = Math.min(chunkbuf.remaining(), n); + + // verifies some assertions + // this methods is called only to consume CRLF + if (Utils.ASSERTIONSENABLED) { + assert n <= 2 && e <= 2; + ByteBuffer tmp = chunkbuf.slice(); + // if n == 2 assert that we will first consume CR + assert (n == 2 && e > 0) ? tmp.get() == CR : true; + // if n == 1 || n == 2 && e == 2 assert that we then consume LF + assert (n == 1 || e == 2) ? tmp.get() == LF : true; + } + + chunkbuf.position(chunkbuf.position() + e); + n -= e; + bytesToConsume = n; + } + assert n >= 0; + return n; + } + + /** + * Returns a ByteBuffer containing chunk of data or a "hunk" of data + * (a chunk of a chunk if the chunk size is larger than our ByteBuffers). + * If the given chunk does not have enough data this method return + * an empty ByteBuffer (READMORE). + * If we encounter the final chunk (an empty chunk) this method + * returns null. + */ + ByteBuffer tryReadOneHunk(ByteBuffer chunk) throws IOException { + int unfulfilled = bytesremaining; + int toconsume = bytesToConsume; + ChunkState st = state; + if (st == ChunkState.READING_LENGTH && chunklen == -1) { + debug.log(Level.DEBUG, () -> "Trying to read chunk len" + + " (remaining in buffer:"+chunk.remaining()+")"); + int clen = chunklen = tryReadChunkLen(chunk); + if (clen == -1) return READMORE; + debug.log(Level.DEBUG, "Got chunk len %d", clen); + cr = false; partialChunklen = 0; + unfulfilled = bytesremaining = clen; + if (clen == 0) toconsume = bytesToConsume = 2; // that was the last chunk + else st = state = ChunkState.READING_DATA; // read the data + } + + if (toconsume > 0) { + debug.log(Level.DEBUG, + "Trying to consume bytes: %d (remaining in buffer: %s)", + toconsume, chunk.remaining()); + if (tryConsumeBytes(chunk) > 0) { + return READMORE; + } + } + + toconsume = bytesToConsume; + assert toconsume == 0; + + + if (st == ChunkState.READING_LENGTH) { + // we will come here only if chunklen was 0, after having + // consumed the trailing CRLF + int clen = chunklen; + assert clen == 0; + debug.log(Level.DEBUG, "No more chunks: %d", clen); + // the DONE state is not really needed but it helps with + // assertions... + state = ChunkState.DONE; + return null; + } + + int clen = chunklen; + assert clen > 0; + assert st == ChunkState.READING_DATA; + + ByteBuffer returnBuffer = READMORE; // May be a hunk or a chunk + if (unfulfilled > 0) { + int bytesread = chunk.remaining(); + debug.log(Level.DEBUG, "Reading chunk: available %d, needed %d", + bytesread, unfulfilled); + + int bytes2return = Math.min(bytesread, unfulfilled); + debug.log(Level.DEBUG, "Returning chunk bytes: %d", bytes2return); + returnBuffer = Utils.slice(chunk, bytes2return); + unfulfilled = bytesremaining -= bytes2return; + if (unfulfilled == 0) bytesToConsume = 2; + } + + assert unfulfilled >= 0; + + if (unfulfilled == 0) { + debug.log(Level.DEBUG, + "No more bytes to read - %d yet to consume.", + unfulfilled); + // check whether the trailing CRLF is consumed, try to + // consume it if not. If tryConsumeBytes needs more bytes + // then we will come back here later - skipping the block + // that reads data because remaining==0, and finding + // that the two bytes are now consumed. + if (tryConsumeBytes(chunk) == 0) { + // we're done for this chunk! reset all states and + // prepare to read the next chunk. + chunklen = -1; + partialChunklen = 0; + cr = false; + state = ChunkState.READING_LENGTH; + debug.log(Level.DEBUG, "Ready to read next chunk"); + } + } + if (returnBuffer == READMORE) { + debug.log(Level.DEBUG, "Need more data"); + } + return returnBuffer; + } + + + // Attempt to parse and push one hunk from the buffer. + // Returns true if the final chunk was parsed. + // Returns false if we need to push more chunks. + private boolean tryPushOneHunk(ByteBuffer b, List<ByteBuffer> out) + throws IOException { + assert state != ChunkState.DONE; + ByteBuffer b1 = tryReadOneHunk(b); + if (b1 != null) { + //assert b1.hasRemaining() || b1 == READMORE; + if (b1.hasRemaining()) { + debug.log(Level.DEBUG, "Sending chunk to consumer (%d)", + b1.remaining()); + out.add(b1); + debug.log(Level.DEBUG, "Chunk sent."); + } + return false; // we haven't parsed the final chunk yet. + } else { + return true; // we're done! the final chunk was parsed. + } + } + + private int toDigit(int b) throws IOException { + if (b >= 0x30 && b <= 0x39) { + return b - 0x30; + } + if (b >= 0x41 && b <= 0x46) { + return b - 0x41 + 10; + } + if (b >= 0x61 && b <= 0x66) { + return b - 0x61 + 10; + } + throw new IOException("Invalid chunk header byte " + b); + } + + } + + class FixedLengthBodyParser implements BodyParser { + final int contentLength; + final Consumer<Throwable> onComplete; + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + final String dbgTag = ResponseContent.this.dbgTag + "/FixedLengthBodyParser"; + volatile int remaining; + volatile Throwable closedExceptionally; + volatile AbstractSubscription sub; + FixedLengthBodyParser(int contentLength, Consumer<Throwable> onComplete) { + this.contentLength = this.remaining = contentLength; + this.onComplete = onComplete; + } + + String dbgString() { + return dbgTag; + } + + @Override + public void onSubscribe(AbstractSubscription sub) { + debug.log(Level.DEBUG, () -> "length=" + + contentLength +", onSubscribe: " + + pusher.getClass().getName()); + pusher.onSubscribe(this.sub = sub); + try { + if (contentLength == 0) { + pusher.onComplete(); + onFinished.run(); + onComplete.accept(null); + } + } catch (Throwable t) { + closedExceptionally = t; + try { + pusher.onError(t); + } finally { + onComplete.accept(t); + } + } + } + + @Override + public void accept(ByteBuffer b) { + if (closedExceptionally != null) { + debug.log(Level.DEBUG, () -> "already closed: " + + closedExceptionally); + return; + } + boolean completed = false; + try { + int unfulfilled = remaining; + debug.log(Level.DEBUG, "Parser got %d bytes (%d remaining / %d)", + b.remaining(), unfulfilled, contentLength); + assert unfulfilled != 0 || contentLength == 0 || b.remaining() == 0; + + if (unfulfilled == 0 && contentLength > 0) return; + + if (b.hasRemaining() && unfulfilled > 0) { + // only reduce demand if we actually push something. + // we would not have come here if there was no + // demand. + boolean hasDemand = sub.demand().tryDecrement(); + assert hasDemand; + int amount = Math.min(b.remaining(), unfulfilled); + unfulfilled = remaining -= amount; + ByteBuffer buffer = Utils.slice(b, amount); + pusher.onNext(List.of(buffer)); + } + if (unfulfilled == 0) { + // We're done! All data has been received. + assert closedExceptionally == null; + onFinished.run(); + pusher.onComplete(); + completed = true; + onComplete.accept(closedExceptionally); // should be null + } else { + assert b.remaining() == 0; + } + } catch (Throwable t) { + debug.log(Level.DEBUG, "Unexpected exception", t); + closedExceptionally = t; + if (!completed) { + onComplete.accept(t); + } } - remaining -= bytesread; - dataConsumer.accept(Optional.of(buffer)); } - onFinished.run(); - dataConsumer.accept(Optional.empty()); } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java deleted file mode 100644 index 22db4285e69..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2015, 2017, 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. 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. - */ - -package jdk.incubator.http; - -import sun.net.www.MessageHeader; - -import java.io.IOException; -import java.io.InputStream; -import java.net.ProtocolException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.OptionalLong; - -import static java.lang.String.format; -import static jdk.incubator.http.internal.common.Utils.isValidName; -import static jdk.incubator.http.internal.common.Utils.isValidValue; -import static java.util.Objects.requireNonNull; - -/* - * Reads entire header block off channel, in blocking mode. - * This class is not thread-safe. - */ -final class ResponseHeaders implements HttpHeaders { - - private static final char CR = '\r'; - private static final char LF = '\n'; - - private final ImmutableHeaders delegate; - - /* - * This constructor takes a connection from which the header block is read - * and a buffer which may contain an initial portion of this header block. - * - * After the headers have been parsed (this constructor has returned) the - * leftovers (i.e. data, if any, beyond the header block) are accessible - * from this same buffer from its position to its limit. - */ - ResponseHeaders(HttpConnection connection, ByteBuffer buffer) throws IOException { - requireNonNull(connection); - requireNonNull(buffer); - InputStreamWrapper input = new InputStreamWrapper(connection, buffer); - delegate = ImmutableHeaders.of(parse(input)); - } - - static final class InputStreamWrapper extends InputStream { - final HttpConnection connection; - ByteBuffer buffer; - int lastRead = -1; // last byte read from the buffer - int consumed = 0; // number of bytes consumed. - InputStreamWrapper(HttpConnection connection, ByteBuffer buffer) { - super(); - this.connection = connection; - this.buffer = buffer; - } - @Override - public int read() throws IOException { - if (!buffer.hasRemaining()) { - buffer = connection.read(); - if (buffer == null) { - return lastRead = -1; - } - } - // don't let consumed become positive again if it overflowed - // we just want to make sure that consumed == 1 really means - // that only one byte was consumed. - if (consumed >= 0) consumed++; - return lastRead = buffer.get(); - } - } - - private static void display(Map<String, List<String>> map) { - map.forEach((k,v) -> { - System.out.print (k + ": "); - for (String val : v) { - System.out.print(val + ", "); - } - System.out.println(""); - }); - } - - private Map<String, List<String>> parse(InputStreamWrapper input) - throws IOException - { - // The bulk of work is done by this time-proven class - MessageHeader h = new MessageHeader(); - h.parseHeader(input); - - // When there are no headers (and therefore no body), the status line - // will be followed by an empty CRLF line. - // In that case MessageHeader.parseHeader() will consume the first - // CR character and stop there. In this case we must consume the - // remaining LF. - if (input.consumed == 1 && CR == (char) input.lastRead) { - // MessageHeader will not consume LF if the first character it - // finds is CR. This only happens if there are no headers, and - // only one byte will be consumed from the buffer. In this case - // the next byte MUST be LF - if (input.read() != LF) { - throw new IOException("Unexpected byte sequence when no headers: " - + ((int)CR) + " " + input.lastRead - + "(" + ((int)CR) + " " + ((int)LF) + " expected)"); - } - } - - Map<String, List<String>> rawHeaders = h.getHeaders(); - - // Now some additional post-processing to adapt the results received - // from MessageHeader to what is needed here - Map<String, List<String>> cookedHeaders = new HashMap<>(); - for (Map.Entry<String, List<String>> e : rawHeaders.entrySet()) { - String key = e.getKey(); - if (key == null) { - throw new ProtocolException("Bad header-field"); - } - if (!isValidName(key)) { - throw new ProtocolException(format( - "Bad header-name: '%s'", key)); - } - List<String> newValues = e.getValue(); - for (String v : newValues) { - if (!isValidValue(v)) { - throw new ProtocolException(format( - "Bad header-value for header-name: '%s'", key)); - } - } - String k = key.toLowerCase(Locale.US); - cookedHeaders.merge(k, newValues, - (v1, v2) -> { - ArrayList<String> newV = new ArrayList<>(); - if (v1 != null) { - newV.addAll(v1); - } - newV.addAll(v2); - return newV; - }); - } - return cookedHeaders; - } - - int getContentLength() throws IOException { - return (int) firstValueAsLong("Content-Length").orElse(-1); - } - - @Override - public Optional<String> firstValue(String name) { - return delegate.firstValue(name); - } - - @Override - public OptionalLong firstValueAsLong(String name) { - return delegate.firstValueAsLong(name); - } - - @Override - public List<String> allValues(String name) { - return delegate.allValues(name); - } - - @Override - public Map<String, List<String>> map() { - return delegate.map(); - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java deleted file mode 100644 index dd21b1aabf1..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2016, 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. 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. - */ - -package jdk.incubator.http; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Flow; -import java.util.function.Consumer; -import java.util.function.Function; -import jdk.incubator.http.internal.common.MinimalFuture; -import jdk.incubator.http.internal.common.Utils; -import jdk.incubator.http.internal.common.Log; - -class ResponseProcessors { - - static class ConsumerProcessor implements HttpResponse.BodyProcessor<Void> { - private final Consumer<Optional<byte[]>> consumer; - private Flow.Subscription subscription; - private final CompletableFuture<Void> result = new MinimalFuture<>(); - - ConsumerProcessor(Consumer<Optional<byte[]>> consumer) { - this.consumer = consumer; - } - - @Override - public CompletionStage<Void> getBody() { - return result; - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(1); - } - - @Override - public void onNext(ByteBuffer item) { - byte[] buf = new byte[item.remaining()]; - item.get(buf); - consumer.accept(Optional.of(buf)); - subscription.request(1); - } - - @Override - public void onError(Throwable throwable) { - result.completeExceptionally(throwable); - } - - @Override - public void onComplete() { - consumer.accept(Optional.empty()); - result.complete(null); - } - - } - - static class PathProcessor implements HttpResponse.BodyProcessor<Path> { - - private final Path file; - private final CompletableFuture<Path> result = new MinimalFuture<>(); - - private Flow.Subscription subscription; - private FileChannel out; - private final OpenOption[] options; - - PathProcessor(Path file, OpenOption... options) { - this.file = file; - this.options = options; - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - try { - out = FileChannel.open(file, options); - } catch (IOException e) { - result.completeExceptionally(e); - subscription.cancel(); - return; - } - subscription.request(1); - } - - @Override - public void onNext(ByteBuffer item) { - try { - out.write(item); - } catch (IOException ex) { - Utils.close(out); - subscription.cancel(); - result.completeExceptionally(ex); - } - subscription.request(1); - } - - @Override - public void onError(Throwable e) { - result.completeExceptionally(e); - Utils.close(out); - } - - @Override - public void onComplete() { - Utils.close(out); - result.complete(file); - } - - @Override - public CompletionStage<Path> getBody() { - return result; - } - } - - static class ByteArrayProcessor<T> implements HttpResponse.BodyProcessor<T> { - private final Function<byte[], T> finisher; - private final CompletableFuture<T> result = new MinimalFuture<>(); - private final List<ByteBuffer> received = new ArrayList<>(); - - private Flow.Subscription subscription; - - ByteArrayProcessor(Function<byte[],T> finisher) { - this.finisher = finisher; - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - if (this.subscription != null) { - subscription.cancel(); - return; - } - this.subscription = subscription; - // We can handle whatever you've got - subscription.request(Long.MAX_VALUE); - } - - @Override - public void onNext(ByteBuffer item) { - // incoming buffers are allocated by http client internally, - // and won't be used anywhere except this place. - // So it's free simply to store them for further processing. - if(item.hasRemaining()) { - received.add(item); - } - } - - @Override - public void onError(Throwable throwable) { - received.clear(); - result.completeExceptionally(throwable); - } - - static private byte[] join(List<ByteBuffer> bytes) { - int size = Utils.remaining(bytes); - byte[] res = new byte[size]; - int from = 0; - for (ByteBuffer b : bytes) { - int l = b.remaining(); - b.get(res, from, l); - from += l; - } - return res; - } - - @Override - public void onComplete() { - try { - result.complete(finisher.apply(join(received))); - received.clear(); - } catch (IllegalArgumentException e) { - result.completeExceptionally(e); - } - } - - @Override - public CompletionStage<T> getBody() { - return result; - } - } - - static class MultiProcessorImpl<V> implements HttpResponse.MultiProcessor<MultiMapResult<V>,V> { - private final MultiMapResult<V> results; - private final Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler; - private final boolean completion; // aggregate completes on last PP received or overall completion - - MultiProcessorImpl(Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler, boolean completion) { - this.results = new MultiMapResult<V>(new ConcurrentHashMap<>()); - this.pushHandler = pushHandler; - this.completion = completion; - } - - @Override - public Optional<HttpResponse.BodyHandler<V>> onRequest(HttpRequest request) { - return pushHandler.apply(request); - } - - @Override - public void onResponse(HttpResponse<V> response) { - results.put(response.request(), CompletableFuture.completedFuture(response)); - } - - @Override - public void onError(HttpRequest request, Throwable t) { - results.put(request, MinimalFuture.failedFuture(t)); - } - - @Override - public CompletableFuture<MultiMapResult<V>> completion( - CompletableFuture<Void> onComplete, CompletableFuture<Void> onFinalPushPromise) { - if (completion) - return onComplete.thenApply((ignored)-> results); - else - return onFinalPushPromise.thenApply((ignored) -> results); - } - } - - static class MultiFile { - - final Path pathRoot; - - MultiFile(Path destination) { - if (!destination.toFile().isDirectory()) - throw new UncheckedIOException(new IOException("destination is not a directory")); - pathRoot = destination; - } - - Optional<HttpResponse.BodyHandler<Path>> handlePush(HttpRequest request) { - final URI uri = request.uri(); - String path = uri.getPath(); - while (path.startsWith("/")) - path = path.substring(1); - Path p = pathRoot.resolve(path); - if (Log.trace()) { - Log.logTrace("Creating file body processor for URI={0}, path={1}", - uri, p); - } - try { - Files.createDirectories(p.getParent()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - - final HttpResponse.BodyHandler<Path> proc = - HttpResponse.BodyHandler.asFile(p); - - return Optional.of(proc); - } - } - - /** - * Currently this consumes all of the data and ignores it - */ - static class NullProcessor<T> implements HttpResponse.BodyProcessor<T> { - - Flow.Subscription subscription; - final CompletableFuture<T> cf = new MinimalFuture<>(); - final Optional<T> result; - - NullProcessor(Optional<T> result) { - this.result = result; - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - this.subscription = subscription; - subscription.request(Long.MAX_VALUE); - } - - @Override - public void onNext(ByteBuffer item) { - // TODO: check whether this should consume the buffer, as in: - item.position(item.limit()); - } - - @Override - public void onError(Throwable throwable) { - cf.completeExceptionally(throwable); - } - - @Override - public void onComplete() { - if (result.isPresent()) { - cf.complete(result.get()); - } else { - cf.complete(null); - } - } - - @Override - public CompletionStage<T> getBody() { - return cf; - } - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java new file mode 100644 index 00000000000..466f8d82c06 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2016, 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Function; +import jdk.incubator.http.internal.common.MinimalFuture; +import jdk.incubator.http.internal.common.Utils; + +class ResponseSubscribers { + + static class ConsumerSubscriber implements HttpResponse.BodySubscriber<Void> { + private final Consumer<Optional<byte[]>> consumer; + private Flow.Subscription subscription; + private final CompletableFuture<Void> result = new MinimalFuture<>(); + private final AtomicBoolean subscribed = new AtomicBoolean(); + + ConsumerSubscriber(Consumer<Optional<byte[]>> consumer) { + this.consumer = consumer; + } + + @Override + public CompletionStage<Void> getBody() { + return result; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + if (!subscribed.compareAndSet(false, true)) { + subscription.cancel(); + } else { + this.subscription = subscription; + subscription.request(1); + } + } + + @Override + public void onNext(List<ByteBuffer> items) { + for (ByteBuffer item : items) { + byte[] buf = new byte[item.remaining()]; + item.get(buf); + consumer.accept(Optional.of(buf)); + } + subscription.request(1); + } + + @Override + public void onError(Throwable throwable) { + result.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + consumer.accept(Optional.empty()); + result.complete(null); + } + + } + + static class PathSubscriber implements HttpResponse.BodySubscriber<Path> { + + private final Path file; + private final CompletableFuture<Path> result = new MinimalFuture<>(); + + private volatile Flow.Subscription subscription; + private volatile FileChannel out; + private volatile AccessControlContext acc; + private final OpenOption[] options; + + PathSubscriber(Path file, OpenOption... options) { + this.file = file; + this.options = options; + } + + void setAccessControlContext(AccessControlContext acc) { + this.acc = acc; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + if (System.getSecurityManager() != null && acc == null) + throw new InternalError( + "Unexpected null acc when security manager has been installed"); + + this.subscription = subscription; + try { + PrivilegedExceptionAction<FileChannel> pa = + () -> FileChannel.open(file, options); + out = AccessController.doPrivileged(pa, acc); + } catch (PrivilegedActionException pae) { + Throwable t = pae.getCause() != null ? pae.getCause() : pae; + result.completeExceptionally(t); + subscription.cancel(); + return; + } + subscription.request(1); + } + + @Override + public void onNext(List<ByteBuffer> items) { + try { + out.write(items.toArray(Utils.EMPTY_BB_ARRAY)); + } catch (IOException ex) { + Utils.close(out); + subscription.cancel(); + result.completeExceptionally(ex); + } + subscription.request(1); + } + + @Override + public void onError(Throwable e) { + result.completeExceptionally(e); + Utils.close(out); + } + + @Override + public void onComplete() { + Utils.close(out); + result.complete(file); + } + + @Override + public CompletionStage<Path> getBody() { + return result; + } + } + + static class ByteArraySubscriber<T> implements HttpResponse.BodySubscriber<T> { + private final Function<byte[], T> finisher; + private final CompletableFuture<T> result = new MinimalFuture<>(); + private final List<ByteBuffer> received = new ArrayList<>(); + + private volatile Flow.Subscription subscription; + + ByteArraySubscriber(Function<byte[],T> finisher) { + this.finisher = finisher; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + if (this.subscription != null) { + subscription.cancel(); + return; + } + this.subscription = subscription; + // We can handle whatever you've got + subscription.request(Long.MAX_VALUE); + } + + @Override + public void onNext(List<ByteBuffer> items) { + // incoming buffers are allocated by http client internally, + // and won't be used anywhere except this place. + // So it's free simply to store them for further processing. + assert Utils.hasRemaining(items); + Utils.accumulateBuffers(received, items); + } + + @Override + public void onError(Throwable throwable) { + received.clear(); + result.completeExceptionally(throwable); + } + + static private byte[] join(List<ByteBuffer> bytes) { + int size = Utils.remaining(bytes, Integer.MAX_VALUE); + byte[] res = new byte[size]; + int from = 0; + for (ByteBuffer b : bytes) { + int l = b.remaining(); + b.get(res, from, l); + from += l; + } + return res; + } + + @Override + public void onComplete() { + try { + result.complete(finisher.apply(join(received))); + received.clear(); + } catch (IllegalArgumentException e) { + result.completeExceptionally(e); + } + } + + @Override + public CompletionStage<T> getBody() { + return result; + } + } + + /** + * An InputStream built on top of the Flow API. + */ + static class HttpResponseInputStream extends InputStream + implements HttpResponse.BodySubscriber<InputStream> + { + final static boolean DEBUG = Utils.DEBUG; + final static int MAX_BUFFERS_IN_QUEUE = 1; // lock-step with the producer + + // An immutable ByteBuffer sentinel to mark that the last byte was received. + private static final ByteBuffer LAST_BUFFER = ByteBuffer.wrap(new byte[0]); + private static final List<ByteBuffer> LAST_LIST = List.of(LAST_BUFFER); + private static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("HttpResponseInputStream"::toString, DEBUG); + + // A queue of yet unprocessed ByteBuffers received from the flow API. + private final BlockingQueue<List<ByteBuffer>> buffers; + private volatile Flow.Subscription subscription; + private volatile boolean closed; + private volatile Throwable failed; + private volatile Iterator<ByteBuffer> currentListItr; + private volatile ByteBuffer currentBuffer; + private final AtomicBoolean subscribed = new AtomicBoolean(); + + HttpResponseInputStream() { + this(MAX_BUFFERS_IN_QUEUE); + } + + HttpResponseInputStream(int maxBuffers) { + int capacity = (maxBuffers <= 0 ? MAX_BUFFERS_IN_QUEUE : maxBuffers); + // 1 additional slot needed for LAST_LIST added by onComplete + this.buffers = new ArrayBlockingQueue<>(capacity + 1); + } + + @Override + public CompletionStage<InputStream> getBody() { + // Returns the stream immediately, before the + // response body is received. + // This makes it possible for senAsync().get().body() + // to complete before the response body is received. + return CompletableFuture.completedStage(this); + } + + // Returns the current byte buffer to read from. + // If the current buffer has no remaining data, this method will take the + // next buffer from the buffers queue, possibly blocking until + // a new buffer is made available through the Flow API, or the + // end of the flow has been reached. + private ByteBuffer current() throws IOException { + while (currentBuffer == null || !currentBuffer.hasRemaining()) { + // Check whether the stream is closed or exhausted + if (closed || failed != null) { + throw new IOException("closed", failed); + } + if (currentBuffer == LAST_BUFFER) break; + + try { + if (currentListItr == null || !currentListItr.hasNext()) { + // Take a new list of buffers from the queue, blocking + // if none is available yet... + + DEBUG_LOGGER.log(Level.DEBUG, "Taking list of Buffers"); + List<ByteBuffer> lb = buffers.take(); + currentListItr = lb.iterator(); + DEBUG_LOGGER.log(Level.DEBUG, "List of Buffers Taken"); + + // Check whether an exception was encountered upstream + if (closed || failed != null) + throw new IOException("closed", failed); + + // Check whether we're done. + if (lb == LAST_LIST) { + currentListItr = null; + currentBuffer = LAST_BUFFER; + break; + } + + // Request another upstream item ( list of buffers ) + Flow.Subscription s = subscription; + if (s != null) { + DEBUG_LOGGER.log(Level.DEBUG, "Increased demand by 1"); + s.request(1); + } + assert currentListItr != null; + if (lb.isEmpty()) continue; + } + assert currentListItr != null; + assert currentListItr.hasNext(); + DEBUG_LOGGER.log(Level.DEBUG, "Next Buffer"); + currentBuffer = currentListItr.next(); + } catch (InterruptedException ex) { + // continue + } + } + assert currentBuffer == LAST_BUFFER || currentBuffer.hasRemaining(); + return currentBuffer; + } + + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + // get the buffer to read from, possibly blocking if + // none is available + ByteBuffer buffer; + if ((buffer = current()) == LAST_BUFFER) return -1; + + // don't attempt to read more than what is available + // in the current buffer. + int read = Math.min(buffer.remaining(), len); + assert read > 0 && read <= buffer.remaining(); + + // buffer.get() will do the boundary check for us. + buffer.get(bytes, off, read); + return read; + } + + @Override + public int read() throws IOException { + ByteBuffer buffer; + if ((buffer = current()) == LAST_BUFFER) return -1; + return buffer.get() & 0xFF; + } + + @Override + public void onSubscribe(Flow.Subscription s) { + try { + if (!subscribed.compareAndSet(false, true)) { + s.cancel(); + } else { + // check whether the stream is already closed. + // if so, we should cancel the subscription + // immediately. + boolean closed; + synchronized (this) { + closed = this.closed; + if (!closed) { + this.subscription = s; + } + } + if (closed) { + s.cancel(); + return; + } + assert buffers.remainingCapacity() > 1; // should contain at least 2 + DEBUG_LOGGER.log(Level.DEBUG, () -> "onSubscribe: requesting " + + Math.max(1, buffers.remainingCapacity() - 1)); + s.request(Math.max(1, buffers.remainingCapacity() - 1)); + } + } catch (Throwable t) { + failed = t; + try { + close(); + } catch (IOException x) { + // OK + } finally { + onError(t); + } + } + } + + @Override + public void onNext(List<ByteBuffer> t) { + Objects.requireNonNull(t); + try { + DEBUG_LOGGER.log(Level.DEBUG, "next item received"); + if (!buffers.offer(t)) { + throw new IllegalStateException("queue is full"); + } + DEBUG_LOGGER.log(Level.DEBUG, "item offered"); + } catch (Throwable ex) { + failed = ex; + try { + close(); + } catch (IOException ex1) { + // OK + } finally { + onError(ex); + } + } + } + + @Override + public void onError(Throwable thrwbl) { + subscription = null; + failed = Objects.requireNonNull(thrwbl); + // The client process that reads the input stream might + // be blocked in queue.take(). + // Tries to offer LAST_LIST to the queue. If the queue is + // full we don't care if we can't insert this buffer, as + // the client can't be blocked in queue.take() in that case. + // Adding LAST_LIST to the queue is harmless, as the client + // should find failed != null before handling LAST_LIST. + buffers.offer(LAST_LIST); + } + + @Override + public void onComplete() { + subscription = null; + onNext(LAST_LIST); + } + + @Override + public void close() throws IOException { + Flow.Subscription s; + synchronized (this) { + if (closed) return; + closed = true; + s = subscription; + subscription = null; + } + // s will be null if already completed + try { + if (s != null) { + s.cancel(); + } + } finally { + buffers.offer(LAST_LIST); + super.close(); + } + } + + } + + static class MultiSubscriberImpl<V> + implements HttpResponse.MultiSubscriber<MultiMapResult<V>,V> + { + private final MultiMapResult<V> results; + private final Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler; + private final Function<HttpRequest,HttpResponse.BodyHandler<V>> requestHandler; + private final boolean completion; // aggregate completes on last PP received or overall completion + + MultiSubscriberImpl( + Function<HttpRequest,HttpResponse.BodyHandler<V>> requestHandler, + Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler, boolean completion) { + this.results = new MultiMapResult<>(new ConcurrentHashMap<>()); + this.requestHandler = requestHandler; + this.pushHandler = pushHandler; + this.completion = completion; + } + + @Override + public HttpResponse.BodyHandler<V> onRequest(HttpRequest request) { + CompletableFuture<HttpResponse<V>> cf = MinimalFuture.newMinimalFuture(); + results.put(request, cf); + return requestHandler.apply(request); + } + + @Override + public Optional<HttpResponse.BodyHandler<V>> onPushPromise(HttpRequest push) { + CompletableFuture<HttpResponse<V>> cf = MinimalFuture.newMinimalFuture(); + results.put(push, cf); + return pushHandler.apply(push); + } + + @Override + public void onResponse(HttpResponse<V> response) { + CompletableFuture<HttpResponse<V>> cf = results.get(response.request()); + cf.complete(response); + } + + @Override + public void onError(HttpRequest request, Throwable t) { + CompletableFuture<HttpResponse<V>> cf = results.get(request); + cf.completeExceptionally(t); + } + + @Override + public CompletableFuture<MultiMapResult<V>> completion( + CompletableFuture<Void> onComplete, CompletableFuture<Void> onFinalPushPromise) { + if (completion) + return onComplete.thenApply((ignored)-> results); + else + return onFinalPushPromise.thenApply((ignored) -> results); + } + } + + /** + * Currently this consumes all of the data and ignores it + */ + static class NullSubscriber<T> implements HttpResponse.BodySubscriber<T> { + + private final CompletableFuture<T> cf = new MinimalFuture<>(); + private final Optional<T> result; + private final AtomicBoolean subscribed = new AtomicBoolean(); + + NullSubscriber(Optional<T> result) { + this.result = result; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + if (!subscribed.compareAndSet(false, true)) { + subscription.cancel(); + } else { + subscription.request(Long.MAX_VALUE); + } + } + + @Override + public void onNext(List<ByteBuffer> items) { + Objects.requireNonNull(items); + } + + @Override + public void onError(Throwable throwable) { + cf.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + if (result.isPresent()) { + cf.complete(result.get()); + } else { + cf.complete(null); + } + } + + @Override + public CompletionStage<T> getBody() { + return cf; + } + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java deleted file mode 100644 index 9eb6a37e250..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2015, 2017, 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. 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. - */ - -package jdk.incubator.http; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; -import java.util.concurrent.CompletableFuture; -import javax.net.ssl.SSLEngineResult.Status; -import javax.net.ssl.SSLParameters; -import jdk.incubator.http.SSLDelegate.WrapperResult; - -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.MinimalFuture; -import jdk.incubator.http.internal.common.Utils; - -/** - * An SSL connection built on a Plain TCP connection. - */ -class SSLConnection extends HttpConnection { - - PlainHttpConnection delegate; - SSLDelegate sslDelegate; - final String[] alpn; - final String serverName; - - @Override - public CompletableFuture<Void> connectAsync() { - return delegate.connectAsync() - .thenCompose((Void v) -> - MinimalFuture.supply( () -> { - this.sslDelegate = new SSLDelegate(delegate.channel(), client, alpn, serverName); - return null; - })); - } - - @Override - public void connect() throws IOException { - delegate.connect(); - this.sslDelegate = new SSLDelegate(delegate.channel(), client, alpn, serverName); - } - - SSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) { - super(addr, client); - this.alpn = ap; - this.serverName = Utils.getServerName(addr); - delegate = new PlainHttpConnection(addr, client); - } - - /** - * Create an SSLConnection from an existing connected AsyncSSLConnection. - * Used when downgrading from HTTP/2 to HTTP/1.1 - */ - SSLConnection(AsyncSSLConnection c) { - super(c.address, c.client); - this.delegate = c.plainConnection(); - AsyncSSLDelegate adel = c.sslDelegate(); - this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName); - this.alpn = adel.alpn; - this.serverName = adel.serverName; - } - - @Override - SSLParameters sslParameters() { - return sslDelegate.getSSLParameters(); - } - - @Override - public String toString() { - return "SSLConnection: " + super.toString(); - } - - private static long countBytes(ByteBuffer[] buffers, int start, int length) { - long c = 0; - for (int i=0; i<length; i++) { - c+= buffers[start+i].remaining(); - } - return c; - } - - @Override - ConnectionPool.CacheKey cacheKey() { - return ConnectionPool.cacheKey(address, null); - } - - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - //debugPrint("Send", buffers, start, number); - long l = countBytes(buffers, start, number); - WrapperResult r = sslDelegate.sendData(buffers, start, number); - if (r.result.getStatus() == Status.CLOSED) { - if (l > 0) { - throw new IOException("SSLHttpConnection closed"); - } - } - return l; - } - - @Override - long write(ByteBuffer buffer) throws IOException { - //debugPrint("Send", buffer); - long l = buffer.remaining(); - WrapperResult r = sslDelegate.sendData(buffer); - if (r.result.getStatus() == Status.CLOSED) { - if (l > 0) { - throw new IOException("SSLHttpConnection closed"); - } - } - return l; - } - - @Override - void writeAsync(ByteBufferReference[] buffers) throws IOException { - write(ByteBufferReference.toBuffers(buffers), 0, buffers.length); - } - - @Override - void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - write(ByteBufferReference.toBuffers(buffers), 0, buffers.length); - } - - @Override - void flushAsync() throws IOException { - // nothing to do - } - - @Override - public void close() { - Utils.close(delegate.channel()); - } - - @Override - void shutdownInput() throws IOException { - delegate.channel().shutdownInput(); - } - - @Override - void shutdownOutput() throws IOException { - delegate.channel().shutdownOutput(); - } - - @Override - protected ByteBuffer readImpl() throws IOException { - WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192)); - // TODO: check for closure - int n = r.result.bytesProduced(); - if (n > 0) { - return r.buf; - } else if (n == 0) { - return Utils.EMPTY_BYTEBUFFER; - } else { - return null; - } - } - - @Override - boolean connected() { - return delegate.connected(); - } - - @Override - SocketChannel channel() { - return delegate.channel(); - } - - @Override - CompletableFuture<Void> whenReceivingResponse() { - return delegate.whenReceivingResponse(); - } - - @Override - boolean isSecure() { - return true; - } - - @Override - boolean isProxied() { - return false; - } - -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java index 85f206fae05..0af6fd6d6f4 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java @@ -28,8 +28,6 @@ package jdk.incubator.http; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.util.Arrays; -import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -41,60 +39,56 @@ import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; /** * Implements the mechanics of SSL by managing an SSLEngine object. - * One of these is associated with each SSLConnection. + * <p> + * This class is only used to implement the {@link + * AbstractAsyncSSLConnection.SSLConnectionChannel} which is handed of + * to RawChannelImpl when creating a WebSocket. */ class SSLDelegate { final SSLEngine engine; final EngineWrapper wrapper; final Lock handshaking = new ReentrantLock(); - final SSLParameters sslParameters; final SocketChannel chan; - final HttpClientImpl client; - final String serverName; - SSLDelegate(SSLEngine eng, SocketChannel chan, HttpClientImpl client, String sn) + SSLDelegate(SSLEngine eng, SocketChannel chan) { this.engine = eng; this.chan = chan; - this.client = client; this.wrapper = new EngineWrapper(chan, engine); - this.sslParameters = engine.getSSLParameters(); - this.serverName = sn; } // alpn[] may be null - SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn) - throws IOException - { - serverName = sn; - SSLContext context = client.sslContext(); - engine = context.createSSLEngine(); - engine.setUseClientMode(true); - SSLParameters sslp = client.sslParameters() - .orElseGet(context::getSupportedSSLParameters); - sslParameters = Utils.copySSLParameters(sslp); - if (sn != null) { - SNIHostName sni = new SNIHostName(sn); - sslParameters.setServerNames(List.of(sni)); - } - if (alpn != null) { - sslParameters.setApplicationProtocols(alpn); - Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn)); - } else { - Log.logSSL("SSLDelegate: No application protocols proposed"); - } - engine.setSSLParameters(sslParameters); - wrapper = new EngineWrapper(chan, engine); - this.chan = chan; - this.client = client; - } +// SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn) +// throws IOException +// { +// serverName = sn; +// SSLContext context = client.sslContext(); +// engine = context.createSSLEngine(); +// engine.setUseClientMode(true); +// SSLParameters sslp = client.sslParameters(); +// sslParameters = Utils.copySSLParameters(sslp); +// if (sn != null) { +// SNIHostName sni = new SNIHostName(sn); +// sslParameters.setServerNames(List.of(sni)); +// } +// if (alpn != null) { +// sslParameters.setApplicationProtocols(alpn); +// Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn)); +// } else { +// Log.logSSL("SSLDelegate: No application protocols proposed"); +// } +// engine.setSSLParameters(sslParameters); +// wrapper = new EngineWrapper(chan, engine); +// this.chan = chan; +// this.client = client; +// } - SSLParameters getSSLParameters() { - return sslParameters; - } +// SSLParameters getSSLParameters() { +// return sslParameters; +// } - private static long countBytes(ByteBuffer[] buffers, int start, int number) { + static long countBytes(ByteBuffer[] buffers, int start, int number) { long c = 0; for (int i=0; i<number; i++) { c+= buffers[start+i].remaining(); @@ -191,7 +185,8 @@ class SSLDelegate { SocketChannel chan; SSLEngine engine; - Object wrapLock, unwrapLock; + final Object wrapLock; + final Object unwrapLock; ByteBuffer unwrap_src, wrap_dst; boolean closed = false; int u_remaining; // the number of bytes left in unwrap_src after an unwrap() @@ -205,8 +200,8 @@ class SSLDelegate { wrap_dst = allocate(BufType.PACKET); } - void close () throws IOException { - } +// void close () throws IOException { +// } WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose) throws IOException @@ -320,11 +315,11 @@ class SSLDelegate { } } - WrapperResult sendData (ByteBuffer src) throws IOException { - ByteBuffer[] buffers = new ByteBuffer[1]; - buffers[0] = src; - return sendData(buffers, 0, 1); - } +// WrapperResult sendData (ByteBuffer src) throws IOException { +// ByteBuffer[] buffers = new ByteBuffer[1]; +// buffers[0] = src; +// return sendData(buffers, 0, 1); +// } /** * send the data in the given ByteBuffer. If a handshake is needed @@ -407,7 +402,7 @@ class SSLDelegate { */ @SuppressWarnings("fallthrough") void doHandshake (HandshakeStatus hs_status) throws IOException { - boolean wasBlocking = false; + boolean wasBlocking; try { wasBlocking = chan.isBlocking(); handshaking.lock(); @@ -453,29 +448,29 @@ class SSLDelegate { } } - static void printParams(SSLParameters p) { - System.out.println("SSLParameters:"); - if (p == null) { - System.out.println("Null params"); - return; - } - for (String cipher : p.getCipherSuites()) { - System.out.printf("cipher: %s\n", cipher); - } - // JDK 8 EXCL START - for (String approto : p.getApplicationProtocols()) { - System.out.printf("application protocol: %s\n", approto); - } - // JDK 8 EXCL END - for (String protocol : p.getProtocols()) { - System.out.printf("protocol: %s\n", protocol); - } - if (p.getServerNames() != null) { - for (SNIServerName sname : p.getServerNames()) { - System.out.printf("server name: %s\n", sname.toString()); - } - } - } +// static void printParams(SSLParameters p) { +// System.out.println("SSLParameters:"); +// if (p == null) { +// System.out.println("Null params"); +// return; +// } +// for (String cipher : p.getCipherSuites()) { +// System.out.printf("cipher: %s\n", cipher); +// } +// // JDK 8 EXCL START +// for (String approto : p.getApplicationProtocols()) { +// System.out.printf("application protocol: %s\n", approto); +// } +// // JDK 8 EXCL END +// for (String protocol : p.getProtocols()) { +// System.out.printf("protocol: %s\n", protocol); +// } +// if (p.getServerNames() != null) { +// for (SNIServerName sname : p.getServerNames()) { +// System.out.printf("server name: %s\n", sname.toString()); +// } +// } +// } String getSessionInfo() { StringBuilder sb = new StringBuilder(); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java deleted file mode 100644 index d5cade109b7..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2015, 2017, 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. 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. - */ - -package jdk.incubator.http; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; -import java.util.concurrent.CompletableFuture; -import javax.net.ssl.SSLEngineResult.Status; -import javax.net.ssl.SSLParameters; -import jdk.incubator.http.SSLDelegate.WrapperResult; - -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; - -/** - * An SSL tunnel built on a Plain (CONNECT) TCP tunnel. - */ -class SSLTunnelConnection extends HttpConnection { - - final PlainTunnelingConnection delegate; - protected SSLDelegate sslDelegate; - private volatile boolean connected; - final String serverName; - - @Override - public void connect() throws IOException, InterruptedException { - delegate.connect(); - this.sslDelegate = new SSLDelegate(delegate.channel(), client, null, serverName); - connected = true; - } - - @Override - boolean connected() { - return connected && delegate.connected(); - } - - @Override - public CompletableFuture<Void> connectAsync() { - return delegate.connectAsync() - .thenAccept((Void v) -> { - try { - // can this block? - this.sslDelegate = new SSLDelegate(delegate.channel(), - client, - null, serverName); - connected = true; - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - } - - SSLTunnelConnection(InetSocketAddress addr, - HttpClientImpl client, - InetSocketAddress proxy) - { - super(addr, client); - this.serverName = Utils.getServerName(addr); - delegate = new PlainTunnelingConnection(addr, proxy, client); - } - - /** - * Create an SSLTunnelConnection from an existing connected AsyncSSLTunnelConnection. - * Used when downgrading from HTTP/2 to HTTP/1.1 - */ - SSLTunnelConnection(AsyncSSLTunnelConnection c) { - super(c.address, c.client); - this.delegate = c.plainConnection(); - AsyncSSLDelegate adel = c.sslDelegate(); - this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName); - this.serverName = adel.serverName; - connected = c.connected(); - } - - @Override - SSLParameters sslParameters() { - return sslDelegate.getSSLParameters(); - } - - @Override - public String toString() { - return "SSLTunnelConnection: " + super.toString(); - } - - private static long countBytes(ByteBuffer[] buffers, int start, int number) { - long c = 0; - for (int i=0; i<number; i++) { - c+= buffers[start+i].remaining(); - } - return c; - } - - @Override - ConnectionPool.CacheKey cacheKey() { - return ConnectionPool.cacheKey(address, delegate.proxyAddr); - } - - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - //debugPrint("Send", buffers, start, number); - long l = countBytes(buffers, start, number); - WrapperResult r = sslDelegate.sendData(buffers, start, number); - if (r.result.getStatus() == Status.CLOSED) { - if (l > 0) { - throw new IOException("SSLHttpConnection closed"); - } - } - return l; - } - - @Override - long write(ByteBuffer buffer) throws IOException { - //debugPrint("Send", buffer); - long l = buffer.remaining(); - WrapperResult r = sslDelegate.sendData(buffer); - if (r.result.getStatus() == Status.CLOSED) { - if (l > 0) { - throw new IOException("SSLHttpConnection closed"); - } - } - return l; - } - - @Override - void writeAsync(ByteBufferReference[] buffers) throws IOException { - write(ByteBufferReference.toBuffers(buffers), 0, buffers.length); - } - - @Override - void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - write(ByteBufferReference.toBuffers(buffers), 0, buffers.length); - } - - @Override - void flushAsync() throws IOException { - // nothing to do - } - - @Override - public void close() { - Utils.close(delegate.channel()); - } - - @Override - void shutdownInput() throws IOException { - delegate.channel().shutdownInput(); - } - - @Override - void shutdownOutput() throws IOException { - delegate.channel().shutdownOutput(); - } - - @Override - protected ByteBuffer readImpl() throws IOException { - ByteBuffer buf = Utils.getBuffer(); - - WrapperResult r = sslDelegate.recvData(buf); - return r.buf; - } - - @Override - SocketChannel channel() { - return delegate.channel(); - } - - @Override - CompletableFuture<Void> whenReceivingResponse() { - return delegate.whenReceivingResponse(); - } - - @Override - boolean isSecure() { - return true; - } - - @Override - boolean isProxied() { - return true; - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java new file mode 100644 index 00000000000..546ed617121 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http; + +import java.io.EOFException; +import java.io.IOException; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter; +import jdk.incubator.http.internal.common.SequentialScheduler.RestartableTask; +import jdk.incubator.http.internal.common.Utils; + +/** + * A SocketTube is a terminal tube plugged directly into the socket. + * The read subscriber should call {@code subscribe} on the SocketTube before + * the SocketTube can be subscribed to the write publisher. + */ +final class SocketTube implements FlowTube { + + static final boolean DEBUG = Utils.DEBUG; // revisit: temporary developer's flag + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + static final AtomicLong IDS = new AtomicLong(); + + private final HttpClientImpl client; + private final SocketChannel channel; + private final Supplier<ByteBuffer> buffersSource; + private final Object lock = new Object(); + private final AtomicReference<Throwable> errorRef = new AtomicReference<>(); + private final InternalReadPublisher readPublisher; + private final InternalWriteSubscriber writeSubscriber; + private final long id = IDS.incrementAndGet(); + + public SocketTube(HttpClientImpl client, SocketChannel channel, + Supplier<ByteBuffer> buffersSource) { + this.client = client; + this.channel = channel; + this.buffersSource = buffersSource; + this.readPublisher = new InternalReadPublisher(); + this.writeSubscriber = new InternalWriteSubscriber(); + } + +// private static Flow.Subscription nopSubscription() { +// return new Flow.Subscription() { +// @Override public void request(long n) { } +// @Override public void cancel() { } +// }; +// } + + /** + * Returns {@code true} if this flow is finished. + * This happens when this flow internal read subscription is completed, + * either normally (EOF reading) or exceptionally (EOF writing, or + * underlying socket closed, or some exception occurred while reading or + * writing to the socket). + * + * @return {@code true} if this flow is finished. + */ + public boolean isFinished() { + InternalReadPublisher.InternalReadSubscription subscription = + readPublisher.subscriptionImpl; + return subscription != null && subscription.completed + || subscription == null && errorRef.get() != null; + } + + // ===================================================================== // + // Flow.Publisher // + // ======================================================================// + + /** + * {@inheritDoc } + * @apiNote This method should be called first. In particular, the caller + * must ensure that this method must be called by the read + * subscriber before the write publisher can call {@code onSubscribe}. + * Failure to adhere to this contract may result in assertion errors. + */ + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) { + Objects.requireNonNull(s); + assert s instanceof TubeSubscriber : "Expected TubeSubscriber, got:" + s; + readPublisher.subscribe(s); + } + + + // ===================================================================== // + // Flow.Subscriber // + // ======================================================================// + + /** + * {@inheritDoc } + * @apiNote The caller must ensure that {@code subscribe} is called by + * the read subscriber before {@code onSubscribe} is called by + * the write publisher. + * Failure to adhere to this contract may result in assertion errors. + */ + @Override + public void onSubscribe(Flow.Subscription subscription) { + writeSubscriber.onSubscribe(subscription); + } + + @Override + public void onNext(List<ByteBuffer> item) { + writeSubscriber.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + writeSubscriber.onError(throwable); + } + + @Override + public void onComplete() { + writeSubscriber.onComplete(); + } + + // ===================================================================== // + // Events // + // ======================================================================// + + /** + * A restartable task used to process tasks in sequence. + */ + private static class SocketFlowTask implements RestartableTask { + final Runnable task; + private final Object monitor = new Object(); + SocketFlowTask(Runnable task) { + this.task = task; + } + @Override + public final void run(DeferredCompleter taskCompleter) { + try { + // non contentious synchronized for visibility. + synchronized(monitor) { + task.run(); + } + } finally { + taskCompleter.complete(); + } + } + } + + // This is best effort - there's no guarantee that the printed set + // of values is consistent. It should only be considered as + // weakly accurate - in particular in what concerns the events states, + // especially when displaying a read event state from a write event + // callback and conversely. + void debugState(String when) { + if (debug.isLoggable(Level.DEBUG)) { + StringBuilder state = new StringBuilder(); + + InternalReadPublisher.InternalReadSubscription sub = + readPublisher.subscriptionImpl; + InternalReadPublisher.ReadEvent readEvent = + sub == null ? null : sub.readEvent; + Demand rdemand = sub == null ? null : sub.demand; + InternalWriteSubscriber.WriteEvent writeEvent = + writeSubscriber.writeEvent; + AtomicLong wdemand = writeSubscriber.writeDemand; + int rops = readEvent == null ? 0 : readEvent.interestOps(); + long rd = rdemand == null ? 0 : rdemand.get(); + int wops = writeEvent == null ? 0 : writeEvent.interestOps(); + long wd = wdemand == null ? 0 : wdemand.get(); + + state.append(when).append(" Reading: [ops=") + .append(rops).append(", demand=").append(rd) + .append(", stopped=") + .append((sub == null ? false : sub.readScheduler.isStopped())) + .append("], Writing: [ops=").append(wops) + .append(", demand=").append(wd) + .append("]"); + debug.log(Level.DEBUG, state.toString()); + } + } + + /** + * A repeatable event that can be paused or resumed by changing + * its interestOps. + * When the event is fired, it is first paused before being signaled. + * It is the responsibility of the code triggered by {@code signalEvent} + * to resume the event if required. + */ + private static abstract class SocketFlowEvent extends AsyncEvent { + final SocketChannel channel; + final int defaultInterest; + volatile int interestOps; + volatile boolean registered; + SocketFlowEvent(int defaultInterest, SocketChannel channel) { + super(AsyncEvent.REPEATING); + this.defaultInterest = defaultInterest; + this.channel = channel; + } + final boolean registered() {return registered;} + final void resume() { + interestOps = defaultInterest; + registered = true; + } + final void pause() {interestOps = 0;} + @Override + public final SelectableChannel channel() {return channel;} + @Override + public final int interestOps() {return interestOps;} + + @Override + public final void handle() { + pause(); // pause, then signal + signalEvent(); // won't be fired again until resumed. + } + @Override + public final void abort(IOException error) { + debug().log(Level.DEBUG, () -> "abort: " + error); + pause(); // pause, then signal + signalError(error); // should not be resumed after abort (not checked) + } + + protected abstract void signalEvent(); + protected abstract void signalError(Throwable error); + abstract System.Logger debug(); + } + + // ===================================================================== // + // Writing // + // ======================================================================// + + // This class makes the assumption that the publisher will call + // onNext sequentially, and that onNext won't be called if the demand + // has not been incremented by request(1). + // It has a 'queue of 1' meaning that it will call request(1) in + // onSubscribe, and then only after its 'current' buffer list has been + // fully written and current set to null; + private final class InternalWriteSubscriber + implements Flow.Subscriber<List<ByteBuffer>> { + + volatile Flow.Subscription subscription; + volatile List<ByteBuffer> current; + volatile boolean completed; + final WriteEvent writeEvent = new WriteEvent(channel, this); + final AtomicLong writeDemand = new AtomicLong(); + + @Override + public void onSubscribe(Flow.Subscription subscription) { + Flow.Subscription previous = this.subscription; + this.subscription = subscription; + debug.log(Level.DEBUG, "subscribed for writing"); + if (current == null) { + if (previous == subscription || previous == null) { + if (writeDemand.compareAndSet(0, 1)) { + subscription.request(1); + } + } else { + writeDemand.set(1); + subscription.request(1); + } + } + } + + @Override + public void onNext(List<ByteBuffer> bufs) { + assert current == null; // this is a queue of 1. + assert subscription != null; + current = bufs; + tryFlushCurrent(client.isSelectorThread()); // may be in selector thread + // For instance in HTTP/2, a received SETTINGS frame might trigger + // the sending of a SETTINGS frame in turn which might cause + // onNext to be called from within the same selector thread that the + // original SETTINGS frames arrived on. If rs is the read-subscriber + // and ws is the write-subscriber then the following can occur: + // ReadEvent -> rs.onNext(bytes) -> process server SETTINGS -> write + // client SETTINGS -> ws.onNext(bytes) -> tryFlushCurrent + debugState("leaving w.onNext"); + } + + // we don't use a SequentialScheduler here: we rely on + // onNext() being called sequentially, and not being called + // if we haven't call request(1) + // onNext is usually called from within a user/executor thread. + // we will perform the initial writing in that thread. + // if for some reason, not all data can be written, the writeEvent + // will be resumed, and the rest of the data will be written from + // the selector manager thread when the writeEvent is fired. + // If we are in the selector manager thread, then we will use the executor + // to call request(1), ensuring that onNext() won't be called from + // within the selector thread. + // If we are not in the selector manager thread, then we don't care. + void tryFlushCurrent(boolean inSelectorThread) { + List<ByteBuffer> bufs = current; + if (bufs == null) return; + try { + assert inSelectorThread == client.isSelectorThread() : + "should " + (inSelectorThread ? "" : "not ") + + " be in the selector thread"; + long remaining = Utils.remaining(bufs); + debug.log(Level.DEBUG, "trying to write: %d", remaining); + long written = writeAvailable(bufs); + debug.log(Level.DEBUG, "wrote: %d", remaining); + if (written == -1) { + signalError(new EOFException("EOF reached while writing")); + return; + } + assert written <= remaining; + if (remaining - written == 0) { + current = null; + writeDemand.decrementAndGet(); + Runnable requestMore = this::requestMore; + if (inSelectorThread) { + assert client.isSelectorThread(); + client.theExecutor().execute(requestMore); + } else { + assert !client.isSelectorThread(); + requestMore.run(); + } + } else { + resumeWriteEvent(inSelectorThread); + } + } catch (Throwable t) { + signalError(t); + subscription.cancel(); + } + } + + void requestMore() { + try { + if (completed) return; + long d = writeDemand.get(); + if (writeDemand.compareAndSet(0,1)) { + debug.log(Level.DEBUG, "write: requesting more..."); + subscription.request(1); + } else { + debug.log(Level.DEBUG, "write: no need to request more: %d", d); + } + } catch (Throwable t) { + debug.log(Level.DEBUG, () -> + "write: error while requesting more: " + t); + signalError(t); + subscription.cancel(); + } finally { + debugState("leaving requestMore: "); + } + } + + @Override + public void onError(Throwable throwable) { + signalError(throwable); + } + + @Override + public void onComplete() { + completed = true; + // no need to pause the write event here: the write event will + // be paused if there is nothing more to write. + List<ByteBuffer> bufs = current; + long remaining = bufs == null ? 0 : Utils.remaining(bufs); + debug.log(Level.DEBUG, "write completed, %d yet to send", remaining); + debugState("InternalWriteSubscriber::onComplete"); + } + + void resumeWriteEvent(boolean inSelectorThread) { + debug.log(Level.DEBUG, "scheduling write event"); + resumeEvent(writeEvent, this::signalError); + } + +// void pauseWriteEvent() { +// debug.log(Level.DEBUG, "pausing write event"); +// pauseEvent(writeEvent, this::signalError); +// } + + void signalWritable() { + debug.log(Level.DEBUG, "channel is writable"); + tryFlushCurrent(true); + } + + void signalError(Throwable error) { + debug.log(Level.DEBUG, () -> "write error: " + error); + completed = true; + readPublisher.signalError(error); + } + + // A repeatable WriteEvent which is paused after firing and can + // be resumed if required - see SocketFlowEvent; + final class WriteEvent extends SocketFlowEvent { + final InternalWriteSubscriber sub; + WriteEvent(SocketChannel channel, InternalWriteSubscriber sub) { + super(SelectionKey.OP_WRITE, channel); + this.sub = sub; + } + @Override + protected final void signalEvent() { + try { + client.eventUpdated(this); + sub.signalWritable(); + } catch(Throwable t) { + sub.signalError(t); + } + } + + @Override + protected void signalError(Throwable error) { + sub.signalError(error); + } + + @Override + System.Logger debug() { + return debug; + } + + } + + } + + // ===================================================================== // + // Reading // + // ===================================================================== // + + // The InternalReadPublisher uses a SequentialScheduler to ensure that + // onNext/onError/onComplete are called sequentially on the caller's + // subscriber. + // However, it relies on the fact that the only time where + // runOrSchedule() is called from a user/executor thread is in signalError, + // right after the errorRef has been set. + // Because the sequential scheduler's task always checks for errors first, + // and always terminate the scheduler on error, then it is safe to assume + // that if it reaches the point where it reads from the channel, then + // it is running in the SelectorManager thread. This is because all + // other invocation of runOrSchedule() are triggered from within a + // ReadEvent. + // + // When pausing/resuming the event, some shortcuts can then be taken + // when we know we're running in the selector manager thread + // (in that case there's no need to call client.eventUpdated(readEvent); + // + private final class InternalReadPublisher + implements Flow.Publisher<List<ByteBuffer>> { + private final InternalReadSubscription subscriptionImpl + = new InternalReadSubscription(); + AtomicReference<ReadSubscription> pendingSubscription = new AtomicReference<>(); + private volatile ReadSubscription subscription; + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) { + Objects.requireNonNull(s); + + TubeSubscriber sub = FlowTube.asTubeSubscriber(s); + ReadSubscription target = new ReadSubscription(subscriptionImpl, sub); + ReadSubscription previous = pendingSubscription.getAndSet(target); + + if (previous != null && previous != target) { + debug.log(Level.DEBUG, + () -> "read publisher: dropping pending subscriber: " + + previous.subscriber); + previous.errorRef.compareAndSet(null, errorRef.get()); + previous.signalOnSubscribe(); + if (subscriptionImpl.completed) { + previous.signalCompletion(); + } else { + previous.subscriber.dropSubscription(); + } + } + + debug.log(Level.DEBUG, "read publisher got subscriber"); + subscriptionImpl.signalSubscribe(); + debugState("leaving read.subscribe: "); + } + + void signalError(Throwable error) { + if (!errorRef.compareAndSet(null, error)) { + return; + } + subscriptionImpl.handleError(); + } + + final class ReadSubscription implements Flow.Subscription { + final InternalReadSubscription impl; + final TubeSubscriber subscriber; + final AtomicReference<Throwable> errorRef = new AtomicReference<>(); + volatile boolean subscribed; + volatile boolean cancelled; + volatile boolean completed; + + public ReadSubscription(InternalReadSubscription impl, + TubeSubscriber subscriber) { + this.impl = impl; + this.subscriber = subscriber; + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void request(long n) { + if (!cancelled) { + impl.request(n); + } else { + debug.log(Level.DEBUG, + "subscription cancelled, ignoring request %d", n); + } + } + + void signalCompletion() { + assert subscribed || cancelled; + if (completed || cancelled) return; + synchronized (this) { + if (completed) return; + completed = true; + } + Throwable error = errorRef.get(); + if (error != null) { + debug.log(Level.DEBUG, () -> + "forwarding error to subscriber: " + + error); + subscriber.onError(error); + } else { + debug.log(Level.DEBUG, "completing subscriber"); + subscriber.onComplete(); + } + } + + void signalOnSubscribe() { + if (subscribed || cancelled) return; + synchronized (this) { + if (subscribed || cancelled) return; + subscribed = true; + } + subscriber.onSubscribe(this); + debug.log(Level.DEBUG, "onSubscribe called"); + if (errorRef.get() != null) { + signalCompletion(); + } + } + } + + final class InternalReadSubscription implements Flow.Subscription { + + private final Demand demand = new Demand(); + final SequentialScheduler readScheduler; + private volatile boolean completed; + private final ReadEvent readEvent; + private final AsyncEvent subscribeEvent; + + InternalReadSubscription() { + readScheduler = new SequentialScheduler(new SocketFlowTask(this::read)); + subscribeEvent = new AsyncTriggerEvent(this::signalError, + this::handleSubscribeEvent); + readEvent = new ReadEvent(channel, this); + } + + /* + * This method must be invoked before any other method of this class. + */ + final void signalSubscribe() { + if (readScheduler.isStopped() || completed) { + // if already completed or stopped we can handle any + // pending connection directly from here. + debug.log(Level.DEBUG, + "handling pending subscription while completed"); + handlePending(); + } else { + try { + debug.log(Level.DEBUG, + "registering subscribe event"); + client.registerEvent(subscribeEvent); + } catch (Throwable t) { + signalError(t); + handlePending(); + } + } + } + + final void handleSubscribeEvent() { + assert client.isSelectorThread(); + debug.log(Level.DEBUG, "subscribe event raised"); + readScheduler.runOrSchedule(); + if (readScheduler.isStopped() || completed) { + // if already completed or stopped we can handle any + // pending connection directly from here. + debug.log(Level.DEBUG, + "handling pending subscription when completed"); + handlePending(); + } + } + + + /* + * Although this method is thread-safe, the Reactive-Streams spec seems + * to not require it to be as such. It's a responsibility of the + * subscriber to signal demand in a thread-safe manner. + * + * https://github.com/reactive-streams/reactive-streams-jvm/blob/dd24d2ab164d7de6c316f6d15546f957bec29eaa/README.md + * (rules 2.7 and 3.4) + */ + @Override + public final void request(long n) { + if (n > 0L) { + boolean wasFulfilled = demand.increase(n); + if (wasFulfilled) { + debug.log(Level.DEBUG, "got some demand for reading"); + resumeReadEvent(); + // if demand has been changed from fulfilled + // to unfulfilled register read event; + } + } else { + signalError(new IllegalArgumentException("non-positive request")); + } + debugState("leaving request("+n+"): "); + } + + @Override + public final void cancel() { + pauseReadEvent(); + readScheduler.stop(); + } + + private void resumeReadEvent() { + debug.log(Level.DEBUG, "resuming read event"); + resumeEvent(readEvent, this::signalError); + } + + private void pauseReadEvent() { + debug.log(Level.DEBUG, "pausing read event"); + pauseEvent(readEvent, this::signalError); + } + + + final void handleError() { + assert errorRef.get() != null; + readScheduler.runOrSchedule(); + } + + final void signalError(Throwable error) { + if (!errorRef.compareAndSet(null, error)) { + return; + } + debug.log(Level.DEBUG, () -> "got read error: " + error); + readScheduler.runOrSchedule(); + } + + final void signalReadable() { + readScheduler.runOrSchedule(); + } + + /** The body of the task that runs in SequentialScheduler. */ + final void read() { + // It is important to only call pauseReadEvent() when stopping + // the scheduler. The event is automatically paused before + // firing, and trying to pause it again could cause a race + // condition between this loop, which calls tryDecrementDemand(), + // and the thread that calls request(n), which will try to resume + // reading. + try { + while(!readScheduler.isStopped()) { + if (completed) return; + + // make sure we have a subscriber + if (handlePending()) { + debug.log(Level.DEBUG, "pending subscriber subscribed"); + return; + } + + // If an error was signaled, we might not be in the + // the selector thread, and that is OK, because we + // will just call onError and return. + ReadSubscription current = subscription; + TubeSubscriber subscriber = current.subscriber; + Throwable error = errorRef.get(); + if (error != null) { + completed = true; + // safe to pause here because we're finished anyway. + pauseReadEvent(); + debug.log(Level.DEBUG, () -> "Sending error " + error + + " to subscriber " + subscriber); + current.errorRef.compareAndSet(null, error); + current.signalCompletion(); + readScheduler.stop(); + debugState("leaving read() loop with error: "); + return; + } + + // If we reach here then we must be in the selector thread. + assert client.isSelectorThread(); + if (demand.tryDecrement()) { + // we have demand. + try { + List<ByteBuffer> bytes = readAvailable(); + if (bytes == EOF) { + if (!completed) { + debug.log(Level.DEBUG, "got read EOF"); + completed = true; + // safe to pause here because we're finished + // anyway. + pauseReadEvent(); + current.signalCompletion(); + readScheduler.stop(); + } + debugState("leaving read() loop after EOF: "); + return; + } else if (Utils.remaining(bytes) > 0) { + // the subscriber is responsible for offloading + // to another thread if needed. + debug.log(Level.DEBUG, () -> "read bytes: " + + Utils.remaining(bytes)); + assert !current.completed; + subscriber.onNext(bytes); + // we could continue looping until the demand + // reaches 0. However, that would risk starving + // other connections (bound to other socket + // channels) - as other selected keys activated + // by the selector manager thread might be + // waiting for this event to terminate. + // So resume the read event and return now... + resumeReadEvent(); + debugState("leaving read() loop after onNext: "); + return; + } else { + // nothing available! + debug.log(Level.DEBUG, "no more bytes available"); + // re-increment the demand and resume the read + // event. This ensures that this loop is + // executed again when the socket becomes + // readable again. + demand.increase(1); + resumeReadEvent(); + debugState("leaving read() loop with no bytes"); + return; + } + } catch (Throwable x) { + signalError(x); + continue; + } + } else { + debug.log(Level.DEBUG, "no more demand for reading"); + // the event is paused just after firing, so it should + // still be paused here, unless the demand was just + // incremented from 0 to n, in which case, the + // event will be resumed, causing this loop to be + // invoked again when the socket becomes readable: + // This is what we want. + // Trying to pause the event here would actually + // introduce a race condition between this loop and + // request(n). + debugState("leaving read() loop with no demand"); + break; + } + } + } catch (Throwable t) { + debug.log(Level.DEBUG, "Unexpected exception in read loop", t); + signalError(t); + } finally { + handlePending(); + } + } + + boolean handlePending() { + ReadSubscription pending = pendingSubscription.getAndSet(null); + if (pending == null) return false; + debug.log(Level.DEBUG, "handling pending subscription for %s", + pending.subscriber); + ReadSubscription current = subscription; + if (current != null && current != pending && !completed) { + current.subscriber.dropSubscription(); + } + debug.log(Level.DEBUG, "read demand reset to 0"); + subscriptionImpl.demand.reset(); // subscriber will increase demand if it needs to. + pending.errorRef.compareAndSet(null, errorRef.get()); + if (!readScheduler.isStopped()) { + subscription = pending; + } else { + debug.log(Level.DEBUG, "socket tube is already stopped"); + } + debug.log(Level.DEBUG, "calling onSubscribe"); + pending.signalOnSubscribe(); + if (completed) { + pending.errorRef.compareAndSet(null, errorRef.get()); + pending.signalCompletion(); + } + return true; + } + } + + + // A repeatable ReadEvent which is paused after firing and can + // be resumed if required - see SocketFlowEvent; + final class ReadEvent extends SocketFlowEvent { + final InternalReadSubscription sub; + ReadEvent(SocketChannel channel, InternalReadSubscription sub) { + super(SelectionKey.OP_READ, channel); + this.sub = sub; + } + @Override + protected final void signalEvent() { + try { + client.eventUpdated(this); + sub.signalReadable(); + } catch(Throwable t) { + sub.signalError(t); + } + } + + @Override + protected final void signalError(Throwable error) { + sub.signalError(error); + } + + @Override + System.Logger debug() { + return debug; + } + } + + } + + // ===================================================================== // + // Socket Channel Read/Write // + // ===================================================================== // + static final int MAX_BUFFERS = 3; + static final List<ByteBuffer> EOF = List.of(); + + private List<ByteBuffer> readAvailable() throws IOException { + ByteBuffer buf = buffersSource.get(); + assert buf.hasRemaining(); + + int read; + int pos = buf.position(); + List<ByteBuffer> list = null; + while (buf.hasRemaining()) { + while ((read = channel.read(buf)) > 0) { + if (!buf.hasRemaining()) break; + } + + // nothing read; + if (buf.position() == pos) { + // An empty list signal the end of data, and should only be + // returned if read == -1. + // If we already read some data, then we must return what we have + // read, and -1 will be returned next time the caller attempts to + // read something. + if (list == null && read == -1) { // eof + list = EOF; + break; + } + } + buf.limit(buf.position()); + buf.position(pos); + if (list == null) { + list = List.of(buf); + } else { + if (!(list instanceof ArrayList)) { + list = new ArrayList<>(list); + } + list.add(buf); + } + if (read <= 0 || list.size() == MAX_BUFFERS) break; + buf = buffersSource.get(); + pos = buf.position(); + assert buf.hasRemaining(); + } + return list; + } + + private long writeAvailable(List<ByteBuffer> bytes) throws IOException { + ByteBuffer[] srcs = bytes.toArray(Utils.EMPTY_BB_ARRAY); + final long remaining = Utils.remaining(srcs); + long written = 0; + while (remaining > written) { + long w = channel.write(srcs); + if (w == -1 && written == 0) return -1; + if (w == 0) break; + written += w; + } + return written; + } + + private void resumeEvent(SocketFlowEvent event, + Consumer<Throwable> errorSignaler) { + boolean registrationRequired; + synchronized(lock) { + registrationRequired = !event.registered(); + event.resume(); + } + try { + if (registrationRequired) { + client.registerEvent(event); + } else { + client.eventUpdated(event); + } + } catch(Throwable t) { + errorSignaler.accept(t); + } + } + + private void pauseEvent(SocketFlowEvent event, + Consumer<Throwable> errorSignaler) { + synchronized(lock) { + event.pause(); + } + try { + client.eventUpdated(event); + } catch(Throwable t) { + errorSignaler.accept(t); + } + } + + @Override + public void connectFlows(TubePublisher writePublisher, + TubeSubscriber readSubscriber) { + debug.log(Level.DEBUG, "connecting flows"); + this.subscribe(readSubscriber); + writePublisher.subscribe(this); + } + + + @Override + public String toString() { + return dbgString(); + } + + final String dbgString() { + return "SocketTube("+id+")"; + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java index 814e617a7a5..cdf5366e042 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java @@ -26,21 +26,21 @@ package jdk.incubator.http; import java.io.IOException; +import java.lang.System.Logger.Level; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.Flow.Subscription; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; - +import java.util.concurrent.atomic.AtomicReference; +import jdk.incubator.http.HttpResponse.BodySubscriber; import jdk.incubator.http.internal.common.*; import jdk.incubator.http.internal.frame.*; import jdk.incubator.http.internal.hpack.DecodingCallback; @@ -54,10 +54,6 @@ import jdk.incubator.http.internal.hpack.DecodingCallback; * * sendRequest() -- sendHeadersOnly() + sendBody() * - * sendBody() -- in calling thread: obeys all flow control (so may block) - * obtains data from request body processor and places on connection - * outbound Q. - * * sendBodyAsync() -- calls sendBody() in an executor thread. * * sendHeadersAsync() -- calls sendHeadersOnly() which does not block @@ -77,10 +73,6 @@ import jdk.incubator.http.internal.hpack.DecodingCallback; * * getResponse() -- calls getResponseAsync() and waits for CF to complete * - * responseBody() -- in calling thread: blocks for incoming DATA frames on - * stream inputQ. Obeys remote and local flow control so may block. - * Calls user response body processor with data buffers. - * * responseBodyAsync() -- calls responseBody() in an executor thread. * * incoming() -- entry point called from connection reader thread. Frames are @@ -98,7 +90,13 @@ import jdk.incubator.http.internal.hpack.DecodingCallback; */ class Stream<T> extends ExchangeImpl<T> { - final AsyncDataReadQueue inputQ = new AsyncDataReadQueue(); + final static boolean DEBUG = Utils.DEBUG; // Revisit: temporary developer's flag + final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG); + + final ConcurrentLinkedQueue<Http2Frame> inputQ = new ConcurrentLinkedQueue<>(); + final SequentialScheduler sched = + SequentialScheduler.synchronizedScheduler(this::schedule); + final SubscriptionBase userSubscription = new SubscriptionBase(sched, this::cancel); /** * This stream's identifier. Assigned lazily by the HTTP2Connection before @@ -106,24 +104,21 @@ class Stream<T> extends ExchangeImpl<T> { */ protected volatile int streamid; - long responseContentLen = -1; - long responseBytesProcessed = 0; long requestContentLen; final Http2Connection connection; - HttpClientImpl client; final HttpRequestImpl request; final DecodingCallback rspHeadersConsumer; HttpHeadersImpl responseHeaders; - final HttpHeadersImpl requestHeaders; final HttpHeadersImpl requestPseudoHeaders; - HttpResponse.BodyProcessor<T> responseProcessor; - final HttpRequest.BodyProcessor requestProcessor; + volatile HttpResponse.BodySubscriber<T> responseSubscriber; + final HttpRequest.BodyPublisher requestPublisher; + volatile RequestSubscriber requestSubscriber; volatile int responseCode; volatile Response response; - volatile CompletableFuture<Response> responseCF; - final AbstractPushPublisher<ByteBuffer> publisher; + volatile Throwable failed; // The exception with which this stream was canceled. final CompletableFuture<Void> requestBodyCF = new MinimalFuture<>(); + volatile CompletableFuture<T> responseBodyCF; /** True if END_STREAM has been seen in a frame received on this stream. */ private volatile boolean remotelyClosed; @@ -131,7 +126,7 @@ class Stream<T> extends ExchangeImpl<T> { private volatile boolean endStreamSent; // state flags - boolean requestSent, responseReceived, responseHeadersReceived; + private boolean requestSent, responseReceived; /** * A reference to this Stream's connection Send Window controller. The @@ -146,15 +141,88 @@ class Stream<T> extends ExchangeImpl<T> { return connection.connection; } + /** + * Invoked either from incoming() -> {receiveDataFrame() or receiveResetFrame() } + * of after user subscription window has re-opened, from SubscriptionBase.request() + */ + private void schedule() { + if (responseSubscriber == null) + // can't process anything yet + return; + + while (!inputQ.isEmpty()) { + Http2Frame frame = inputQ.peek(); + if (frame instanceof ResetFrame) { + inputQ.remove(); + handleReset((ResetFrame)frame); + return; + } + DataFrame df = (DataFrame)frame; + boolean finished = df.getFlag(DataFrame.END_STREAM); + + List<ByteBuffer> buffers = df.getData(); + List<ByteBuffer> dsts = Collections.unmodifiableList(buffers); + int size = Utils.remaining(dsts, Integer.MAX_VALUE); + if (size == 0 && finished) { + inputQ.remove(); + Log.logTrace("responseSubscriber.onComplete"); + debug.log(Level.DEBUG, "incoming: onComplete"); + sched.stop(); + responseSubscriber.onComplete(); + setEndStreamReceived(); + return; + } else if (userSubscription.tryDecrement()) { + inputQ.remove(); + Log.logTrace("responseSubscriber.onNext {0}", size); + debug.log(Level.DEBUG, "incoming: onNext(%d)", size); + responseSubscriber.onNext(dsts); + if (consumed(df)) { + Log.logTrace("responseSubscriber.onComplete"); + debug.log(Level.DEBUG, "incoming: onComplete"); + sched.stop(); + responseSubscriber.onComplete(); + setEndStreamReceived(); + return; + } + } else { + return; + } + } + Throwable t = failed; + if (t != null) { + sched.stop(); + responseSubscriber.onError(t); + close(); + } + } + + // Callback invoked after the Response BodySubscriber has consumed the + // buffers contained in a DataFrame. + // Returns true if END_STREAM is reached, false otherwise. + private boolean consumed(DataFrame df) { + // RFC 7540 6.1: + // The entire DATA frame payload is included in flow control, + // including the Pad Length and Padding fields if present + int len = df.payloadLength(); + connection.windowUpdater.update(len); + + if (!df.getFlag(DataFrame.END_STREAM)) { + // Don't send window update on a stream which is + // closed or half closed. + windowUpdater.update(len); + return false; // more data coming + } + return true; // end of stream + } + @Override CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler, boolean returnConnectionToPool, Executor executor) { Log.logTrace("Reading body on stream {0}", streamid); - responseProcessor = handler.apply(responseCode, responseHeaders); - publisher.subscribe(responseProcessor); - CompletableFuture<T> cf = receiveData(executor); + BodySubscriber<T> bodySubscriber = handler.apply(responseCode, responseHeaders); + CompletableFuture<T> cf = receiveData(bodySubscriber); PushGroup<?,?> pg = exchange.getPushGroup(); if (pg != null) { @@ -164,20 +232,6 @@ class Stream<T> extends ExchangeImpl<T> { return cf; } - @Override - T readBody(HttpResponse.BodyHandler<T> handler, boolean returnConnectionToPool) - throws IOException - { - CompletableFuture<T> cf = readBodyAsync(handler, - returnConnectionToPool, - null); - try { - return cf.join(); - } catch (CompletionException e) { - throw Utils.getIOException(e); - } - } - @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -186,85 +240,51 @@ class Stream<T> extends ExchangeImpl<T> { return sb.toString(); } - private boolean receiveDataFrame(Http2Frame frame) throws IOException, InterruptedException { - if (frame instanceof ResetFrame) { - handleReset((ResetFrame) frame); - return true; - } else if (!(frame instanceof DataFrame)) { - assert false; - return true; - } - DataFrame df = (DataFrame) frame; - // RFC 7540 6.1: - // The entire DATA frame payload is included in flow control, - // including the Pad Length and Padding fields if present - int len = df.payloadLength(); - ByteBufferReference[] buffers = df.getData(); - for (ByteBufferReference b : buffers) { - ByteBuffer buf = b.get(); - if (buf.hasRemaining()) { - publisher.acceptData(Optional.of(buf)); - } - } - connection.windowUpdater.update(len); - if (df.getFlag(DataFrame.END_STREAM)) { - setEndStreamReceived(); - publisher.acceptData(Optional.empty()); - return false; - } - // Don't send window update on a stream which is - // closed or half closed. - windowUpdater.update(len); - return true; + private void receiveDataFrame(DataFrame df) { + inputQ.add(df); + sched.runOrSchedule(); } - // pushes entire response body into response processor + /** Handles a RESET frame. RESET is always handled inline in the queue. */ + private void receiveResetFrame(ResetFrame frame) { + inputQ.add(frame); + sched.runOrSchedule(); + } + + // pushes entire response body into response subscriber // blocking when required by local or remote flow control - CompletableFuture<T> receiveData(Executor executor) { - CompletableFuture<T> cf = responseProcessor - .getBody() - .toCompletableFuture(); - Consumer<Throwable> onError = e -> { - Log.logTrace("receiveData: {0}", e.toString()); - e.printStackTrace(); - cf.completeExceptionally(e); - publisher.acceptError(e); - }; - if (executor == null) { - inputQ.blockingReceive(this::receiveDataFrame, onError); + CompletableFuture<T> receiveData(BodySubscriber<T> bodySubscriber) { + responseBodyCF = MinimalFuture.of(bodySubscriber.getBody()); + + if (isCanceled()) { + Throwable t = getCancelCause(); + responseBodyCF.completeExceptionally(t); } else { - inputQ.asyncReceive(executor, this::receiveDataFrame, onError); + bodySubscriber.onSubscribe(userSubscription); } - return cf; + // Set the responseSubscriber field now that onSubscribe has been called. + // This effectively allows the scheduler to start invoking the callbacks. + responseSubscriber = bodySubscriber; + sched.runOrSchedule(); // in case data waiting already to be processed + return responseBodyCF; } @Override - void sendBody() throws IOException { - try { - sendBodyImpl().join(); - } catch (CompletionException e) { - throw Utils.getIOException(e); - } - } - CompletableFuture<ExchangeImpl<T>> sendBodyAsync() { return sendBodyImpl().thenApply( v -> this); } @SuppressWarnings("unchecked") - Stream(HttpClientImpl client, - Http2Connection connection, + Stream(Http2Connection connection, Exchange<T> e, WindowController windowController) { super(e); - this.client = client; this.connection = connection; this.windowController = windowController; this.request = e.request(); - this.requestProcessor = request.requestProcessor; + this.requestPublisher = request.requestPublisher; // may be null responseHeaders = new HttpHeadersImpl(); - requestHeaders = new HttpHeadersImpl(); rspHeadersConsumer = (name, value) -> { responseHeaders.addHeader(name.toString(), value.toString()); if (Log.headers() && Log.trace()) { @@ -274,7 +294,6 @@ class Stream<T> extends ExchangeImpl<T> { }; this.requestPseudoHeaders = new HttpHeadersImpl(); // NEW - this.publisher = new BlockingPushPublisher<>(); this.windowUpdater = new StreamWindowUpdateSender(connection); } @@ -284,17 +303,18 @@ class Stream<T> extends ExchangeImpl<T> { * Data frames will be removed by response body thread. */ void incoming(Http2Frame frame) throws IOException { + debug.log(Level.DEBUG, "incoming: %s", frame); if ((frame instanceof HeaderFrame)) { HeaderFrame hframe = (HeaderFrame)frame; if (hframe.endHeaders()) { Log.logTrace("handling response (streamid={0})", streamid); handleResponse(); if (hframe.getFlag(HeaderFrame.END_STREAM)) { - inputQ.put(new DataFrame(streamid, DataFrame.END_STREAM, new ByteBufferReference[0])); + receiveDataFrame(new DataFrame(streamid, DataFrame.END_STREAM, List.of())); } } } else if (frame instanceof DataFrame) { - inputQ.put(frame); + receiveDataFrame((DataFrame)frame); } else { otherFrame(frame); } @@ -324,10 +344,6 @@ class Stream<T> extends ExchangeImpl<T> { } protected void handleResponse() throws IOException { - synchronized(this) { - responseHeadersReceived = true; - } - HttpConnection c = connection.connection; // TODO: improve responseCode = (int)responseHeaders .firstValueAsLong(":status") .orElseThrow(() -> new IOException("no statuscode in response")); @@ -336,9 +352,11 @@ class Stream<T> extends ExchangeImpl<T> { request, exchange, responseHeaders, responseCode, HttpClient.Version.HTTP_2); - this.responseContentLen = responseHeaders - .firstValueAsLong("content-length") - .orElse(-1L); + /* TODO: review if needs to be removed + the value is not used, but in case `content-length` doesn't parse as + long, there will be NumberFormatException. If left as is, make sure + code up the stack handles NFE correctly. */ + responseHeaders.firstValueAsLong("content-length"); if (Log.headers()) { StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n"); @@ -349,50 +367,32 @@ class Stream<T> extends ExchangeImpl<T> { completeResponse(response); } - void incoming_reset(ResetFrame frame) throws IOException { + void incoming_reset(ResetFrame frame) { Log.logTrace("Received RST_STREAM on stream {0}", streamid); if (endStreamReceived()) { Log.logTrace("Ignoring RST_STREAM frame received on remotely closed stream {0}", streamid); } else if (closed) { Log.logTrace("Ignoring RST_STREAM frame received on closed stream {0}", streamid); } else { - boolean pushedToQueue = false; - synchronized(this) { - // if the response headers are not yet - // received, or the inputQueue is closed, handle reset directly. - // Otherwise, put it in the input queue in order to read all - // pending data frames first. Indeed, a server may send - // RST_STREAM after sending END_STREAM, in which case we should - // ignore it. However, we won't know if we have received END_STREAM - // or not until all pending data frames are read. - // Because the inputQ will not be read until the response - // headers are received, and because response headers won't be - // sent if the server sent RST_STREAM, then we must handle - // reset here directly unless responseHeadersReceived is true. - pushedToQueue = !closed && responseHeadersReceived && inputQ.tryPut(frame); - } - if (!pushedToQueue) { - // RST_STREAM was not pushed to the queue: handle it. - try { - handleReset(frame); - } catch (IOException io) { - completeResponseExceptionally(io); - } - } else { - // RST_STREAM was pushed to the queue. It will be handled by - // asyncReceive after all pending data frames have been - // processed. - Log.logTrace("RST_STREAM pushed in queue for stream {0}", streamid); - } + // put it in the input queue in order to read all + // pending data frames first. Indeed, a server may send + // RST_STREAM after sending END_STREAM, in which case we should + // ignore it. However, we won't know if we have received END_STREAM + // or not until all pending data frames are read. + receiveResetFrame(frame); + // RST_STREAM was pushed to the queue. It will be handled by + // asyncReceive after all pending data frames have been + // processed. + Log.logTrace("RST_STREAM pushed in queue for stream {0}", streamid); } } - void handleReset(ResetFrame frame) throws IOException { + void handleReset(ResetFrame frame) { Log.logTrace("Handling RST_STREAM on stream {0}", streamid); if (!closed) { close(); int error = frame.getErrorCode(); - throw new IOException(ErrorFrame.stringForCode(error)); + completeResponseExceptionally(new IOException(ErrorFrame.stringForCode(error))); } else { Log.logTrace("Ignoring RST_STREAM frame received on closed stream {0}", streamid); } @@ -431,20 +431,21 @@ class Stream<T> extends ExchangeImpl<T> { if (pushGroup == null || pushGroup.noMorePushes()) { cancelImpl(new IllegalStateException("unexpected push promise" + " on stream " + streamid)); + return; } - HttpResponse.MultiProcessor<?,T> proc = pushGroup.processor(); + HttpResponse.MultiSubscriber<?,T> proc = pushGroup.subscriber(); CompletableFuture<HttpResponse<T>> cf = pushStream.responseCF(); - Optional<HttpResponse.BodyHandler<T>> bpOpt = proc.onRequest( - pushReq); + Optional<HttpResponse.BodyHandler<T>> bpOpt = + pushGroup.handlerForPushRequest(pushReq); if (!bpOpt.isPresent()) { IOException ex = new IOException("Stream " + streamid + " cancelled by user"); if (Log.trace()) { - Log.logTrace("No body processor for {0}: {1}", pushReq, + Log.logTrace("No body subscriber for {0}: {1}", pushReq, ex.getMessage()); } pushStream.cancelImpl(ex); @@ -458,6 +459,7 @@ class Stream<T> extends ExchangeImpl<T> { // setup housekeeping for when the push is received // TODO: deal with ignoring of CF anti-pattern cf.whenComplete((HttpResponse<T> resp, Throwable t) -> { + t = Utils.getCompletionCause(t); if (Log.trace()) { Log.logTrace("Push completed on stream {0} for {1}{2}", pushStream.streamid, resp, @@ -516,34 +518,6 @@ class Stream<T> extends ExchangeImpl<T> { return requestPseudoHeaders; } - @Override - Response getResponse() throws IOException { - try { - if (request.duration() != null) { - Log.logTrace("Waiting for response (streamid={0}, timeout={1}ms)", - streamid, - request.duration().toMillis()); - return getResponseAsync(null).get( - request.duration().toMillis(), TimeUnit.MILLISECONDS); - } else { - Log.logTrace("Waiting for response (streamid={0})", streamid); - return getResponseAsync(null).join(); - } - } catch (TimeoutException e) { - Log.logTrace("Response timeout (streamid={0})", streamid); - throw new HttpTimeoutException("Response timed out"); - } catch (InterruptedException | ExecutionException | CompletionException e) { - Throwable t = e.getCause(); - Log.logTrace("Response failed (streamid={0}): {1}", streamid, t); - if (t instanceof IOException) { - throw (IOException)t; - } - throw new IOException(e); - } finally { - Log.logTrace("Got response or failed (streamid={0})", streamid); - } - } - /** Sets endStreamReceived. Should be called only once. */ void setEndStreamReceived() { assert remotelyClosed == false: "Unexpected endStream already set"; @@ -558,100 +532,247 @@ class Stream<T> extends ExchangeImpl<T> { } @Override - void sendHeadersOnly() throws IOException, InterruptedException { + CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() { + debug.log(Level.DEBUG, "sendHeadersOnly()"); if (Log.requests() && request != null) { Log.logRequest(request.toString()); } - requestContentLen = requestProcessor.contentLength(); + if (requestPublisher != null) { + requestContentLen = requestPublisher.contentLength(); + } else { + requestContentLen = 0; + } OutgoingHeaders<Stream<T>> f = headerFrame(requestContentLen); connection.sendFrame(f); + CompletableFuture<ExchangeImpl<T>> cf = new MinimalFuture<>(); + cf.complete(this); // #### good enough for now + return cf; + } + + @Override + void released() { + if (streamid > 0) { + debug.log(Level.DEBUG, "Released stream %d", streamid); + // remove this stream from the Http2Connection map. + connection.closeStream(streamid); + } else { + debug.log(Level.DEBUG, "Can't release stream %d", streamid); + } + } + + @Override + void completed() { + // There should be nothing to do here: the stream should have + // been already closed (or will be closed shortly after). } void registerStream(int id) { this.streamid = id; connection.putStream(this, streamid); + debug.log(Level.DEBUG, "Registered stream %d", id); } + void signalWindowUpdate() { + RequestSubscriber subscriber = requestSubscriber; + assert subscriber != null; + debug.log(Level.DEBUG, "Signalling window update"); + subscriber.sendScheduler.runOrSchedule(); + } + + static final ByteBuffer COMPLETED = ByteBuffer.allocate(0); class RequestSubscriber implements Flow.Subscriber<ByteBuffer> { // can be < 0 if the actual length is not known. + private final long contentLength; private volatile long remainingContentLength; private volatile Subscription subscription; + // Holds the outgoing data. There will be at most 2 outgoing ByteBuffers. + // 1) The data that was published by the request body Publisher, and + // 2) the COMPLETED sentinel, since onComplete can be invoked without demand. + final ConcurrentLinkedDeque<ByteBuffer> outgoing = new ConcurrentLinkedDeque<>(); + + private final AtomicReference<Throwable> errorRef = new AtomicReference<>(); + // A scheduler used to honor window updates. Writing must be paused + // when the window is exhausted, and resumed when the window acquires + // some space. The sendScheduler makes it possible to implement this + // behaviour in an asynchronous non-blocking way. + // See RequestSubscriber::trySend below. + final SequentialScheduler sendScheduler; + RequestSubscriber(long contentLen) { + this.contentLength = contentLen; this.remainingContentLength = contentLen; + this.sendScheduler = + SequentialScheduler.synchronizedScheduler(this::trySend); } @Override public void onSubscribe(Flow.Subscription subscription) { if (this.subscription != null) { - throw new IllegalStateException(); + throw new IllegalStateException("already subscribed"); } this.subscription = subscription; + debug.log(Level.DEBUG, "RequestSubscriber: onSubscribe, request 1"); subscription.request(1); } @Override public void onNext(ByteBuffer item) { - if (requestBodyCF.isDone()) { - throw new IllegalStateException(); - } + debug.log(Level.DEBUG, "RequestSubscriber: onNext(%d)", item.remaining()); + int size = outgoing.size(); + assert size == 0 : "non-zero size: " + size; + onNextImpl(item); + } + private void onNextImpl(ByteBuffer item) { + // Got some more request body bytes to send. + if (requestBodyCF.isDone()) { + // stream already cancelled, probably in timeout + sendScheduler.stop(); + subscription.cancel(); + return; + } + outgoing.add(item); + sendScheduler.runOrSchedule(); + } + + @Override + public void onError(Throwable throwable) { + debug.log(Level.DEBUG, () -> "RequestSubscriber: onError: " + throwable); + // ensure that errors are handled within the flow. + if (errorRef.compareAndSet(null, throwable)) { + sendScheduler.runOrSchedule(); + } + } + + @Override + public void onComplete() { + debug.log(Level.DEBUG, "RequestSubscriber: onComplete"); + int size = outgoing.size(); + assert size == 0 || size == 1 : "non-zero or one size: " + size; + // last byte of request body has been obtained. + // ensure that everything is completed within the flow. + onNextImpl(COMPLETED); + } + + // Attempts to send the data, if any. + // Handles errors and completion state. + // Pause writing if the send window is exhausted, resume it if the + // send window has some bytes that can be acquired. + void trySend() { try { - while (item.hasRemaining()) { - assert !endStreamSent : "internal error, send data after END_STREAM flag"; - DataFrame df = getDataFrame(item); - if (remainingContentLength > 0) { - remainingContentLength -= df.getDataLength(); - assert remainingContentLength >= 0; - if (remainingContentLength == 0) { - df.setFlag(DataFrame.END_STREAM); - endStreamSent = true; - } - } - connection.sendDataFrame(df); + // handle errors raised by onError; + Throwable t = errorRef.get(); + if (t != null) { + sendScheduler.stop(); + if (requestBodyCF.isDone()) return; + subscription.cancel(); + requestBodyCF.completeExceptionally(t); + return; } + + do { + // handle COMPLETED; + ByteBuffer item = outgoing.peekFirst(); + if (item == null) return; + else if (item == COMPLETED) { + sendScheduler.stop(); + complete(); + return; + } + + // handle bytes to send downstream + while (item.hasRemaining()) { + debug.log(Level.DEBUG, "trySend: %d", item.remaining()); + assert !endStreamSent : "internal error, send data after END_STREAM flag"; + DataFrame df = getDataFrame(item); + if (df == null) { + debug.log(Level.DEBUG, "trySend: can't send yet: %d", + item.remaining()); + return; // the send window is exhausted: come back later + } + + if (contentLength > 0) { + remainingContentLength -= df.getDataLength(); + if (remainingContentLength < 0) { + String msg = connection().getConnectionFlow() + + " stream=" + streamid + " " + + "[" + Thread.currentThread().getName() + "] " + + "Too many bytes in request body. Expected: " + + contentLength + ", got: " + + (contentLength - remainingContentLength); + connection.resetStream(streamid, ResetFrame.PROTOCOL_ERROR); + throw new IOException(msg); + } else if (remainingContentLength == 0) { + df.setFlag(DataFrame.END_STREAM); + endStreamSent = true; + } + } + debug.log(Level.DEBUG, "trySend: sending: %d", df.getDataLength()); + connection.sendDataFrame(df); + } + assert !item.hasRemaining(); + ByteBuffer b = outgoing.removeFirst(); + assert b == item; + } while (outgoing.peekFirst() != null); + + debug.log(Level.DEBUG, "trySend: request 1"); subscription.request(1); - } catch (InterruptedException ex) { + } catch (Throwable ex) { + debug.log(Level.DEBUG, "trySend: ", ex); + sendScheduler.stop(); subscription.cancel(); requestBodyCF.completeExceptionally(ex); } } - @Override - public void onError(Throwable throwable) { - if (requestBodyCF.isDone()) { - return; + private void complete() throws IOException { + long remaining = remainingContentLength; + long written = contentLength - remaining; + if (remaining > 0) { + connection.resetStream(streamid, ResetFrame.PROTOCOL_ERROR); + // let trySend() handle the exception + throw new IOException(connection().getConnectionFlow() + + " stream=" + streamid + " " + + "[" + Thread.currentThread().getName() +"] " + + "Too few bytes returned by the publisher (" + + written + "/" + + contentLength + ")"); } - subscription.cancel(); - requestBodyCF.completeExceptionally(throwable); - } - - @Override - public void onComplete() { - assert endStreamSent || remainingContentLength < 0; - try { - if (!endStreamSent) { - endStreamSent = true; - connection.sendDataFrame(getEmptyEndStreamDataFrame()); - } - requestBodyCF.complete(null); - } catch (InterruptedException ex) { - requestBodyCF.completeExceptionally(ex); + if (!endStreamSent) { + endStreamSent = true; + connection.sendDataFrame(getEmptyEndStreamDataFrame()); } + requestBodyCF.complete(null); } } - DataFrame getDataFrame(ByteBuffer buffer) throws InterruptedException { + /** + * Send a RESET frame to tell server to stop sending data on this stream + */ + @Override + public CompletableFuture<Void> ignoreBody() { + try { + connection.resetStream(streamid, ResetFrame.STREAM_CLOSED); + return MinimalFuture.completedFuture(null); + } catch (Throwable e) { + Log.logTrace("Error resetting stream {0}", e.toString()); + return MinimalFuture.failedFuture(e); + } + } + + DataFrame getDataFrame(ByteBuffer buffer) { int requestAmount = Math.min(connection.getMaxSendFrameSize(), buffer.remaining()); // blocks waiting for stream send window, if exhausted - int actualAmount = windowController.tryAcquire(requestAmount, streamid); + int actualAmount = windowController.tryAcquire(requestAmount, streamid, this); + if (actualAmount <= 0) return null; ByteBuffer outBuf = Utils.slice(buffer, actualAmount); - DataFrame df = new DataFrame(streamid, 0 , ByteBufferReference.of(outBuf)); + DataFrame df = new DataFrame(streamid, 0 , outBuf); return df; } - private DataFrame getEmptyEndStreamDataFrame() throws InterruptedException { - return new DataFrame(streamid, DataFrame.END_STREAM, new ByteBufferReference[0]); + private DataFrame getEmptyEndStreamDataFrame() { + return new DataFrame(streamid, DataFrame.END_STREAM, List.of()); } /** @@ -666,7 +787,7 @@ class Stream<T> extends ExchangeImpl<T> { @Override CompletableFuture<Response> getResponseAsync(Executor executor) { - CompletableFuture<Response> cf = null; + CompletableFuture<Response> cf; // The code below deals with race condition that can be caused when // completeResponse() is being called before getResponseAsync() synchronized (response_cfs) { @@ -693,7 +814,7 @@ class Stream<T> extends ExchangeImpl<T> { PushGroup<?,?> pg = exchange.getPushGroup(); if (pg != null) { // if an error occurs make sure it is recorded in the PushGroup - cf = cf.whenComplete((t,e) -> pg.pushError(e)); + cf = cf.whenComplete((t,e) -> pg.pushError(Utils.getCompletionCause(e))); } return cf; } @@ -732,10 +853,6 @@ class Stream<T> extends ExchangeImpl<T> { } } - final synchronized boolean isResponseReceived() { - return responseReceived; - } - synchronized void responseReceived() { responseReceived = true; if (requestSent) { @@ -763,9 +880,15 @@ class Stream<T> extends ExchangeImpl<T> { } CompletableFuture<Void> sendBodyImpl() { - RequestSubscriber subscriber = new RequestSubscriber(requestContentLen); - requestProcessor.subscribe(subscriber); - requestBodyCF.whenComplete((v,t) -> requestSent()); + requestBodyCF.whenComplete((v, t) -> requestSent()); + if (requestPublisher != null) { + final RequestSubscriber subscriber = new RequestSubscriber(requestContentLen); + requestPublisher.subscribe(requestSubscriber = subscriber); + } else { + // there is no request body, therefore the request is complete, + // END_STREAM has already sent with outgoing headers + requestBodyCF.complete(null); + } return requestBodyCF; } @@ -781,21 +904,30 @@ class Stream<T> extends ExchangeImpl<T> { // This method sends a RST_STREAM frame void cancelImpl(Throwable e) { + debug.log(Level.DEBUG, "cancelling stream {0}: {1}", streamid, e); if (Log.trace()) { Log.logTrace("cancelling stream {0}: {1}\n", streamid, e); } boolean closing; if (closing = !closed) { // assigning closing to !closed synchronized (this) { + failed = e; if (closing = !closed) { // assigning closing to !closed closed=true; } } } if (closing) { // true if the stream has not been closed yet - inputQ.close(); + if (responseSubscriber != null) + sched.runOrSchedule(); } completeResponseExceptionally(e); + if (!requestBodyCF.isDone()) { + requestBodyCF.completeExceptionally(e); // we may be sending the body.. + } + if (responseBodyCF != null) { + responseBodyCF.completeExceptionally(e); + } try { // will send a RST_STREAM frame if (streamid != 0) { @@ -814,14 +946,12 @@ class Stream<T> extends ExchangeImpl<T> { closed = true; } Log.logTrace("Closing stream {0}", streamid); - inputQ.close(); connection.closeStream(streamid); Log.logTrace("Stream {0} closed", streamid); } static class PushedStream<U,T> extends Stream<T> { final PushGroup<U,T> pushGroup; - private final Stream<T> parent; // used by server push streams // push streams need the response CF allocated up front as it is // given directly to user via the multi handler callback function. final CompletableFuture<Response> pushCF; @@ -829,16 +959,15 @@ class Stream<T> extends ExchangeImpl<T> { final HttpRequestImpl pushReq; HttpResponse.BodyHandler<T> pushHandler; - PushedStream(PushGroup<U,T> pushGroup, HttpClientImpl client, - Http2Connection connection, Stream<T> parent, - Exchange<T> pushReq) { + PushedStream(PushGroup<U,T> pushGroup, + Http2Connection connection, + Exchange<T> pushReq) { // ## no request body possible, null window controller - super(client, connection, pushReq, null); + super(connection, pushReq, null); this.pushGroup = pushGroup; this.pushReq = pushReq.request(); this.pushCF = new MinimalFuture<>(); this.responseCF = new MinimalFuture<>(); - this.parent = parent; } CompletableFuture<HttpResponse<T>> responseCF() { @@ -860,18 +989,21 @@ class Stream<T> extends ExchangeImpl<T> { @Override CompletableFuture<ExchangeImpl<T>> sendBodyAsync() { return super.sendBodyAsync() - .whenComplete((ExchangeImpl<T> v, Throwable t) -> pushGroup.pushError(t)); + .whenComplete((ExchangeImpl<T> v, Throwable t) + -> pushGroup.pushError(Utils.getCompletionCause(t))); } @Override CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() { return super.sendHeadersAsync() - .whenComplete((ExchangeImpl<T> ex, Throwable t) -> pushGroup.pushError(t)); + .whenComplete((ExchangeImpl<T> ex, Throwable t) + -> pushGroup.pushError(Utils.getCompletionCause(t))); } @Override CompletableFuture<Response> getResponseAsync(Executor executor) { - CompletableFuture<Response> cf = pushCF.whenComplete((v, t) -> pushGroup.pushError(t)); + CompletableFuture<Response> cf = pushCF.whenComplete( + (v, t) -> pushGroup.pushError(Utils.getCompletionCause(t))); if(executor!=null && !cf.isDone()) { cf = cf.thenApplyAsync( r -> r, executor); } @@ -890,17 +1022,18 @@ class Stream<T> extends ExchangeImpl<T> { @Override void completeResponse(Response r) { - HttpResponseImpl.logResponse(r); + Log.logResponse(r::toString); pushCF.complete(r); // not strictly required for push API - // start reading the body using the obtained BodyProcessor + // start reading the body using the obtained BodySubscriber CompletableFuture<Void> start = new MinimalFuture<>(); start.thenCompose( v -> readBodyAsync(getPushHandler(), false, getExchange().executor())) .whenComplete((T body, Throwable t) -> { if (t != null) { responseCF.completeExceptionally(t); } else { - HttpResponseImpl<T> response = new HttpResponseImpl<>(r.request, r, body, getExchange()); - responseCF.complete(response); + HttpResponseImpl<T> resp = + new HttpResponseImpl<>(r.request, r, null, body, getExchange()); + responseCF.complete(resp); } }); start.completeAsync(() -> null, getExchange().executor()); @@ -911,15 +1044,14 @@ class Stream<T> extends ExchangeImpl<T> { pushCF.completeExceptionally(t); } - @Override - synchronized void responseReceived() { - super.responseReceived(); - } +// @Override +// synchronized void responseReceived() { +// super.responseReceived(); +// } // create and return the PushResponseImpl @Override protected void handleResponse() { - HttpConnection c = connection.connection; // TODO: improve responseCode = (int)responseHeaders .firstValueAsLong(":status") .orElse(-1); @@ -932,9 +1064,11 @@ class Stream<T> extends ExchangeImpl<T> { pushReq, exchange, responseHeaders, responseCode, HttpClient.Version.HTTP_2); - this.responseContentLen = responseHeaders - .firstValueAsLong("content-length") - .orElse(-1L); + /* TODO: review if needs to be removed + the value is not used, but in case `content-length` doesn't parse + as long, there will be NumberFormatException. If left as is, make + sure code up the stack handles NFE correctly. */ + responseHeaders.firstValueAsLong("content-length"); if (Log.headers()) { StringBuilder sb = new StringBuilder("RESPONSE HEADERS"); @@ -960,4 +1094,23 @@ class Stream<T> extends ExchangeImpl<T> { } } + /** + * Returns true if this exchange was canceled. + * @return true if this exchange was canceled. + */ + synchronized boolean isCanceled() { + return failed != null; + } + + /** + * Returns the cause for which this exchange was canceled, if available. + * @return the cause for which this exchange was canceled, if available. + */ + synchronized Throwable getCancelCause() { + return failed; + } + + final String dbgString() { + return connection.dbgString() + "/Stream("+streamid+")"; + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java index 2433e2499e7..e992f9efccc 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java @@ -26,7 +26,6 @@ package jdk.incubator.http; import java.io.IOException; -import java.net.ProtocolException; import java.net.URI; import java.nio.ByteBuffer; import java.time.Duration; @@ -34,50 +33,54 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; /** - * A WebSocket client conforming to RFC 6455. + * A WebSocket client. * {@Incubating} * - * <p> A {@code WebSocket} provides full-duplex communication over a TCP - * connection. + * <p> To create a {@code WebSocket} use the {@link HttpClient#newWebSocketBuilder} + * method. To close a {@code WebSocket} use one of the {@code sendClose} or + * {@code abort} methods. * - * <p> To create a {@code WebSocket} use a {@linkplain HttpClient#newWebSocketBuilder( - * URI, Listener) builder}. Once a {@code WebSocket} is built, it's ready - * to send and receive messages. When the {@code WebSocket} is no longer needed - * it must be closed: a Close message must both be {@linkplain #sendClose - * sent} and {@linkplain Listener#onClose(WebSocket, int, String) received}. - * The {@code WebSocket} may be also closed {@linkplain #abort() abruptly}. + * <p> WebSocket messages are sent through a {@code WebSocket} and received + * through the {@code WebSocket}'s {@code Listener}. Messages can be sent until + * the output is closed, and received until the input is closed. + * A {@code WebSocket} whose output and input are both closed may be considered + * itself closed. To check these states use {@link #isOutputClosed()} and + * {@link #isInputClosed()}. * - * <p> Once closed the {@code WebSocket} remains {@linkplain #isClosed() closed} - * and cannot be reopened. + * <p> Methods that send messages return {@code CompletableFuture} which + * completes normally if the message is sent or completes exceptionally if an + * error occurs. * - * <p> Messages of type {@code X} (where {@code X} is one of: Text, Binary, - * Ping, Pong or Close) are sent and received asynchronously through the {@code - * WebSocket.send{X}} and {@link WebSocket.Listener}{@code .on{X}} methods - * respectively. Each method returns a {@link CompletionStage} which completes - * when the operation has completed. + * <p> To receive a message, first request it. If {@code n} messages are + * requested, the listener will receive up to {@code n} more invocations of the + * designated methods from the {@code WebSocket}. To request messages use + * {@link #request(long)}. Request is an additive operation, that is + * {@code request(n)} followed by {@code request(m)} is equivalent to + * {@code request(n + m)}. * - * <p> Note that messages (of any type) are received only if {@linkplain - * #request(long) requested}. + * <p> When sending or receiving a message in parts, a whole message is + * transferred as a sequence of one or more invocations where the last + * invocation is identified via an additional method argument. * - * <p> One outstanding send operation is permitted. No further send operation - * can be initiated before the previous one has completed. When sending, a - * message must not be modified until the returned {@link CompletableFuture} - * completes (either normally or exceptionally). + * <p> Unless otherwise stated, {@code null} arguments will cause methods + * of {@code WebSocket} to throw {@code NullPointerException}, similarly, + * {@code WebSocket} will not pass {@code null} arguments to methods of + * {@code Listener}. * - * <p> Text and Binary messages can be sent and received as a whole or in parts. - * A whole message is transferred as a sequence of one or more invocations of a - * corresponding method where the last invocation is identified via an - * additional method argument. + * @implSpec Methods of {@code WebSocket} are failure-atomic in respect to + * {@code NullPointerException}, {@code IllegalArgumentException} and + * {@code IllegalStateException}. That is, if a method throws said exception, or + * a returned {@code CompletableFuture} completes exceptionally with said + * exception, the {@code WebSocket} will behave as if the method has not been + * invoked at all. * - * <p> If the message is contained in a {@link ByteBuffer}, bytes are considered - * arranged from the {@code buffer}'s {@link ByteBuffer#position() position} to - * the {@code buffer}'s {@link ByteBuffer#limit() limit}. + * <p> A {@code WebSocket} invokes methods of its listener in a thread-safe + * manner. * - * <p> Unless otherwise stated, {@code null} parameter values will cause methods - * and constructors to throw {@link NullPointerException}. - * - * @implNote This implementation's methods do not block before returning - * a {@code CompletableFuture}. + * <p> {@code WebSocket} handles Ping and Close messages automatically (as per + * RFC 6455) by replying with Pong and Close messages respectively. If the + * listener receives Ping or Close messages, no mandatory actions from the + * listener are required. * * @since 9 */ @@ -97,29 +100,24 @@ public interface WebSocket { * A builder for creating {@code WebSocket} instances. * {@Incubating} * - * <p> To build a {@code WebSocket}, {@linkplain HttpClient#newWebSocketBuilder( - * URI, Listener) create} a builder, configure it as required by + * <p> To obtain a {@code WebSocket} configure a builder as required by * calling intermediate methods (the ones that return the builder itself), - * then finally call {@link #buildAsync()} to get a {@link - * CompletableFuture} with resulting {@code WebSocket}. + * then call {@code buildAsync()}. If an intermediate method is not called, + * an appropriate default value (or behavior) will be assumed. * - * <p> If an intermediate method has not been called, an appropriate - * default value (or behavior) will be used. Unless otherwise noted, a - * repeated call to an intermediate method overwrites the previous value (or - * overrides the previous behaviour). - * - * <p> Instances of {@code Builder} are not safe for use by multiple threads - * without external synchronization. + * <p> Unless otherwise stated, {@code null} arguments will cause methods of + * {@code Builder} to throw {@code NullPointerException}. * * @since 9 */ interface Builder { /** - * Adds the given name-value pair to the list of additional headers for - * the opening handshake. + * Adds the given name-value pair to the list of additional HTTP headers + * sent during the opening handshake. * - * <p> Headers defined in WebSocket Protocol are not allowed to be added. + * <p> Headers defined in WebSocket Protocol are illegal. If this method + * is not invoked, no additional HTTP headers will be sent. * * @param name * the header name @@ -131,38 +129,12 @@ public interface WebSocket { Builder header(String name, String value); /** - * Includes a request for the given subprotocols during the opening - * handshake. + * Sets a timeout for establishing a WebSocket connection. * - * <p> Among the requested subprotocols at most one will be chosen by - * the server. This subprotocol will be available from {@link - * WebSocket#getSubprotocol}. Subprotocols are specified in the order of - * preference. - * - * <p> Each of the given subprotocols must conform to the relevant - * rules defined in the WebSocket Protocol. - * - * <p> If this method is not invoked then no subprotocols are requested. - * - * @param mostPreferred - * the most preferred subprotocol - * @param lesserPreferred - * the lesser preferred subprotocols, with the least preferred - * at the end - * - * @return this builder - */ - Builder subprotocols(String mostPreferred, String... lesserPreferred); - - /** - * Sets a timeout for the opening handshake. - * - * <p> If the opening handshake does not complete within the specified - * duration then the {@code CompletableFuture} returned from {@link - * #buildAsync()} completes exceptionally with a {@link - * HttpTimeoutException}. - * - * <p> If this method is not invoked then the timeout is deemed infinite. + * <p> If the connection is not established within the specified + * duration then building of the {@code WebSocket} will fail with + * {@link HttpTimeoutException}. If this method is not invoked then the + * infinite timeout is assumed. * * @param timeout * the timeout, non-{@linkplain Duration#isNegative() negative}, @@ -173,14 +145,37 @@ public interface WebSocket { Builder connectTimeout(Duration timeout); /** - * Builds a {@code WebSocket}. + * Sets a request for the given subprotocols. * - * <p> Returns a {@code CompletableFuture<WebSocket>} which completes - * normally with the {@code WebSocket} when it is connected or completes - * exceptionally if an error occurs. + * <p> After the {@code WebSocket} has been built, the actual + * subprotocol can be queried via + * {@link WebSocket#getSubprotocol WebSocket.getSubprotocol()}. * - * <p> {@code CompletableFuture} may complete exceptionally with the - * following errors: + * <p> Subprotocols are specified in the order of preference. The most + * preferred subprotocol is specified first. If there are any additional + * subprotocols they are enumerated from the most preferred to the least + * preferred. + * + * <p> Subprotocols not conforming to the syntax of subprotocol + * identifiers are illegal. If this method is not invoked then no + * subprotocols will be requested. + * + * @param mostPreferred + * the most preferred subprotocol + * @param lesserPreferred + * the lesser preferred subprotocols + * + * @return this builder + */ + Builder subprotocols(String mostPreferred, String... lesserPreferred); + + /** + * Builds a {@link WebSocket} connected to the given {@code URI} and + * associated with the given {@code Listener}. + * + * <p> Returns a {@code CompletableFuture} which will either complete + * normally with the resulting {@code WebSocket} or complete + * exceptionally with one of the following errors: * <ul> * <li> {@link IOException} - * if an I/O error occurs @@ -188,112 +183,56 @@ public interface WebSocket { * if the opening handshake fails * <li> {@link HttpTimeoutException} - * if the opening handshake does not complete within - * the specified {@linkplain #connectTimeout(Duration) duration} + * the timeout * <li> {@link InterruptedException} - - * if the operation was interrupted + * if the operation is interrupted * <li> {@link SecurityException} - - * if a security manager is set, and the caller does not - * have a {@link java.net.URLPermission} for the WebSocket URI + * if a security manager has been installed and it denies + * {@link java.net.URLPermission access} to {@code uri}. + * <a href="HttpRequest.html#securitychecks">Security checks</a> + * contains more information relating to the security context + * in which the the listener is invoked. * <li> {@link IllegalArgumentException} - - * if any of the additional {@link #header(String, String) - * headers} are illegal; - * or if any of the WebSocket Protocol rules relevant to {@link - * #subprotocols(String, String...) subprotocols} are violated; - * or if the {@link #connectTimeout(Duration) connect timeout} - * is invalid; + * if any of the arguments of this builder's methods are + * illegal * </ul> * + * @param uri + * the WebSocket URI + * @param listener + * the listener + * * @return a {@code CompletableFuture} with the {@code WebSocket} */ - CompletableFuture<WebSocket> buildAsync(); + CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener); } /** - * A listener for events and messages on a {@code WebSocket}. + * The receiving interface of {@code WebSocket}. * {@Incubating} * - * <p> Each method of {@code Listener} corresponds to a type of event or a - * type of message. The {@code WebSocket} argument of the method is the - * {@code WebSocket} the event has occurred (the message has been received) - * on. All methods with the same {@code WebSocket} argument are invoked in a - * sequential - * (and <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility">happens-before</a>) - * order, one after another, possibly by different threads. + * <p> A {@code WebSocket} invokes methods on its listener when it receives + * messages or encounters events. The invoking {@code WebSocket} is passed + * as an argument to {@code Listener}'s methods. A {@code WebSocket} invokes + * methods on its listener in a thread-safe manner. * - * <ul> - * <li> {@link #onOpen(WebSocket) onOpen} <br> - * This method is invoked first. - * <li> {@link #onText(WebSocket, CharSequence, WebSocket.MessagePart) - * onText}, {@link #onBinary(WebSocket, ByteBuffer, WebSocket.MessagePart) - * onBinary}, {@link #onPing(WebSocket, ByteBuffer) onPing} and {@link - * #onPong(WebSocket, ByteBuffer) onPong} <br> - * These methods are invoked zero or more times after {@code onOpen}. - * <li> {@link #onClose(WebSocket, int, String) onClose}, {@link - * #onError(WebSocket, Throwable) onError} <br> - * Only one of these methods is invoked, and that method is invoked last. - * </ul> + * <p> Unless otherwise stated if a listener's method throws an exception or + * a {@code CompletionStage} returned from a method completes exceptionally, + * the {@code WebSocket} will invoke {@code onError} with this exception. * - * <p> Messages received by the {@code Listener} conform to the WebSocket - * Protocol, otherwise {@code onError} with a {@link ProtocolException} is - * invoked. - * - * <p> If a whole message is received, then the corresponding method - * ({@code onText} or {@code onBinary}) will be invoked with {@link - * WebSocket.MessagePart#WHOLE WHOLE} marker. Otherwise the method will be - * invoked with {@link WebSocket.MessagePart#FIRST FIRST}, zero or more - * times with {@link WebSocket.MessagePart#PART PART} and, finally, with - * {@link WebSocket.MessagePart#LAST LAST} markers. - * - * If any of the methods above throws an exception, {@code onError} is then - * invoked with the same {@code WebSocket} and this exception. Exceptions - * thrown from {@code onError} or {@code onClose} are ignored. - * - * <p> When the method returns, the message is deemed received (in - * particular, if contained in a {@code ByteBuffer buffer}, the data is - * deemed received completely regardless of the result {@code - * buffer.hasRemaining()} upon the method's return. After this further - * messages may be received. - * - * <p> These invocations begin asynchronous processing which might not end - * with the invocation. To provide coordination, methods of {@code Listener} - * return a {@link CompletionStage CompletionStage}. - * The {@code CompletionStage} signals the {@code WebSocket} that the - * processing of a message has ended. For convenience, methods may return - * {@code null}, which (by convention) means the same as returning an - * already completed (normally) {@code CompletionStage}. - * If the returned {@code CompletionStage} completes exceptionally, then - * {@link #onError(WebSocket, Throwable) onError} will be invoked with the - * same {@code WebSocket} and this exception. - * - * <p> Control of the message passes to the {@code Listener} with the - * invocation of the method. Control of the message returns to the {@code - * WebSocket} at the earliest of, either returning {@code null} from the - * method, or the completion of the {@code CompletionStage} returned from - * the method. The {@code WebSocket} does not access the message while it's - * not in its control. The {@code Listener} must not access the message - * after its control has been returned to the {@code WebSocket}. - * - * <p> A {@code WebSocket} implementation never invokes {@code Listener}'s - * methods with {@code null}s as their arguments. + * <p> If a listener's method returns {@code null} rather than a + * {@code CompletionStage}, {@code WebSocket} will behave as if the listener + * returned a {@code CompletionStage} that is already completed normally. * * @since 9 */ interface Listener { /** - * Notifies the {@code Listener} that it is connected to the provided - * {@code WebSocket}. + * A {@code WebSocket} has been connected. * - * <p> The {@code onOpen} method does not correspond to any message from - * the WebSocket Protocol. It is a synthetic event and the first {@code - * Listener}'s method to be invoked. - * - * <p> This method is usually used to make an initial {@linkplain - * WebSocket#request(long) request} for messages. - * - * <p> If an exception is thrown from this method then {@link - * #onError(WebSocket, Throwable) onError} will be invoked with the same - * {@code WebSocket} and this exception. + * <p> This is the first invocation and it is made at most once. This + * method is typically used to make an initial request for messages. * * @implSpec The default implementation of this method behaves as if: * @@ -302,24 +241,24 @@ public interface WebSocket { * }</pre> * * @param webSocket - * the WebSocket + * the WebSocket that has been connected */ default void onOpen(WebSocket webSocket) { webSocket.request(1); } /** - * Receives a Text message. + * A Text message has been received. * - * <p> The {@code onText} method is invoked zero or more times between - * {@code onOpen} and ({@code onClose} or {@code onError}). + * <p> If a whole message has been received, this method will be invoked + * with {@code MessagePart.WHOLE} marker. Otherwise, it will be invoked + * with {@code FIRST}, possibly a number of times with {@code PART} and, + * finally, with {@code LAST} markers. If this message is partial, it + * may be an incomplete UTF-16 sequence. However, the concatenation of + * all messages through the last will be a complete UTF-16 sequence. * - * <p> This message may be a partial UTF-16 sequence. However, the - * concatenation of all messages through the last will be a whole UTF-16 - * sequence. - * - * <p> If an exception is thrown from this method or the returned {@code - * CompletionStage} completes exceptionally, then {@link - * #onError(WebSocket, Throwable) onError} will be invoked with the same - * {@code WebSocket} and this exception. + * <p> Return a {@code CompletionStage} which will be used by the + * {@code WebSocket} as a signal it may reclaim the + * {@code CharSequence}. Do not access the {@code CharSequence} after + * this {@ode CompletionStage} has completed. * * @implSpec The default implementation of this method behaves as if: * @@ -328,18 +267,19 @@ public interface WebSocket { * return null; * }</pre> * - * @implNote This implementation passes only complete UTF-16 sequences - * to the {@code onText} method. + * @implNote This method is always invoked with character sequences + * which are complete UTF-16 sequences. * * @param webSocket - * the WebSocket + * the WebSocket on which the message has been received * @param message * the message * @param part * the part * - * @return a {@code CompletionStage} which completes when the message - * processing is done; or {@code null} if already done + * @return a {@code CompletionStage} which completes when the + * {@code CharSequence} may be reclaimed; or {@code null} if it may be + * reclaimed immediately */ default CompletionStage<?> onText(WebSocket webSocket, CharSequence message, @@ -349,15 +289,20 @@ public interface WebSocket { } /** - * Receives a Binary message. + * A Binary message has been received. * - * <p> The {@code onBinary} method is invoked zero or more times - * between {@code onOpen} and ({@code onClose} or {@code onError}). + * <p> If a whole message has been received, this method will be invoked + * with {@code MessagePart.WHOLE} marker. Otherwise, it will be invoked + * with {@code FIRST}, possibly a number of times with {@code PART} and, + * finally, with {@code LAST} markers. * - * <p> If an exception is thrown from this method or the returned {@code - * CompletionStage} completes exceptionally, then {@link - * #onError(WebSocket, Throwable) onError} will be invoked with the same - * {@code WebSocket} and this exception. + * <p> This message consists of bytes from the buffer's position to + * its limit. + * + * <p> Return a {@code CompletionStage} which will be used by the + * {@code WebSocket} as a signal it may reclaim the + * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after + * this {@ode CompletionStage} has completed. * * @implSpec The default implementation of this method behaves as if: * @@ -367,14 +312,15 @@ public interface WebSocket { * }</pre> * * @param webSocket - * the WebSocket + * the WebSocket on which the message has been received * @param message * the message * @param part * the part * - * @return a {@code CompletionStage} which completes when the message - * processing is done; or {@code null} if already done + * @return a {@code CompletionStage} which completes when the + * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be + * reclaimed immediately */ default CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message, @@ -384,34 +330,15 @@ public interface WebSocket { } /** - * Receives a Ping message. + * A Ping message has been received. * - * <p> A Ping message may be sent or received by either client or - * server. It may serve either as a keepalive or as a means to verify - * that the remote endpoint is still responsive. + * <p> The message consists of not more than {@code 125} bytes from + * the buffer's position to its limit. * - * <p> The {@code WebSocket} handles Ping messages by replying with - * appropriate Pong messages using a strategy of its choice, but within - * the boundaries of the WebSocket Protocol. The {@code WebSocket} may - * invoke {@code onPing} after handling a Ping message, before doing so - * or in parallel with it. In other words no particular ordering is - * guaranteed. If an error occurs while implementation handles this Ping - * message, then {@code onError} will be invoked with this error. For - * more details on handling Ping messages see RFC 6455 sections - * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">5.5.2. Ping</a> - * and - * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">5.5.3. Pong</a>. - * - * <p> The message will consist of not more than {@code 125} bytes: - * {@code message.remaining() <= 125}. - * - * <p> The {@code onPing} is invoked zero or more times in between - * {@code onOpen} and ({@code onClose} or {@code onError}). - * - * <p> If an exception is thrown from this method or the returned {@code - * CompletionStage} completes exceptionally, then {@link - * #onError(WebSocket, Throwable) onError} will be invoked with this - * exception. + * <p> Return a {@code CompletionStage} which will be used by the + * {@code WebSocket} as a signal it may reclaim the + * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after + * this {@ode CompletionStage} has completed. * * @implSpec The default implementation of this method behaves as if: * @@ -421,12 +348,13 @@ public interface WebSocket { * }</pre> * * @param webSocket - * the WebSocket + * the WebSocket on which the message has been received * @param message * the message * - * @return a {@code CompletionStage} which completes when the message - * processing is done; or {@code null} if already done + * @return a {@code CompletionStage} which completes when the + * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be + * reclaimed immediately */ default CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) { @@ -435,22 +363,15 @@ public interface WebSocket { } /** - * Receives a Pong message. + * A Pong message has been received. * - * <p> A Pong message may be unsolicited or may be received in response - * to a previously sent Ping. In the latter case, the contents of the - * Pong is identical to the originating Ping. + * <p> The message consists of not more than {@code 125} bytes from + * the buffer's position to its limit. * - * <p> The message will consist of not more than {@code 125} bytes: - * {@code message.remaining() <= 125}. - * - * <p> The {@code onPong} method is invoked zero or more times in - * between {@code onOpen} and ({@code onClose} or {@code onError}). - * - * <p> If an exception is thrown from this method or the returned {@code - * CompletionStage} completes exceptionally, then {@link - * #onError(WebSocket, Throwable) onError} will be invoked with this - * exception. + * <p> Return a {@code CompletionStage} which will be used by the + * {@code WebSocket} as a signal it may reclaim the + * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after + * this {@ode CompletionStage} has completed. * * @implSpec The default implementation of this method behaves as if: * @@ -460,12 +381,13 @@ public interface WebSocket { * }</pre> * * @param webSocket - * the WebSocket + * the WebSocket on which the message has been received * @param message * the message * - * @return a {@code CompletionStage} which completes when the message - * processing is done; or {@code null} if already done + * @return a {@code CompletionStage} which completes when the + * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be + * reclaimed immediately */ default CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) { @@ -474,52 +396,39 @@ public interface WebSocket { } /** - * Receives a Close message. + * A Close message has been received. + * + * <p> This is the last invocation from the {@code WebSocket}. By the + * time this invocation begins the {@code WebSocket}'s input will have + * been closed. Be prepared to receive this invocation at any time after + * {@code onOpen} regardless of whether or not any messages have been + * requested from the {@code WebSocket}. * * <p> A Close message consists of a status code and a reason for - * closing. The status code is an integer in the range {@code 1000 <= - * code <= 65535}. The {@code reason} is a short string that has an - * UTF-8 representation not longer than {@code 123} bytes. For more - * details on Close message, status codes and reason see RFC 6455 sections - * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">5.5.1. Close</a> - * and - * <a href="https://tools.ietf.org/html/rfc6455#section-7.4">7.4. Status Codes</a>. + * closing. The status code is an integer from the range + * {@code 1000 <= code <= 65535}. The {@code reason} is a string which + * has an UTF-8 representation not longer than {@code 123} bytes. * - * <p> After the returned {@code CompletionStage} has completed - * (normally or exceptionally), the {@code WebSocket} completes the - * closing handshake by replying with an appropriate Close message. + * <p> Return a {@code CompletionStage} that will be used by the + * {@code WebSocket} as a signal that it may close the output. The + * {@code WebSocket} will close the output at the earliest of completion + * of the returned {@code CompletionStage} or invoking a + * {@link WebSocket#sendClose(int, String) sendClose} method. * - * <p> This implementation replies with a Close message that has the - * same code this message has and an empty reason. - * - * <p> {@code onClose} is the last invocation on the {@code Listener}. - * It is invoked at most once, but after {@code onOpen}. If an exception - * is thrown from this method, it is ignored. - * - * <p> The {@code WebSocket} will close at the earliest of completion of - * the returned {@code CompletionStage} or sending a Close message. In - * particular, if a Close message has been {@linkplain WebSocket#sendClose - * sent} before, then this invocation completes the closing handshake - * and by the time this method is invoked, the {@code WebSocket} will - * have been closed. - * - * @implSpec The default implementation of this method behaves as if: - * - * <pre>{@code - * return null; - * }</pre> + * <p> If an exception is thrown from this method or a + * {@code CompletionStage} returned from it completes exceptionally, + * the resulting behaviour is undefined. * * @param webSocket - * the WebSocket + * the WebSocket on which the message has been received * @param statusCode * the status code * @param reason * the reason * - * @return a {@code CompletionStage} which completes when the {@code - * WebSocket} can be closed; or {@code null} if it can be closed immediately - * - * @see #NORMAL_CLOSURE + * @return a {@code CompletionStage} which completes when the + * {@code WebSocket} may be closed; or {@code null} if it may be + * closed immediately */ default CompletionStage<?> onClose(WebSocket webSocket, int statusCode, @@ -528,31 +437,19 @@ public interface WebSocket { } /** - * Notifies an I/O or protocol error has occurred. + * An unrecoverable error has occurred. * - * <p> The {@code onError} method does not correspond to any message - * from the WebSocket Protocol. It is a synthetic event and the last - * {@code Listener}'s method to be invoked. It is invoked at most once - * but after {@code onOpen}. If an exception is thrown from this method, - * it is ignored. + * <p> This is the last invocation from the {@code WebSocket}. By the + * time this invocation begins both {@code WebSocket}'s input and output + * will have been closed. Be prepared to receive this invocation at any + * time after {@code onOpen} regardless of whether or not any messages + * have been requested from the {@code WebSocket}. * - * <p> Note that the WebSocket Protocol requires <i>some</i> errors - * occur in the incoming destination must be fatal to the connection. In - * such cases the implementation takes care of <i>Failing the WebSocket - * Connection</i>: by the time {@code onError} is invoked, the {@code - * WebSocket} will have been closed. Any outstanding or subsequent send - * operation will complete exceptionally with an {@code IOException}. - * For more details on Failing the WebSocket Connection see RFC 6455 - * section <a href="https://tools.ietf.org/html/rfc6455#section-7.1.7">7.1.7. Fail the WebSocket Connection</a>. - * - * @apiNote Errors associated with sending messages are reported to the - * {@code CompletableFuture}s {@code sendX} methods return, rather than - * to this this method. - * - * @implSpec The default implementation of this method does nothing. + * <p> If an exception is thrown from this method, resulting behavior is + * undefined. * * @param webSocket - * the WebSocket + * the WebSocket on which the error has occurred * @param error * the error */ @@ -560,29 +457,26 @@ public interface WebSocket { } /** - * A marker used by {@link WebSocket.Listener} in cases where a partial - * message may be received. + * A marker used by {@link WebSocket.Listener} for identifying partial + * messages. * {@Incubating} * - * @see Listener#onText(WebSocket, CharSequence, MessagePart) - * @see Listener#onBinary(WebSocket, ByteBuffer, MessagePart) - * * @since 9 */ enum MessagePart { /** - * The first part of a message in a sequence. + * The first part of a message. */ FIRST, /** - * A middle part of a message in a sequence. + * A middle part of a message. */ PART, /** - * The last part of a message in a sequence. + * The last part of a message. */ LAST, @@ -595,31 +489,28 @@ public interface WebSocket { /** * Sends a Text message with characters from the given {@code CharSequence}. * - * <p> Returns a {@code CompletableFuture<WebSocket>} which completes - * normally when the message has been sent or completes exceptionally if an - * error occurs. + * <p> To send a Text message invoke this method only after the previous + * Text or Binary message has been sent. The character sequence must not be + * modified until the {@code CompletableFuture} returned from this method + * has completed. * - * <p> The {@code CharSequence} must not be modified until the returned - * {@code CompletableFuture} completes (either normally or exceptionally). - * - * <p> The returned {@code CompletableFuture} can complete exceptionally - * with: + * <p> A {@code CompletableFuture} returned from this method can + * complete exceptionally with: * <ul> * <li> {@link IllegalArgumentException} - * if {@code message} is a malformed UTF-16 sequence * <li> {@link IllegalStateException} - - * if the {@code WebSocket} is closed; - * or if a Close message has been sent; - * or if there is an outstanding send operation; - * or if a previous Binary message was sent with {@code isLast == false} + * if {@code sendClose} has been invoked + * or if the previous message has not been sent yet + * or if a previous Binary message was sent with + * {@code isLast == false} * <li> {@link IOException} - - * if an I/O error occurs during this operation; - * or if the {@code WebSocket} has been closed due to an error; + * if an I/O error occurs * </ul> * - * @implNote This implementation does not accept partial UTF-16 sequences. - * In case such a sequence is passed, a returned {@code CompletableFuture} - * completes exceptionally with {@code IOException}. + * @implNote If a partial UTF-16 sequence is passed to this method, a + * {@code CompletableFuture} returned will complete exceptionally with + * {@code IOException}. * * @param message * the message @@ -627,28 +518,30 @@ public interface WebSocket { * {@code true} if this is the last part of the message, * {@code false} otherwise * - * @return a {@code CompletableFuture} with this {@code WebSocket} + * @return a {@code CompletableFuture} that completes, with this + * {@code WebSocket}, when the message has been sent */ CompletableFuture<WebSocket> sendText(CharSequence message, boolean isLast); /** * Sends a Binary message with bytes from the given {@code ByteBuffer}. * - * <p> Returns a {@code CompletableFuture<WebSocket>} which completes - * normally when the message has been sent or completes exceptionally if an - * error occurs. + * <p> To send a Binary message invoke this method only after the previous + * Text or Binary message has been sent. The message consists of bytes from + * the buffer's position to its limit. Upon normal completion of a + * {@code CompletableFuture} returned from this method the buffer will have + * no remaining bytes. The buffer must not be accessed until after that. * - * <p> The returned {@code CompletableFuture} can complete exceptionally - * with: + * <p> The {@code CompletableFuture} returned from this method can + * complete exceptionally with: * <ul> * <li> {@link IllegalStateException} - - * if the {@code WebSocket} is closed; - * or if a Close message has been sent; - * or if there is an outstanding send operation; - * or if a previous Text message was sent with {@code isLast == false} + * if {@code sendClose} has been invoked + * or if the previous message has not been sent yet + * or if a previous Text message was sent with + * {@code isLast == false} * <li> {@link IOException} - - * if an I/O error occurs during this operation; - * or if the {@code WebSocket} has been closed due to an error + * if an I/O error occurs * </ul> * * @param message @@ -657,200 +550,169 @@ public interface WebSocket { * {@code true} if this is the last part of the message, * {@code false} otherwise * - * @return a {@code CompletableFuture} with this {@code WebSocket} + * @return a {@code CompletableFuture} that completes, with this + * {@code WebSocket}, when the message has been sent */ CompletableFuture<WebSocket> sendBinary(ByteBuffer message, boolean isLast); /** - * Sends a Ping message with bytes from the given ByteBuffer. + * Sends a Ping message with bytes from the given {@code ByteBuffer}. * - * <p> Returns a {@code CompletableFuture<WebSocket>} which completes - * normally when the message has been sent or completes exceptionally if an - * error occurs. + * <p> The message consists of not more than {@code 125} bytes from the + * buffer's position to its limit. Upon normal completion of a + * {@code CompletableFuture} returned from this method the buffer will + * have no remaining bytes. The buffer must not be accessed until after that. * - * <p> A Ping message may be sent or received by either client or server. - * It may serve either as a keepalive or as a means to verify that the - * remote endpoint is still responsive. - * - * <p> The message must consist of not more than {@code 125} bytes: {@code - * message.remaining() <= 125}. - * - * <p> The returned {@code CompletableFuture} can complete exceptionally - * with: + * <p> The {@code CompletableFuture} returned from this method can + * complete exceptionally with: * <ul> * <li> {@link IllegalArgumentException} - - * if {@code message.remaining() > 125} + * if the message is too long * <li> {@link IllegalStateException} - - * if the {@code WebSocket} is closed; - * or if a Close message has been sent; - * or if there is an outstanding send operation + * if {@code sendClose} has been invoked * <li> {@link IOException} - - * if an I/O error occurs during this operation; - * or if the {@code WebSocket} has been closed due to an error + * if an I/O error occurs * </ul> * * @param message * the message * - * @return a {@code CompletableFuture} with this {@code WebSocket} + * @return a {@code CompletableFuture} that completes, with this + * {@code WebSocket}, when the Ping message has been sent */ CompletableFuture<WebSocket> sendPing(ByteBuffer message); /** - * Sends a Pong message with bytes from the given ByteBuffer. + * Sends a Pong message with bytes from the given {@code ByteBuffer}. * - * <p> Returns a {@code CompletableFuture<WebSocket>} which completes - * normally when the message has been sent or completes exceptionally if an - * error occurs. + * <p> The message consists of not more than {@code 125} bytes from the + * buffer's position to its limit. Upon normal completion of a + * {@code CompletableFuture} returned from this method the buffer will have + * no remaining bytes. The buffer must not be accessed until after that. * - * <p> A Pong message may be unsolicited or may be sent in response to a - * previously received Ping. In latter case the contents of the Pong must be - * identical to the originating Ping. - * - * <p> The message must consist of not more than {@code 125} bytes: {@code - * message.remaining() <= 125}. - * - * <p> The returned {@code CompletableFuture} can complete exceptionally - * with: + * <p> The {@code CompletableFuture} returned from this method can + * complete exceptionally with: * <ul> * <li> {@link IllegalArgumentException} - - * if {@code message.remaining() > 125} + * if the message is too long * <li> {@link IllegalStateException} - - * if the {@code WebSocket} is closed; - * or if a Close message has been sent; - * or if there is an outstanding send operation + * if {@code sendClose} has been invoked * <li> {@link IOException} - - * if an I/O error occurs during this operation; - * or if the {@code WebSocket} has been closed due to an error + * if an I/O error occurs * </ul> * * @param message * the message * - * @return a {@code CompletableFuture} with this {@code WebSocket} + * @return a {@code CompletableFuture} that completes, with this + * {@code WebSocket}, when the Pong message has been sent */ CompletableFuture<WebSocket> sendPong(ByteBuffer message); /** - * Sends a Close message with the given status code and the reason. + * Sends a Close message with the given status code and the reason, + * initiating an orderly closure. * - * <p> When this method has been invoked, no further messages can be sent. + * <p> When this method returns the output will have been closed. * - * <p> The {@code statusCode} is an integer in the range {@code 1000 <= code - * <= 4999}. However, not all status codes may be legal in some - * implementations. Regardless of an implementation, - * <code>{@value jdk.incubator.http.WebSocket#NORMAL_CLOSURE}</code> - * is always legal and {@code 1002}, {@code 1003}, {@code 1005}, {@code - * 1006}, {@code 1007}, {@code 1009}, {@code 1010}, {@code 1012}, {@code - * 1013} and {@code 1015} are always illegal codes. + * <p> The {@code statusCode} is an integer from the range + * {@code 1000 <= code <= 4999}. Status codes {@code 1002}, {@code 1003}, + * {@code 1006}, {@code 1007}, {@code 1009}, {@code 1010}, {@code 1012}, + * {@code 1013} and {@code 1015} are illegal. Behaviour in respect to other + * status codes is implementation-specific. The {@code reason} is a string + * that has an UTF-8 representation not longer than {@code 123} bytes. * - * <p> The {@code reason} is a short string that must have an UTF-8 - * representation not longer than {@code 123} bytes. For more details on - * Close message, status codes and reason see RFC 6455 sections - * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">5.5.1. Close</a> - * and - * <a href="https://tools.ietf.org/html/rfc6455#section-7.4">7.4. Status Codes</a>. + * <p> Use the provided integer constant {@link #NORMAL_CLOSURE} as a status + * code and an empty string as a reason in a typical case. * - * <p> The method returns a {@code CompletableFuture<WebSocket>} which - * completes normally when the message has been sent or completes - * exceptionally if an error occurs. - * - * <p> The returned {@code CompletableFuture} can complete exceptionally - * with: + * <p> A {@code CompletableFuture} returned from this method can + * complete exceptionally with: * <ul> * <li> {@link IllegalArgumentException} - - * if the {@code statusCode} has an illegal value; - * or if {@code reason} doesn't have an UTF-8 representation of - * length {@code <= 123} + * if {@code statusCode} or {@code reason} are illegal * <li> {@link IOException} - - * if an I/O error occurs during this operation; - * or the {@code WebSocket} has been closed due to an error + * if an I/O error occurs * </ul> * - * <p> If this method has already been invoked or the {@code WebSocket} is - * closed, then subsequent invocations of this method have no effect and the - * returned {@code CompletableFuture} completes normally. - * - * <p> If a Close message has been {@linkplain Listener#onClose(WebSocket, - * int, String) received} before, then this invocation completes the closing - * handshake and by the time the returned {@code CompletableFuture} - * completes, the {@code WebSocket} will have been closed. + * @implSpec An endpoint sending a Close message might not receive a + * complementing Close message in a timely manner for a variety of reasons. + * The {@code WebSocket} implementation is responsible for providing a + * closure mechanism that guarantees that once {@code sendClose} method has + * been invoked the {@code WebSocket} will close regardless of whether or + * not a Close frame has been received and without further intervention from + * the user of this API. Method {@code sendClose} is designed to be, + * possibly, the last call from the user of this API. * * @param statusCode * the status code * @param reason * the reason * - * @return a {@code CompletableFuture} with this {@code WebSocket} + * @return a {@code CompletableFuture} that completes, with this + * {@code WebSocket}, when the Close message has been sent */ CompletableFuture<WebSocket> sendClose(int statusCode, String reason); /** - * Allows {@code n} more messages to be received by the {@link Listener - * Listener}. + * Requests {@code n} more messages from this {@code WebSocket}. * - * <p> The actual number of received messages might be fewer if a Close - * message is received, the {@code WebSocket} closes or an error occurs. + * <p> This {@code WebSocket} will invoke its listener's {@code onText}, + * {@code onBinary}, {@code onPing}, {@code onPong} or {@code onClose} + * methods up to {@code n} more times. * - * <p> A {@code WebSocket} that has just been built, hasn't requested - * anything yet. Usually the initial request for messages is made in {@link - * Listener#onOpen(jdk.incubator.http.WebSocket) Listener.onOpen}. - * - * <p> If the {@code WebSocket} is closed then invoking this method has no - * effect. - * - * @implNote This implementation does not distinguish between partial and - * whole messages, because it's not known beforehand how a message will be - * received. - * - * <p> If a server sends more messages than requested, this implementation - * queues up these messages on the TCP connection and may eventually force - * the sender to stop sending through TCP flow control. + * <p> This method may be invoked at any time. * * @param n - * the number of messages + * the number of messages requested * * @throws IllegalArgumentException - * if {@code n < 0} + * if {@code n <= 0} */ void request(long n); /** - * Returns a {@linkplain Builder#subprotocols(String, String...) subprotocol} - * which has been chosen for this {@code WebSocket}. + * Returns the subprotocol for this {@code WebSocket}. * - * @return a subprotocol, or an empty {@code String} if there is none + * <p> This method may be invoked at any time. + * + * @return the subprotocol for this {@code WebSocket}, or an empty + * {@code String} if there's no subprotocol */ String getSubprotocol(); /** - * Tells whether the {@code WebSocket} is closed. + * Tells whether or not this {@code WebSocket} is permanently closed + * for sending messages. * - * <p> When a {@code WebSocket} is closed no further messages can be sent or - * received. + * <p> If this method returns {@code true}, subsequent invocations will also + * return {@code true}. This method may be invoked at any time. * - * @return {@code true} if the {@code WebSocket} is closed, - * {@code false} otherwise + * @return {@code true} if closed, {@code false} otherwise */ - boolean isClosed(); + boolean isOutputClosed(); /** - * Closes the {@code WebSocket} abruptly. + * Tells whether or not this {@code WebSocket} is permanently closed + * for receiving messages. * - * <p> This method may be invoked at any time. This method closes the - * underlying TCP connection and puts the {@code WebSocket} into a closed - * state. + * <p> If this method returns {@code true}, subsequent invocations will also + * return {@code true}. This method may be invoked at any time. * - * <p> As the result {@link Listener#onClose(WebSocket, int, String) - * Listener.onClose} will be invoked unless either {@code onClose} or {@link - * Listener#onError(WebSocket, Throwable) onError} has been invoked before. - * In which case no additional invocation will happen. - * - * <p> If the {@code WebSocket} is already closed then invoking this method - * has no effect. - * - * @throws IOException - * if an I/O error occurs + * @return {@code true} if closed, {@code false} otherwise */ - void abort() throws IOException; + boolean isInputClosed(); + + /** + * Closes this {@code WebSocket} abruptly. + * + * <p> When this method returns both the input and output will have been + * closed. This method may be invoked at any time. Subsequent invocations + * have no effect. + * + * @apiNote Depending on its implementation, the state (for example, whether + * or not a message is being transferred at the moment) and possible errors + * while releasing associated resources, this {@code WebSocket} may invoke + * its listener's {@code onError}. + */ + void abort(); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java index 8c354083302..988778f4df6 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java index 76ac343befa..765f9386a46 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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,14 +25,19 @@ package jdk.incubator.http; +import java.lang.System.Logger.Level; +import java.util.ArrayList; import java.util.Map; import java.util.HashMap; -import java.util.concurrent.locks.Condition; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; import java.util.concurrent.locks.ReentrantLock; +import jdk.incubator.http.internal.common.Utils; /** - * A Simple blocking Send Window Flow-Controller that is used to control - * outgoing Connection and Stream flows, per HTTP/2 connection. + * A Send Window Flow-Controller that is used to control outgoing Connection + * and Stream flows, per HTTP/2 connection. * * A Http2Connection has its own unique single instance of a WindowController * that it shares with its Streams. Each stream must acquire the appropriate @@ -44,6 +49,10 @@ import java.util.concurrent.locks.ReentrantLock; */ final class WindowController { + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary developer's flag + static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("WindowController"::toString, DEBUG); + /** * Default initial connection Flow-Control Send Window size, as per HTTP/2. */ @@ -54,20 +63,23 @@ final class WindowController { /** A Map of the active streams, where the key is the stream id, and the * value is the stream's Send Window size, which may be negative. */ private final Map<Integer,Integer> streams = new HashMap<>(); + /** A Map of streams awaiting Send Window. The key is the stream id. The + * value is a pair of the Stream ( representing the key's stream id ) and + * the requested amount of send Window. */ + private final Map<Integer, Map.Entry<Stream<?>, Integer>> pending + = new LinkedHashMap<>(); private final ReentrantLock controllerLock = new ReentrantLock(); - private final Condition notExhausted = controllerLock.newCondition(); - /** A Controller with the default initial window size. */ WindowController() { connectionWindowSize = DEFAULT_INITIAL_WINDOW_SIZE; } - /** A Controller with the given initial window size. */ - WindowController(int initialConnectionWindowSize) { - connectionWindowSize = initialConnectionWindowSize; - } +// /** A Controller with the given initial window size. */ +// WindowController(int initialConnectionWindowSize) { +// connectionWindowSize = initialConnectionWindowSize; +// } /** Registers the given stream with this controller. */ void registerStream(int streamid, int initialStreamWindowSize) { @@ -75,7 +87,8 @@ final class WindowController { try { Integer old = streams.put(streamid, initialStreamWindowSize); if (old != null) - throw new InternalError("Unexpected entry [" + old + "] for streamid: " + streamid); + throw new InternalError("Unexpected entry [" + + old + "] for streamid: " + streamid); } finally { controllerLock.unlock(); } @@ -109,28 +122,45 @@ final class WindowController { * 1) the requested amount, 2) the stream's Send Window, and 3) the * connection's Send Window. * - * This method ( currently ) blocks until some positive amount of Send - * Window is available. + * A negative or zero value is returned if there's no window available. + * When the result is negative or zero, this method arranges for the + * given stream's {@link Stream#signalWindowUpdate()} method to be invoke at + * a later time when the connection and/or stream window's have been + * increased. The {@code tryAcquire} method should then be invoked again to + * attempt to acquire the available window. */ - int tryAcquire(int requestAmount, int streamid) throws InterruptedException { + int tryAcquire(int requestAmount, int streamid, Stream<?> stream) { controllerLock.lock(); try { - int x = 0; - Integer streamSize = 0; - while (x <= 0) { - streamSize = streams.get(streamid); - if (streamSize == null) - throw new InternalError("Expected entry for streamid: " + streamid); - x = Math.min(requestAmount, + Integer streamSize = streams.get(streamid); + if (streamSize == null) + throw new InternalError("Expected entry for streamid: " + + streamid); + int x = Math.min(requestAmount, Math.min(streamSize, connectionWindowSize)); - if (x <= 0) // stream window size may be negative - notExhausted.await(); + if (x <= 0) { // stream window size may be negative + DEBUG_LOGGER.log(Level.DEBUG, + "Stream %d requesting %d but only %d available (stream: %d, connection: %d)", + streamid, requestAmount, Math.min(streamSize, connectionWindowSize), + streamSize, connectionWindowSize); + // If there's not enough window size available, put the + // caller in a pending list. + pending.put(streamid, Map.entry(stream, requestAmount)); + return x; } + // Remove the caller from the pending list ( if was waiting ). + pending.remove(streamid); + + // Update window sizes and return the allocated amount to the caller. streamSize -= x; streams.put(streamid, streamSize); connectionWindowSize -= x; + DEBUG_LOGGER.log(Level.DEBUG, + "Stream %d amount allocated %d, now %d available (stream: %d, connection: %d)", + streamid, x, Math.min(streamSize, connectionWindowSize), + streamSize, connectionWindowSize); return x; } finally { controllerLock.unlock(); @@ -140,10 +170,15 @@ final class WindowController { /** * Increases the Send Window size for the connection. * + * A number of awaiting requesters, from unfulfilled tryAcquire requests, + * may have their stream's {@link Stream#signalWindowUpdate()} method + * scheduled to run ( i.e. awake awaiters ). + * * @return false if, and only if, the addition of the given amount would * cause the Send Window to exceed 2^31-1 (overflow), otherwise true */ boolean increaseConnectionWindow(int amount) { + List<Stream<?>> candidates = null; controllerLock.lock(); try { int size = connectionWindowSize; @@ -151,20 +186,54 @@ final class WindowController { if (size < 0) return false; connectionWindowSize = size; - notExhausted.signalAll(); + DEBUG_LOGGER.log(Level.DEBUG, "Connection window size is now %d", size); + + // Notify waiting streams, until the new increased window size is + // effectively exhausted. + Iterator<Map.Entry<Integer,Map.Entry<Stream<?>,Integer>>> iter = + pending.entrySet().iterator(); + + while (iter.hasNext() && size > 0) { + Map.Entry<Integer,Map.Entry<Stream<?>,Integer>> item = iter.next(); + Integer streamSize = streams.get(item.getKey()); + if (streamSize == null) { + iter.remove(); + } else { + Map.Entry<Stream<?>,Integer> e = item.getValue(); + int requestedAmount = e.getValue(); + // only wakes up the pending streams for which there is + // at least 1 byte of space in both windows + int minAmount = 1; + if (size >= minAmount && streamSize >= minAmount) { + size -= Math.min(streamSize, requestedAmount); + iter.remove(); + if (candidates == null) + candidates = new ArrayList<>(); + candidates.add(e.getKey()); + } + } + } } finally { controllerLock.unlock(); } + if (candidates != null) { + candidates.forEach(Stream::signalWindowUpdate); + } return true; } /** * Increases the Send Window size for the given stream. * + * If the given stream is awaiting window size, from an unfulfilled + * tryAcquire request, it will have its stream's {@link + * Stream#signalWindowUpdate()} method scheduled to run ( i.e. awoken ). + * * @return false if, and only if, the addition of the given amount would * cause the Send Window to exceed 2^31-1 (overflow), otherwise true */ boolean increaseStreamWindow(int amount, int streamid) { + Stream<?> s = null; controllerLock.lock(); try { Integer size = streams.get(streamid); @@ -174,10 +243,27 @@ final class WindowController { if (size < 0) return false; streams.put(streamid, size); - notExhausted.signalAll(); + DEBUG_LOGGER.log(Level.DEBUG, + "Stream %s window size is now %s", streamid, size); + + Map.Entry<Stream<?>,Integer> p = pending.get(streamid); + if (p != null) { + int minAmount = 1; + // only wakes up the pending stream if there is at least + // 1 byte of space in both windows + if (size >= minAmount + && connectionWindowSize >= minAmount) { + pending.remove(streamid); + s = p.getKey(); + } + } } finally { controllerLock.unlock(); } + + if (s != null) + s.signalWindowUpdate(); + return true; } @@ -199,6 +285,8 @@ final class WindowController { Integer size = entry.getValue(); size += adjustAmount; streams.put(streamid, size); + DEBUG_LOGGER.log(Level.DEBUG, + "Stream %s window size is now %s", streamid, size); } } } finally { @@ -216,16 +304,17 @@ final class WindowController { } } - /** Returns the Send Window size for the given stream. */ - int streamWindowSize(int streamid) { - controllerLock.lock(); - try { - Integer size = streams.get(streamid); - if (size == null) - throw new InternalError("Expected entry for streamid: " + streamid); - return size; - } finally { - controllerLock.unlock();; - } - } +// /** Returns the Send Window size for the given stream. */ +// int streamWindowSize(int streamid) { +// controllerLock.lock(); +// try { +// Integer size = streams.get(streamid); +// if (size == null) +// throw new InternalError("Expected entry for streamid: " + streamid); +// return size; +// } finally { +// controllerLock.unlock(); +// } +// } + } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java index ecc83de8ca0..1e9975827be 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -24,13 +24,18 @@ */ package jdk.incubator.http; +import java.lang.System.Logger.Level; import jdk.incubator.http.internal.frame.SettingsFrame; import jdk.incubator.http.internal.frame.WindowUpdateFrame; +import jdk.incubator.http.internal.common.Utils; import java.util.concurrent.atomic.AtomicInteger; abstract class WindowUpdateSender { + final static boolean DEBUG = Utils.DEBUG; + final System.Logger debug = + Utils.getDebugLogger(this::dbgString, DEBUG); final int limit; final Http2Connection connection; @@ -59,6 +64,7 @@ abstract class WindowUpdateSender { abstract int getStreamId(); void update(int delta) { + debug.log(Level.DEBUG, "update: %d", delta); if (received.addAndGet(delta) > limit) { synchronized (this) { int tosend = received.get(); @@ -71,8 +77,12 @@ abstract class WindowUpdateSender { } void sendWindowUpdate(int delta) { + debug.log(Level.DEBUG, "sending window update: %d", delta); connection.sendUnorderedFrame(new WindowUpdateFrame(getStreamId(), delta)); } + String dbgString() { + return "WindowUpdateSender(stream: " + getStreamId() + ")"; + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java deleted file mode 100644 index 2557c1f9459..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2017, 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. 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. - */ -package jdk.incubator.http.internal.common; - -import jdk.incubator.http.internal.frame.DataFrame; -import jdk.incubator.http.internal.frame.Http2Frame; - -import java.io.Closeable; -import java.io.IOException; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -/** - * Http2Frame Producer-Consumer queue which either allows to consume all frames in blocking way - * or allows to consume it asynchronously. In the latter case put operation from the producer thread - * executes consume operation in the given executor. - */ -public class AsyncDataReadQueue implements Closeable { - - @FunctionalInterface - public interface DataConsumer { - /** - * - * @param t - frame - * @return true if consuming should be continued. false when END_STREAM was received. - * @throws Throwable - */ - boolean accept(Http2Frame t) throws Throwable; - } - - private static final int BLOCKING = 0; - private static final int FLUSHING = 1; - private static final int REFLUSHING = 2; - private static final int ASYNC = 3; - private static final int CLOSED = 4; - - - private final AtomicInteger state = new AtomicInteger(BLOCKING); - private final BlockingQueue<Http2Frame> queue = new LinkedBlockingQueue<>(); - private Executor executor; - private DataConsumer onData; - private Consumer<Throwable> onError; - - public AsyncDataReadQueue() { - } - - public boolean tryPut(Http2Frame f) { - if(state.get() == CLOSED) { - return false; - } else { - queue.offer(f); - flushAsync(false); - return true; - } - } - - public void put(Http2Frame f) throws IOException { - if(!tryPut(f)) - throw new IOException("stream closed"); - } - - public void blockingReceive(DataConsumer onData, Consumer<Throwable> onError) { - if (state.get() == CLOSED) { - onError.accept(new IOException("stream closed")); - return; - } - assert state.get() == BLOCKING; - try { - while (onData.accept(queue.take())); - assert state.get() == CLOSED; - } catch (Throwable e) { - onError.accept(e); - } - } - - public void asyncReceive(Executor executor, DataConsumer onData, - Consumer<Throwable> onError) { - if (state.get() == CLOSED) { - onError.accept(new IOException("stream closed")); - return; - } - - assert state.get() == BLOCKING; - - // Validates that fields not already set. - if (!checkCanSet("executor", this.executor, onError) - || !checkCanSet("onData", this.onData, onError) - || !checkCanSet("onError", this.onError, onError)) { - return; - } - - this.executor = executor; - this.onData = onData; - this.onError = onError; - - // This will report an error if asyncReceive is called twice, - // because we won't be in BLOCKING state if that happens - if (!this.state.compareAndSet(BLOCKING, ASYNC)) { - onError.accept(new IOException( - new IllegalStateException("State: "+this.state.get()))); - return; - } - - flushAsync(true); - } - - private static <T> boolean checkCanSet(String name, T oldval, Consumer<Throwable> onError) { - if (oldval != null) { - onError.accept(new IOException( - new IllegalArgumentException(name))); - return false; - } - return true; - } - - @Override - public void close() { - int prevState = state.getAndSet(CLOSED); - if(prevState == BLOCKING) { - // wake up blocked take() - queue.offer(new DataFrame(0, DataFrame.END_STREAM, new ByteBufferReference[0])); - } - } - - private void flushAsync(boolean alreadyInExecutor) { - while(true) { - switch (state.get()) { - case BLOCKING: - case CLOSED: - case REFLUSHING: - return; - case ASYNC: - if(state.compareAndSet(ASYNC, FLUSHING)) { - if(alreadyInExecutor) { - flushLoop(); - } else { - executor.execute(this::flushLoop); - } - return; - } - break; - case FLUSHING: - if(state.compareAndSet(FLUSHING, REFLUSHING)) { - return; - } - break; - } - } - } - - private void flushLoop() { - try { - while(true) { - Http2Frame frame = queue.poll(); - while (frame != null) { - if(!onData.accept(frame)) { - assert state.get() == CLOSED; - return; // closed - } - frame = queue.poll(); - } - switch (state.get()) { - case BLOCKING: - assert false; - break; - case ASYNC: - throw new RuntimeException("Shouldn't happen"); - case FLUSHING: - if(state.compareAndSet(FLUSHING, ASYNC)) { - return; - } - break; - case REFLUSHING: - // We need to check if new elements were put after last - // poll() and do graceful exit - state.compareAndSet(REFLUSHING, FLUSHING); - break; - case CLOSED: - return; - } - } - } catch (Throwable e) { - onError.accept(e); - close(); - } - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java deleted file mode 100644 index cfa69bf61d9..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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. 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. - */ -package jdk.incubator.http.internal.common; - - -import java.io.Closeable; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Deque; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.atomic.AtomicInteger; - -public class AsyncWriteQueue implements Closeable { - - @FunctionalInterface - public static interface AsyncConsumer { - /** - * Takes an array of buffer reference and attempt to send the data - * downstream. If not all the data can be sent, then push back - * to the source queue by calling {@code source.setDelayed(buffers)} - * and return false. If all the data was successfully sent downstream - * then returns true. - * @param buffers An array of ButeBufferReference containing data - * to send downstream. - * @param source This AsyncWriteQueue. - * @return true if all the data could be sent downstream, false otherwise. - */ - boolean trySend(ByteBufferReference[] buffers, AsyncWriteQueue source); - } - - private static final int IDLE = 0; // nobody is flushing from the queue - private static final int FLUSHING = 1; // there is the only thread flushing from the queue - private static final int REFLUSHING = 2; // while one thread was flushing from the queue - // the other thread put data into the queue. - // flushing thread should recheck queue before switching to idle state. - private static final int DELAYED = 3; // flushing is delayed - // either by PlainHttpConnection.WriteEvent registration, or - // SSL handshaking - - private static final int CLOSED = 4; // queue is closed - - private final AtomicInteger state = new AtomicInteger(IDLE); - private final Deque<ByteBufferReference[]> queue = new ConcurrentLinkedDeque<>(); - private final AsyncConsumer consumeAction; - - // Queue may be processed in two modes: - // 1. if(!doFullDrain) - invoke callback on each chunk - // 2. if(doFullDrain) - drain the whole queue, merge all chunks into the single array and invoke callback - private final boolean doFullDrain; - - private ByteBufferReference[] delayedElement = null; - - public AsyncWriteQueue(AsyncConsumer consumeAction) { - this(consumeAction, true); - } - - public AsyncWriteQueue(AsyncConsumer consumeAction, boolean doFullDrain) { - this.consumeAction = consumeAction; - this.doFullDrain = doFullDrain; - } - - public void put(ByteBufferReference[] e) throws IOException { - ensureOpen(); - queue.addLast(e); - } - - public void putFirst(ByteBufferReference[] e) throws IOException { - ensureOpen(); - queue.addFirst(e); - } - - /** - * retruns true if flushing was performed - * @return - * @throws IOException - */ - public boolean flush() throws IOException { - while(true) { - switch (state.get()) { - case IDLE: - if(state.compareAndSet(IDLE, FLUSHING)) { - flushLoop(); - return true; - } - break; - case FLUSHING: - if(state.compareAndSet(FLUSHING, REFLUSHING)) { - return false; - } - break; - case REFLUSHING: - case DELAYED: - return false; - case CLOSED: - throw new IOException("Queue closed"); - } - } - } - - /* - * race invocations of flushDelayed are not allowed. - * flushDelayed should be invoked only from: - * - SelectorManager thread - * - Handshaking thread - */ - public void flushDelayed() throws IOException { - ensureOpen(); - if(!state.compareAndSet(DELAYED, FLUSHING)) { - ensureOpen(); // if CAS failed when close was set - throw proper exception - throw new RuntimeException("Shouldn't happen"); - } - flushLoop(); - } - - private ByteBufferReference[] drain(ByteBufferReference[] prev) { - assert prev != null; - if(doFullDrain) { - ByteBufferReference[] next = queue.poll(); - if(next == null) { - return prev; - } - List<ByteBufferReference> drained = new ArrayList<>(); - drained.addAll(Arrays.asList(prev)); - drained.addAll(Arrays.asList(next)); - while ((next = queue.poll()) != null) { - drained.addAll(Arrays.asList(next)); - } - return drained.toArray(new ByteBufferReference[0]); - } else { - return prev; - } - } - - private ByteBufferReference[] drain() { - ByteBufferReference[] next = queue.poll(); - return next == null ? null : drain(next); - } - - private void flushLoop() throws IOException { - ByteBufferReference[] element; - if (delayedElement != null) { - element = drain(delayedElement); - delayedElement = null; - } else { - element = drain(); - } - while(true) { - while (element != null) { - if (!consumeAction.trySend(element, this)) { - return; - } - element = drain(); - } - switch (state.get()) { - case IDLE: - case DELAYED: - throw new RuntimeException("Shouldn't happen"); - case FLUSHING: - if(state.compareAndSet(FLUSHING, IDLE)) { - return; - } - break; - case REFLUSHING: - // We need to check if new elements were put after last poll() and do graceful exit - state.compareAndSet(REFLUSHING, FLUSHING); - break; - case CLOSED: - throw new IOException("Queue closed"); - } - element = drain(); - } - } - - /* - * The methods returns unprocessed chunk of buffers into beginning of the queue. - * Invocation of the method allowed only inside consume callback, - * and consume callback is invoked only when the queue in FLUSHING or REFLUSHING state. - */ - public void setDelayed(ByteBufferReference[] delayedElement) throws IOException { - while(true) { - int state = this.state.get(); - switch (state) { - case IDLE: - case DELAYED: - throw new RuntimeException("Shouldn't happen"); - case FLUSHING: - case REFLUSHING: - if(this.state.compareAndSet(state, DELAYED)) { - this.delayedElement = delayedElement; - return; - } - break; - case CLOSED: - throw new IOException("Queue closed"); - } - } - - } - - private void ensureOpen() throws IOException { - if (state.get() == CLOSED) { - throw new IOException("Queue closed"); - } - } - - @Override - public void close() throws IOException { - state.getAndSet(CLOSED); - } - -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java index f91e07b0cf2..a8f9e5c4b8a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java index 0d218b15a5c..365dc6d4f9c 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,10 +25,8 @@ package jdk.incubator.http.internal.common; import java.nio.ByteBuffer; -import java.util.List; import java.util.Objects; import java.util.function.Supplier; -import java.util.stream.Collectors; public class ByteBufferReference implements Supplier<ByteBuffer> { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java similarity index 58% rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java index 310261601ea..23f3fb2fab8 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -23,35 +23,25 @@ * questions. */ -package jdk.incubator.http; +package jdk.incubator.http.internal.common; -import java.util.Optional; -import java.util.concurrent.CompletionException; -import java.util.concurrent.Flow; -import java.util.function.Consumer; +import java.io.IOException; /** - * A super class for PushPublisher implementation. + * Signals that an end of file or end of stream has been reached + * unexpectedly before any protocol specific data has been received. */ -abstract class AbstractPushPublisher<T> implements Flow.Publisher<T> { +public final class ConnectionExpiredException extends IOException { + private static final long serialVersionUID = 0; - static enum SubscriptionState { OPENED, DONE, CANCELLED }; - - public abstract void acceptData(Optional<T> item) - throws InterruptedException; - - public abstract void acceptError(Throwable t); - - public Consumer<Optional<T>> asDataConsumer() { - return this::consume; + /** + * Constructs a {@code ConnectionExpiredException} with the specified detail + * message and cause. + * + * @param s the detail message + * @param cause the throwable cause + */ + public ConnectionExpiredException(String s, Throwable cause) { + super(s, cause); } - - void consume(Optional<T> item) { - try { - acceptData(item); - } catch (InterruptedException x) { - throw new CompletionException(x); - } - } - } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java new file mode 100644 index 00000000000..583fc86d9f6 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2015, 2017, 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. 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. + */ +package jdk.incubator.http.internal.common; + +import java.io.PrintStream; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.function.Supplier; +import java.lang.System.Logger; + +/** + * A {@code System.Logger} that forwards all messages to an underlying + * {@code System.Logger}, after adding some decoration. + * The logger also has the ability to additionally send the logged messages + * to System.err or System.out, whether the underlying logger is activated or not. + * In addition instance of {@code DebugLogger} support both + * {@link String#format(String, Object...)} and + * {@link java.text.MessageFormat#format(String, Object...)} formatting. + * String-like formatting is enabled by the presence of "%s" or "%d" in the format + * string. MessageFormat-like formatting is enabled by the presence of "{0" or "{1". + * <p> + * See {@link Utils#getDebugLogger(Supplier, boolean)} and + * {@link Utils#getHpackLogger(Supplier, boolean)}. + */ +class DebugLogger implements Logger { + // deliberately not in the same subtree than standard loggers. + final static String HTTP_NAME = "jdk.internal.httpclient.debug"; + final static String HPACK_NAME = "jdk.internal.httpclient.hpack.debug"; + final static Logger HTTP = System.getLogger(HTTP_NAME); + final static Logger HPACK = System.getLogger(HPACK_NAME); + final static long START_NANOS = System.nanoTime(); + + private final Supplier<String> dbgTag; + private final Level errLevel; + private final Level outLevel; + private final Logger logger; + private final boolean debugOn; + private final boolean traceOn; + + /** + * Create a logger for debug traces.The logger should only be used + * with levels whose severity is {@code <= DEBUG}. + * + * By default, this logger will forward all messages logged to the supplied + * {@code logger}. + * But in addition, if the message severity level is {@code >=} to + * the provided {@code errLevel} it will print the messages on System.err, + * and if the message severity level is {@code >=} to + * the provided {@code outLevel} it will also print the messages on System.out. + * <p> + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @apiNote To obtain a logger that will always print things on stderr in + * addition to forwarding to the internal logger, use + * {@code new DebugLogger(logger, this::dbgTag, Level.OFF, Level.ALL);}. + * To obtain a logger that will only forward to the internal logger, + * use {@code new DebugLogger(logger, this::dbgTag, Level.OFF, Level.OFF);}. + * + * @param logger The internal logger to which messages will be forwarded. + * This should be either {@link #HPACK} or {@link #HTTP}; + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") + * @param outLevel The level above which messages will be also printed on + * System.out (in addition to being forwarded to the internal logger). + * @param errLevel The level above which messages will be also printed on + * System.err (in addition to being forwarded to the internal logger). + * + * @return A logger for HTTP internal debug traces + */ + private DebugLogger(Logger logger, + Supplier<String> dbgTag, + Level outLevel, + Level errLevel) { + this.dbgTag = dbgTag; + this.errLevel = errLevel; + this.outLevel = outLevel; + this.logger = Objects.requireNonNull(logger); + // support only static configuration. + this.debugOn = isEnabled(Level.DEBUG); + this.traceOn = isEnabled(Level.TRACE); + } + + @Override + public String getName() { + return logger.getName(); + } + + private boolean isEnabled(Level level) { + if (level == Level.OFF) return false; + int severity = level.getSeverity(); + return severity >= errLevel.getSeverity() + || severity >= outLevel.getSeverity() + || logger.isLoggable(level); + } + + @Override + public boolean isLoggable(Level level) { + // fast path, we assume these guys never change. + // support only static configuration. + if (level == Level.DEBUG) return debugOn; + if (level == Level.TRACE) return traceOn; + return isEnabled(level); + } + + @Override + public void log(Level level, ResourceBundle unused, + String format, Object... params) { + // fast path, we assume these guys never change. + // support only static configuration. + if (level == Level.DEBUG && !debugOn) return; + if (level == Level.TRACE && !traceOn) return; + + int severity = level.getSeverity(); + if (errLevel != Level.OFF + && errLevel.getSeverity() <= severity) { + print(System.err, level, format, params, null); + } + if (outLevel != Level.OFF + && outLevel.getSeverity() <= severity) { + print(System.out, level, format, params, null); + } + if (logger.isLoggable(level)) { + logger.log(level, unused, + getFormat(new StringBuilder(), format, params).toString(), + params); + } + } + + @Override + public void log(Level level, ResourceBundle unused, String msg, + Throwable thrown) { + // fast path, we assume these guys never change. + if (level == Level.DEBUG && !debugOn) return; + if (level == Level.TRACE && !traceOn) return; + + if (errLevel != Level.OFF + && errLevel.getSeverity() <= level.getSeverity()) { + print(System.err, level, msg, null, thrown); + } + if (outLevel != Level.OFF + && outLevel.getSeverity() <= level.getSeverity()) { + print(System.out, level, msg, null, thrown); + } + if (logger.isLoggable(level)) { + logger.log(level, unused, + getFormat(new StringBuilder(), msg, null).toString(), + thrown); + } + } + + private void print(PrintStream out, Level level, String msg, + Object[] params, Throwable t) { + StringBuilder sb = new StringBuilder(); + sb.append(level.name()).append(':').append(' '); + sb = format(sb, msg, params); + if (t != null) sb.append(' ').append(t.toString()); + out.println(sb.toString()); + if (t != null) { + t.printStackTrace(out); + } + } + + private StringBuilder decorate(StringBuilder sb, String msg) { + String tag = dbgTag == null ? null : dbgTag.get(); + String res = msg == null ? "" : msg; + long elapsed = System.nanoTime() - START_NANOS; + long millis = elapsed / 1000_000; + long secs = millis / 1000; + sb.append('[').append(Thread.currentThread().getName()).append(']') + .append(' ').append('['); + if (secs > 0) { + sb.append(secs).append('s'); + } + millis = millis % 1000; + if (millis > 0) { + if (secs > 0) sb.append(' '); + sb.append(millis).append("ms"); + } + sb.append(']').append(' '); + if (tag != null) { + sb.append(tag).append(' '); + } + sb.append(res); + return sb; + } + + + private StringBuilder getFormat(StringBuilder sb, String format, Object[] params) { + if (format == null || params == null || params.length == 0) { + return decorate(sb, format); + } else if (format.contains("{0}") || format.contains("{1}")) { + return decorate(sb, format); + } else if (format.contains("%s") || format.contains("%d")) { + try { + return decorate(sb, String.format(format, params)); + } catch (Throwable t) { + return decorate(sb, format); + } + } else { + return decorate(sb, format); + } + } + + private StringBuilder format(StringBuilder sb, String format, Object[] params) { + if (format == null || params == null || params.length == 0) { + return decorate(sb, format); + } else if (format.contains("{0}") || format.contains("{1}")) { + return decorate(sb, java.text.MessageFormat.format(format, params)); + } else if (format.contains("%s") || format.contains("%d")) { + try { + return decorate(sb, String.format(format, params)); + } catch (Throwable t) { + return decorate(sb, format); + } + } else { + return decorate(sb, format); + } + } + + public static DebugLogger createHttpLogger(Supplier<String> dbgTag, Level outLevel, Level errLevel) { + return new DebugLogger(HTTP, dbgTag, outLevel, errLevel); + } + + public static DebugLogger createHpackLogger(Supplier<String> dbgTag, Level outLevel, Level errLevel) { + return new DebugLogger(HPACK, dbgTag, outLevel, errLevel); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java new file mode 100644 index 00000000000..5eb498018e0 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Encapsulates operations with demand (Reactive Streams). + * + * <p> Demand is the aggregated number of elements requested by a Subscriber + * which is yet to be delivered (fulfilled) by the Publisher. + */ +public final class Demand { + + private final AtomicLong val = new AtomicLong(); + + /** + * Increases this demand by the specified positive value. + * + * @param n + * increment {@code > 0} + * + * @return {@code true} iff prior to this operation this demand was fulfilled + */ + public boolean increase(long n) { + if (n <= 0) { + throw new IllegalArgumentException(String.valueOf(n)); + } + long prev = val.getAndAccumulate(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i); + return prev == 0; + } + + /** + * Tries to decrease this demand by the specified positive value. + * + * <p> The actual value this demand has been decreased by might be less than + * {@code n}, including {@code 0} (no decrease at all). + * + * @param n + * decrement {@code > 0} + * + * @return a value {@code m} ({@code 0 <= m <= n}) this demand has been + * actually decreased by + */ + public long decreaseAndGet(long n) { + if (n <= 0) { + throw new IllegalArgumentException(String.valueOf(n)); + } + long p, d; + do { + d = val.get(); + p = Math.min(d, n); + } while (!val.compareAndSet(d, d - p)); + return p; + } + + /** + * Tries to decrease this demand by {@code 1}. + * + * @return {@code true} iff this demand has been decreased by {@code 1} + */ + public boolean tryDecrement() { + return decreaseAndGet(1) == 1; + } + + /** + * @return {@code true} iff there is no unfulfilled demand + */ + public boolean isFulfilled() { + return val.get() == 0; + } + + /** + * Resets this object to its initial state. + */ + public void reset() { + val.set(0); + } + + /** + * Returns the current value of this demand. + * + * @return the current value of this demand + */ + public long get() { + return val.get(); + } + + @Override + public String toString() { + return String.valueOf(val.get()); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java new file mode 100644 index 00000000000..7230ac158ce --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.Flow; + +/** + * FlowTube is an I/O abstraction that allows reading from and writing to a + * destination asynchronously. + * This is not a {@link Flow.Processor + * Flow.Processor<List<ByteBuffer>, List<ByteBuffer>>}, + * but rather models a publisher source and a subscriber sink in a bidirectional flow. + * <p> + * The {@code connectFlows} method should be called to connect the bidirectional + * flow. A FlowTube supports handing over the same read subscription to different + * sequential read subscribers over time. When {@code connectFlows(writePublisher, + * readSubscriber} is called, the FlowTube will call {@code dropSubscription} on + * its former readSubscriber, and {@code onSubscribe} on its new readSubscriber. + */ +public interface FlowTube extends + Flow.Publisher<List<ByteBuffer>>, + Flow.Subscriber<List<ByteBuffer>> { + + /** + * A subscriber for reading from the bidirectional flow. + * A TubeSubscriber is a {@code Flow.Subscriber} that can be canceled + * by calling {@code dropSubscription()}. + * Once {@code dropSubscription()} is called, the {@code TubeSubscriber} + * should stop calling any method on its subscription. + */ + static interface TubeSubscriber extends Flow.Subscriber<List<ByteBuffer>> { + + /** + * Called when the flow is connected again, and the subscription + * is handed over to a new subscriber. + * Once {@code dropSubscription()} is called, the {@code TubeSubscriber} + * should stop calling any method on its subscription. + */ + default void dropSubscription() { } + + } + + /** + * A publisher for writing to the bidirectional flow. + */ + static interface TubePublisher extends Flow.Publisher<List<ByteBuffer>> { + + } + + /** + * Connects the bidirectional flows to a write {@code Publisher} and a + * read {@code Subscriber}. This method can be called sequentially + * several times to switch existing publishers and subscribers to a new + * pair of write subscriber and read publisher. + * @param writePublisher A new publisher for writing to the bidirectional flow. + * @param readSubscriber A new subscriber for reading from the bidirectional + * flow. + */ + default void connectFlows(TubePublisher writePublisher, + TubeSubscriber readSubscriber) { + + this.subscribe(readSubscriber); + writePublisher.subscribe(this); + } + + /** + * Returns true if this flow was completed, either exceptionally + * or normally (EOF reached). + * @return true if the flow is finished + */ + boolean isFinished(); + + + /** + * Returns {@code s} if {@code s} is a {@code TubeSubscriber}, otherwise + * wraps it in a {@code TubeSubscriber}. + * Using the wrapper is only appropriate in the case where + * {@code dropSubscription} doesn't need to be implemented, and the + * {@code TubeSubscriber} is subscribed only once. + * + * @param s a subscriber for reading. + * @return a {@code TubeSubscriber}: either {@code s} if {@code s} is a + * {@code TubeSubscriber}, otherwise a {@code TubeSubscriber} + * wrapper that delegates to {@code s} + */ + static TubeSubscriber asTubeSubscriber(Flow.Subscriber<? super List<ByteBuffer>> s) { + if (s instanceof TubeSubscriber) { + return (TubeSubscriber) s; + } + return new AbstractTubeSubscriber.TubeSubscriberWrapper(s); + } + + /** + * Returns {@code s} if {@code s} is a {@code TubePublisher}, otherwise + * wraps it in a {@code TubePublisher}. + * + * @param p a publisher for writing. + * @return a {@code TubePublisher}: either {@code s} if {@code s} is a + * {@code TubePublisher}, otherwise a {@code TubePublisher} + * wrapper that delegates to {@code s} + */ + static TubePublisher asTubePublisher(Flow.Publisher<List<ByteBuffer>> p) { + if (p instanceof TubePublisher) { + return (TubePublisher) p; + } + return new AbstractTubePublisher.TubePublisherWrapper(p); + } + + /** + * Convenience abstract class for {@code TubePublisher} implementations. + * It is not required that a {@code TubePublisher} implementation extends + * this class. + */ + static abstract class AbstractTubePublisher implements TubePublisher { + static final class TubePublisherWrapper extends AbstractTubePublisher { + final Flow.Publisher<List<ByteBuffer>> delegate; + public TubePublisherWrapper(Flow.Publisher<List<ByteBuffer>> delegate) { + this.delegate = delegate; + } + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + delegate.subscribe(subscriber); + } + } + } + + /** + * Convenience abstract class for {@code TubeSubscriber} implementations. + * It is not required that a {@code TubeSubscriber} implementation extends + * this class. + */ + static abstract class AbstractTubeSubscriber implements TubeSubscriber { + static final class TubeSubscriberWrapper extends AbstractTubeSubscriber { + final Flow.Subscriber<? super List<ByteBuffer>> delegate; + TubeSubscriberWrapper(Flow.Subscriber<? super List<ByteBuffer>> delegate) { + this.delegate = delegate; + } + @Override + public void dropSubscription() {} + @Override + public void onSubscribe(Flow.Subscription subscription) { + delegate.onSubscribe(subscription); + } + @Override + public void onNext(List<ByteBuffer> item) { + delegate.onNext(item); + } + @Override + public void onError(Throwable throwable) { + delegate.onError(throwable); + } + @Override + public void onComplete() { + delegate.onComplete(); + } + } + + } + +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java index b3ac7d51912..3c7a52647b6 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -30,15 +30,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.OptionalLong; import java.util.Set; import java.util.TreeMap; /** * Implementation of HttpHeaders. */ -public class HttpHeadersImpl implements HttpHeaders { +public class HttpHeadersImpl extends HttpHeaders { private final TreeMap<String,List<String>> headers; @@ -46,36 +44,20 @@ public class HttpHeadersImpl implements HttpHeaders { headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } - @Override - public Optional<String> firstValue(String name) { - List<String> l = headers.get(name); - return Optional.ofNullable(l == null ? null : l.get(0)); - } - - @Override - public List<String> allValues(String name) { - return headers.get(name); - } - @Override public Map<String, List<String>> map() { return Collections.unmodifiableMap(headers); } - public Map<String, List<String>> directMap() { - return headers; - } - // package private mutators public HttpHeadersImpl deepCopy() { HttpHeadersImpl h1 = new HttpHeadersImpl(); - TreeMap<String,List<String>> headers1 = h1.headers; Set<String> keys = headers.keySet(); for (String key : keys) { List<String> vals = headers.get(key); List<String> vals1 = new ArrayList<>(vals); - headers1.put(key, vals1); + h1.headers.put(key, vals1); } return h1; } @@ -90,19 +72,4 @@ public class HttpHeadersImpl implements HttpHeaders { values.add(value); headers.put(name, values); } - - @Override - public OptionalLong firstValueAsLong(String name) { - List<String> l = headers.get(name); - if (l == null) { - return OptionalLong.empty(); - } else { - String v = l.get(0); - return OptionalLong.of(Long.parseLong(v)); - } - } - - public void clear() { - headers.clear(); - } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java index 9fcf9f023a3..e042993634b 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,8 +25,9 @@ package jdk.incubator.http.internal.common; -import java.io.IOException; import jdk.incubator.http.HttpHeaders; + +import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -36,6 +37,9 @@ import jdk.incubator.http.internal.frame.DataFrame; import jdk.incubator.http.internal.frame.Http2Frame; import jdk.incubator.http.internal.frame.WindowUpdateFrame; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLParameters; + /** * -Djava.net.HttpClient.log= * errors,requests,headers, @@ -196,9 +200,9 @@ public abstract class Log implements System.Logger { } } - public static void logResponse(String s, Object... s1) { + public static void logResponse(Supplier<String> supplier) { if (requests()) { - logger.log(Level.INFO, "RESPONSE: " + s, s1); + logger.log(Level.INFO, "RESPONSE: " + supplier.get()); } } @@ -227,6 +231,55 @@ public abstract class Log implements System.Logger { } } + public static void logParams(SSLParameters p) { + if (!Log.ssl()) { + return; + } + + if (p == null) { + Log.logSSL("SSLParameters: Null params"); + return; + } + + final StringBuilder sb = new StringBuilder("SSLParameters:"); + final List<Object> params = new ArrayList<>(); + if (p.getCipherSuites() != null) { + for (String cipher : p.getCipherSuites()) { + sb.append("\n cipher: {") + .append(params.size()).append("}"); + params.add(cipher); + } + } + + // SSLParameters.getApplicationProtocols() can't return null + // JDK 8 EXCL START + for (String approto : p.getApplicationProtocols()) { + sb.append("\n application protocol: {") + .append(params.size()).append("}"); + params.add(approto); + } + // JDK 8 EXCL END + + if (p.getProtocols() != null) { + for (String protocol : p.getProtocols()) { + sb.append("\n protocol: {") + .append(params.size()).append("}"); + params.add(protocol); + } + } + + if (p.getServerNames() != null) { + for (SNIServerName sname : p.getServerNames()) { + sb.append("\n server name: {") + .append(params.size()).append("}"); + params.add(sname.toString()); + } + } + sb.append('\n'); + + Log.logSSL(sb.toString(), params.toArray()); + } + public static void dumpHeaders(StringBuilder sb, String prefix, HttpHeaders headers) { if (headers != null) { Map<String,List<String>> h = headers.map(); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java index bd338e4b294..b50f24c8554 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -139,7 +139,7 @@ public final class MinimalFuture<T> extends CompletableFuture<T> { } } - public <U> MinimalFuture<U> newIncompleteFuture() { + public static <U> MinimalFuture<U> newMinimalFuture() { return new MinimalFuture<>(); } @@ -157,4 +157,18 @@ public final class MinimalFuture<T> extends CompletableFuture<T> { public String toString() { return super.toString() + " (id=" + id +")"; } + + public static <U> MinimalFuture<U> of(CompletionStage<U> stage) { + MinimalFuture<U> cf = newMinimalFuture(); + stage.whenComplete((r,t) -> complete(cf, r, t)); + return cf; + } + + private static <U> void complete(CompletableFuture<U> cf, U result, Throwable t) { + if (t == null) { + cf.complete(result); + } else { + cf.completeExceptionally(t); + } + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java index 805869eb4b7..c3bae587a7b 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java new file mode 100644 index 00000000000..e4523bfe283 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java @@ -0,0 +1,903 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.io.IOException; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscriber; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; +import javax.net.ssl.SSLException; +import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction; + +/** + * Implements SSL using two SubscriberWrappers. + * + * <p> Constructor takes two Flow.Subscribers: one that receives the network + * data (after it has been encrypted by SSLFlowDelegate) data, and one that + * receives the application data (before it has been encrypted by SSLFlowDelegate). + * + * <p> Methods upstreamReader() and upstreamWriter() return the corresponding + * Flow.Subscribers containing Flows for the encrypted/decrypted upstream data. + * See diagram below. + * + * <p> How Flow.Subscribers are used in this class, and where they come from: + * <pre> + * {@code + * + * + * + * ---------> data flow direction + * + * + * +------------------+ + * upstreamWriter | | downWriter + * ---------------> | | ------------> + * obtained from this | | supplied to constructor + * | SSLFlowDelegate | + * downReader | | upstreamReader + * <--------------- | | <-------------- + * supplied to constructor | | obtained from this + * +------------------+ + * } + * </pre> + */ +public class SSLFlowDelegate { + + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger debug = + Utils.getDebugLogger(this::dbgString, DEBUG); + + final Executor exec; + final Reader reader; + final Writer writer; + final SSLEngine engine; + final String tubeName; // hack + final CompletableFuture<Void> cf; + final CompletableFuture<String> alpnCF; // completes on initial handshake + final static ByteBuffer SENTINEL = Utils.EMPTY_BYTEBUFFER; + volatile boolean close_notify_received; + + /** + * Creates an SSLFlowDelegate fed from two Flow.Subscribers. Each + * Flow.Subscriber requires an associated {@link CompletableFuture} + * for errors that need to be signaled from downstream to upstream. + */ + public SSLFlowDelegate(SSLEngine engine, + Executor exec, + Subscriber<? super List<ByteBuffer>> downReader, + Subscriber<? super List<ByteBuffer>> downWriter) + { + this.tubeName = String.valueOf(downWriter); + this.reader = new Reader(); + this.writer = new Writer(); + this.engine = engine; + this.exec = exec; + this.handshakeState = new AtomicInteger(NOT_HANDSHAKING); + this.cf = CompletableFuture.allOf(reader.completion(), writer.completion()) + .thenRun(this::normalStop); + this.alpnCF = new MinimalFuture<>(); + + // connect the Reader to the downReader and the + // Writer to the downWriter. + connect(downReader, downWriter); + + //Monitor.add(this::monitor); + } + + /** + * Returns true if the SSLFlowDelegate has detected a TLS + * close_notify from the server. + * @return true, if a close_notify was detected. + */ + public boolean closeNotifyReceived() { + return close_notify_received; + } + + /** + * Connects the read sink (downReader) to the SSLFlowDelegate Reader, + * and the write sink (downWriter) to the SSLFlowDelegate Writer. + * Called from within the constructor. Overwritten by SSLTube. + * + * @param downReader The left hand side read sink (typically, the + * HttpConnection read subscriber). + * @param downWriter The right hand side write sink (typically + * the SocketTube write subscriber). + */ + void connect(Subscriber<? super List<ByteBuffer>> downReader, + Subscriber<? super List<ByteBuffer>> downWriter) { + this.reader.subscribe(downReader); + this.writer.subscribe(downWriter); + } + + /** + * Returns a CompletableFuture<String> which completes after + * the initial handshake completes, and which contains the negotiated + * alpn. + */ + public CompletableFuture<String> alpn() { + return alpnCF; + } + + private void setALPN() { + // Handshake is finished. So, can retrieve the ALPN now + if (alpnCF.isDone()) + return; + String alpn = engine.getApplicationProtocol(); + debug.log(Level.DEBUG, "setALPN = %s", alpn); + alpnCF.complete(alpn); + } + + public String monitor() { + StringBuilder sb = new StringBuilder(); + sb.append("SSL: HS state: " + states(handshakeState)); + sb.append(" Engine state: " + engine.getHandshakeStatus().toString()); + sb.append(" LL : "); + synchronized(stateList) { + for (String s: stateList) { + sb.append(s).append(" "); + } + } + sb.append("\r\n"); + sb.append("Reader:: ").append(reader.toString()); + sb.append("\r\n"); + sb.append("Writer:: ").append(writer.toString()); + sb.append("\r\n==================================="); + return sb.toString(); + } + + protected SchedulingAction enterReadScheduling() { + return SchedulingAction.CONTINUE; + } + + + /** + * Processing function for incoming data. Pass it thru SSLEngine.unwrap(). + * Any decrypted buffers returned to be passed downstream. + * Status codes: + * NEED_UNWRAP: do nothing. Following incoming data will contain + * any required handshake data + * NEED_WRAP: call writer.addData() with empty buffer + * NEED_TASK: delegate task to executor + * BUFFER_OVERFLOW: allocate larger output buffer. Repeat unwrap + * BUFFER_UNDERFLOW: keep buffer and wait for more data + * OK: return generated buffers. + * + * Upstream subscription strategy is to try and keep no more than + * TARGET_BUFSIZE bytes in readBuf + */ + class Reader extends SubscriberWrapper { + final SequentialScheduler scheduler; + static final int TARGET_BUFSIZE = 16 * 1024; + volatile ByteBuffer readBuf; + volatile boolean completing = false; + final Object readBufferLock = new Object(); + final System.Logger debugr = + Utils.getDebugLogger(this::dbgString, DEBUG); + + class ReaderDownstreamPusher implements Runnable { + @Override public void run() { processData(); } + } + + Reader() { + super(); + scheduler = SequentialScheduler.synchronizedScheduler( + new ReaderDownstreamPusher()); + this.readBuf = ByteBuffer.allocate(1024); + readBuf.limit(0); // keep in read mode + } + + protected SchedulingAction enterScheduling() { + return enterReadScheduling(); + } + + public final String dbgString() { + return "SSL Reader(" + tubeName + ")"; + } + + /** + * entry point for buffers delivered from upstream Subscriber + */ + @Override + public void incoming(List<ByteBuffer> buffers, boolean complete) { + debugr.log(Level.DEBUG, () -> "Adding " + Utils.remaining(buffers) + + " bytes to read buffer"); + addToReadBuf(buffers, complete); + scheduler.runOrSchedule(); + } + + @Override + public String toString() { + return "READER: " + super.toString() + " readBuf: " + readBuf.toString() + + " count: " + count.toString(); + } + + private void reallocReadBuf() { + int sz = readBuf.capacity(); + ByteBuffer newb = ByteBuffer.allocate(sz*2); + readBuf.flip(); + Utils.copy(readBuf, newb); + readBuf = newb; + } + + @Override + protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) { + if (readBuf.remaining() > TARGET_BUFSIZE) { + return 0; + } else { + return super.upstreamWindowUpdate(currentWindow, downstreamQsize); + } + } + + // readBuf is kept ready for reading outside of this method + private void addToReadBuf(List<ByteBuffer> buffers, boolean complete) { + synchronized (readBufferLock) { + for (ByteBuffer buf : buffers) { + readBuf.compact(); + while (readBuf.remaining() < buf.remaining()) + reallocReadBuf(); + readBuf.put(buf); + readBuf.flip(); + } + if (complete) { + this.completing = complete; + } + } + } + + void schedule() { + scheduler.runOrSchedule(); + } + + void stop() { + debugr.log(Level.DEBUG, "stop"); + scheduler.stop(); + } + + AtomicInteger count = new AtomicInteger(0); + + // work function where it all happens + void processData() { + try { + debugr.log(Level.DEBUG, () -> "processData: " + readBuf.remaining() + + " bytes to unwrap " + + states(handshakeState) + + ", " + engine.getHandshakeStatus()); + int len; + boolean complete = false; + while ((len = readBuf.remaining()) > 0) { + boolean handshaking = false; + try { + EngineResult result; + synchronized (readBufferLock) { + complete = this.completing; + result = unwrapBuffer(readBuf); + debugr.log(Level.DEBUG, "Unwrapped: %s", result.result); + } + if (result.bytesProduced() > 0) { + debugr.log(Level.DEBUG, "sending %d", result.bytesProduced()); + count.addAndGet(result.bytesProduced()); + outgoing(result.destBuffer, false); + } + if (result.status() == Status.BUFFER_UNDERFLOW) { + debugr.log(Level.DEBUG, "BUFFER_UNDERFLOW"); + // not enough data in the read buffer... + requestMore(); + synchronized (readBufferLock) { + // check if we have received some data + if (readBuf.remaining() > len) continue; + return; + } + } + if (complete && result.status() == Status.CLOSED) { + debugr.log(Level.DEBUG, "Closed: completing"); + outgoing(Utils.EMPTY_BB_LIST, true); + return; + } + if (result.handshaking() && !complete) { + debugr.log(Level.DEBUG, "handshaking"); + doHandshake(result, READER); + resumeActivity(); + handshaking = true; + } else { + if ((handshakeState.getAndSet(NOT_HANDSHAKING) & ~DOING_TASKS) == HANDSHAKING) { + setALPN(); + handshaking = false; + resumeActivity(); + } + } + } catch (IOException ex) { + errorCommon(ex); + handleError(ex); + } + if (handshaking && !complete) + return; + } + if (!complete) { + synchronized (readBufferLock) { + complete = this.completing && !readBuf.hasRemaining(); + } + } + if (complete) { + debugr.log(Level.DEBUG, "completing"); + // Complete the alpnCF, if not already complete, regardless of + // whether or not the ALPN is available, there will be no more + // activity. + setALPN(); + outgoing(Utils.EMPTY_BB_LIST, true); + } + } catch (Throwable ex) { + errorCommon(ex); + handleError(ex); + } + } + } + + /** + * Returns a CompletableFuture which completes after all activity + * in the delegate is terminated (whether normally or exceptionally). + * + * @return + */ + public CompletableFuture<Void> completion() { + return cf; + } + + public interface Monitorable { + public String getInfo(); + } + + public static class Monitor extends Thread { + final List<Monitorable> list; + static Monitor themon; + + static { + themon = new Monitor(); + themon.start(); // uncomment to enable Monitor + } + + Monitor() { + super("Monitor"); + setDaemon(true); + list = Collections.synchronizedList(new LinkedList<>()); + } + + void addTarget(Monitorable o) { + list.add(o); + } + + public static void add(Monitorable o) { + themon.addTarget(o); + } + + @Override + public void run() { + System.out.println("Monitor starting"); + while (true) { + try {Thread.sleep(20*1000); } catch (Exception e) {} + synchronized (list) { + for (Monitorable o : list) { + System.out.println(o.getInfo()); + System.out.println("-------------------------"); + } + } + System.out.println("--o-o-o-o-o-o-o-o-o-o-o-o-o-o-"); + + } + } + } + + /** + * Processing function for outgoing data. Pass it thru SSLEngine.wrap() + * Any encrypted buffers generated are passed downstream to be written. + * Status codes: + * NEED_UNWRAP: call reader.addData() with empty buffer + * NEED_WRAP: call addData() with empty buffer + * NEED_TASK: delegate task to executor + * BUFFER_OVERFLOW: allocate larger output buffer. Repeat wrap + * BUFFER_UNDERFLOW: shouldn't happen on writing side + * OK: return generated buffers + */ + class Writer extends SubscriberWrapper { + final SequentialScheduler scheduler; + // queues of buffers received from upstream waiting + // to be processed by the SSLEngine + final List<ByteBuffer> writeList; + final System.Logger debugw = + Utils.getDebugLogger(this::dbgString, DEBUG); + volatile boolean completing; + boolean completed; // only accessed in processData + + class WriterDownstreamPusher extends SequentialScheduler.CompleteRestartableTask { + @Override public void run() { processData(); } + } + + Writer() { + super(); + writeList = Collections.synchronizedList(new LinkedList<>()); + scheduler = new SequentialScheduler(new WriterDownstreamPusher()); + } + + @Override + protected void incoming(List<ByteBuffer> buffers, boolean complete) { + assert complete ? buffers == Utils.EMPTY_BB_LIST : true; + assert buffers != Utils.EMPTY_BB_LIST ? complete == false : true; + if (complete) { + debugw.log(Level.DEBUG, "adding SENTINEL"); + completing = true; + writeList.add(SENTINEL); + } else { + writeList.addAll(buffers); + } + debugw.log(Level.DEBUG, () -> "added " + buffers.size() + + " (" + Utils.remaining(buffers) + + " bytes) to the writeList"); + scheduler.runOrSchedule(); + } + + public final String dbgString() { + return "SSL Writer(" + tubeName + ")"; + } + + protected void onSubscribe() { + doHandshake(EngineResult.INIT, INIT); + resumeActivity(); + } + + void schedule() { + scheduler.runOrSchedule(); + } + + void stop() { + debugw.log(Level.DEBUG, "stop"); + scheduler.stop(); + } + + @Override + public boolean closing() { + return closeNotifyReceived(); + } + + private boolean isCompleting() { + return completing; + } + + @Override + protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) { + if (writeList.size() > 10) + return 0; + else + return super.upstreamWindowUpdate(currentWindow, downstreamQsize); + } + + private boolean hsTriggered() { + synchronized(writeList) { + for (ByteBuffer b : writeList) + if (b == HS_TRIGGER) + return true; + return false; + } + } + + private void processData() { + boolean completing = isCompleting(); + + try { + debugw.log(Level.DEBUG, () -> "processData(" + Utils.remaining(writeList) + ")"); + while (Utils.remaining(writeList) > 0 || hsTriggered() + || needWrap()) { + ByteBuffer[] outbufs = writeList.toArray(Utils.EMPTY_BB_ARRAY); + EngineResult result = wrapBuffers(outbufs); + debugw.log(Level.DEBUG, "wrapBuffer returned %s", result.result); + + if (result.status() == Status.CLOSED) { + if (result.bytesProduced() <= 0) + return; + + if (!completing && !completed) { + completing = this.completing = true; + // There could still be some outgoing data in outbufs. + writeList.add(SENTINEL); + } + } + + boolean handshaking = false; + if (result.handshaking()) { + debugw.log(Level.DEBUG, "handshaking"); + doHandshake(result, WRITER); + handshaking = true; + } else { + if ((handshakeState.getAndSet(NOT_HANDSHAKING) & ~DOING_TASKS) == HANDSHAKING) { + setALPN(); + resumeActivity(); + } + } + cleanList(writeList); // tidy up the source list + sendResultBytes(result); + if (handshaking && !completing) { + if (writeList.isEmpty() && !result.needUnwrap()) { + writer.addData(HS_TRIGGER); + } + if (needWrap()) continue; + return; + } + } + if (completing && Utils.remaining(writeList) == 0) { + /* + System.out.println("WRITER DOO 3"); + engine.closeOutbound(); + EngineResult result = wrapBuffers(Utils.EMPTY_BB_ARRAY); + sendResultBytes(result); + */ + if (!completed) { + completed = true; + writeList.clear(); + outgoing(Utils.EMPTY_BB_LIST, true); + } + return; + } + if (writeList.isEmpty() && needWrap()) { + writer.addData(HS_TRIGGER); + } + } catch (Throwable ex) { + handleError(ex); + } + } + + private boolean needWrap() { + return engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP; + } + + private void sendResultBytes(EngineResult result) { + if (result.bytesProduced() > 0) { + debugw.log(Level.DEBUG, "Sending %d bytes downstream", + result.bytesProduced()); + outgoing(result.destBuffer, false); + } + } + + @Override + public String toString() { + return "WRITER: " + super.toString() + + " writeList size " + Integer.toString(writeList.size()); + //" writeList: " + writeList.toString(); + } + } + + private void handleError(Throwable t) { + debug.log(Level.DEBUG, "handleError", t); + cf.completeExceptionally(t); + // no-op if already completed + alpnCF.completeExceptionally(t); + reader.stop(); + writer.stop(); + } + + private void normalStop() { + reader.stop(); + writer.stop(); + } + + private void cleanList(List<ByteBuffer> l) { + synchronized (l) { + Iterator<ByteBuffer> iter = l.iterator(); + while (iter.hasNext()) { + ByteBuffer b = iter.next(); + if (!b.hasRemaining() && b != SENTINEL) { + iter.remove(); + } + } + } + } + + /** + * States for handshake. We avoid races when accessing/updating the AtomicInt + * because updates always schedule an additional call to both the read() + * and write() functions. + */ + private static final int NOT_HANDSHAKING = 0; + private static final int HANDSHAKING = 1; + private static final int INIT = 2; + private static final int DOING_TASKS = 4; // bit added to above state + private static final ByteBuffer HS_TRIGGER = ByteBuffer.allocate(0); + + private static final int READER = 1; + private static final int WRITER = 2; + + private static String states(AtomicInteger state) { + int s = state.get(); + StringBuilder sb = new StringBuilder(); + int x = s & ~DOING_TASKS; + switch (x) { + case NOT_HANDSHAKING: + sb.append(" NOT_HANDSHAKING "); + break; + case HANDSHAKING: + sb.append(" HANDSHAKING "); + break; + case INIT: + sb.append(" INIT "); + break; + default: + throw new InternalError(); + } + if ((s & DOING_TASKS) > 0) + sb.append("|DOING_TASKS"); + return sb.toString(); + } + + private void resumeActivity() { + reader.schedule(); + writer.schedule(); + } + + final AtomicInteger handshakeState; + final ConcurrentLinkedQueue<String> stateList = new ConcurrentLinkedQueue<>(); + + private void doHandshake(EngineResult r, int caller) { + int s = handshakeState.getAndAccumulate(HANDSHAKING, (current, update) -> update | (current & DOING_TASKS)); + stateList.add(r.handshakeStatus().toString()); + stateList.add(Integer.toString(caller)); + switch (r.handshakeStatus()) { + case NEED_TASK: + if ((s & DOING_TASKS) > 0) // someone else was doing tasks + return; + List<Runnable> tasks = obtainTasks(); + executeTasks(tasks); + break; + case NEED_WRAP: + writer.addData(HS_TRIGGER); + break; + case NEED_UNWRAP: + case NEED_UNWRAP_AGAIN: + // do nothing else + break; + default: + throw new InternalError("Unexpected handshake status:" + + r.handshakeStatus()); + } + } + + private List<Runnable> obtainTasks() { + List<Runnable> l = new ArrayList<>(); + Runnable r; + while ((r = engine.getDelegatedTask()) != null) { + l.add(r); + } + return l; + } + + private void executeTasks(List<Runnable> tasks) { + exec.execute(() -> { + handshakeState.getAndUpdate((current) -> current | DOING_TASKS); + List<Runnable> nextTasks = tasks; + do { + try { + nextTasks.forEach((r) -> { + r.run(); + }); + if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + nextTasks = obtainTasks(); + } else break; + } catch (Throwable t) { + handleError(t); + } + } while(true); + handshakeState.getAndUpdate((current) -> current & ~DOING_TASKS); + writer.addData(HS_TRIGGER); + resumeActivity(); + }); + } + + + EngineResult unwrapBuffer(ByteBuffer src) throws IOException { + ByteBuffer dst = getAppBuffer(); + while (true) { + SSLEngineResult sslResult = engine.unwrap(src, dst); + switch (sslResult.getStatus()) { + case BUFFER_OVERFLOW: + // may happen only if app size buffer was changed. + // get it again if app buffer size changed + int appSize = engine.getSession().getApplicationBufferSize(); + ByteBuffer b = ByteBuffer.allocate(appSize + dst.position()); + dst.flip(); + b.put(dst); + dst = b; + break; + case CLOSED: + return doClosure(new EngineResult(sslResult)); + case BUFFER_UNDERFLOW: + // handled implicitly by compaction/reallocation of readBuf + return new EngineResult(sslResult); + case OK: + dst.flip(); + return new EngineResult(sslResult, dst); + } + } + } + + // FIXME: acknowledge a received CLOSE request from peer + EngineResult doClosure(EngineResult r) throws IOException { + debug.log(Level.DEBUG, + "doClosure(%s): %s [isOutboundDone: %s, isInboundDone: %s]", + r.result, engine.getHandshakeStatus(), + engine.isOutboundDone(), engine.isInboundDone()); + if (engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { + // we have received TLS close_notify and need to send + // an acknowledgement back. We're calling doHandshake + // to finish the close handshake. + if (engine.isInboundDone() && !engine.isOutboundDone()) { + debug.log(Level.DEBUG, "doClosure: close_notify received"); + close_notify_received = true; + doHandshake(r, READER); + } + } + return r; + } + + /** + * Returns the upstream Flow.Subscriber of the reading (incoming) side. + * This flow must be given the encrypted data read from upstream (eg socket) + * before it is decrypted. + */ + public Flow.Subscriber<List<ByteBuffer>> upstreamReader() { + return reader; + } + + /** + * Returns the upstream Flow.Subscriber of the writing (outgoing) side. + * This flow contains the plaintext data before it is encrypted. + */ + public Flow.Subscriber<List<ByteBuffer>> upstreamWriter() { + return writer; + } + + public boolean resumeReader() { + return reader.signalScheduling(); + } + + public void resetReaderDemand() { + reader.resetDownstreamDemand(); + } + + static class EngineResult { + final SSLEngineResult result; + final ByteBuffer destBuffer; + + // normal result + EngineResult(SSLEngineResult result) { + this(result, null); + } + + EngineResult(SSLEngineResult result, ByteBuffer destBuffer) { + this.result = result; + this.destBuffer = destBuffer; + } + + // Special result used to trigger handshaking in constructor + static EngineResult INIT = + new EngineResult( + new SSLEngineResult(SSLEngineResult.Status.OK, HandshakeStatus.NEED_WRAP, 0, 0)); + + boolean handshaking() { + HandshakeStatus s = result.getHandshakeStatus(); + return s != HandshakeStatus.FINISHED + && s != HandshakeStatus.NOT_HANDSHAKING + && result.getStatus() != Status.CLOSED; + } + + boolean needUnwrap() { + HandshakeStatus s = result.getHandshakeStatus(); + return s == HandshakeStatus.NEED_UNWRAP; + } + + + int bytesConsumed() { + return result.bytesConsumed(); + } + + int bytesProduced() { + return result.bytesProduced(); + } + + SSLEngineResult.HandshakeStatus handshakeStatus() { + return result.getHandshakeStatus(); + } + + SSLEngineResult.Status status() { + return result.getStatus(); + } + } + + public ByteBuffer getNetBuffer() { + return ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); + } + + private ByteBuffer getAppBuffer() { + return ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); + } + + final String dbgString() { + return "SSLFlowDelegate(" + tubeName + ")"; + } + + @SuppressWarnings("fallthrough") + EngineResult wrapBuffers(ByteBuffer[] src) throws SSLException { + debug.log(Level.DEBUG, () -> "wrapping " + + Utils.remaining(src) + " bytes"); + ByteBuffer dst = getNetBuffer(); + while (true) { + SSLEngineResult sslResult = engine.wrap(src, dst); + debug.log(Level.DEBUG, () -> "SSLResult: " + sslResult); + switch (sslResult.getStatus()) { + case BUFFER_OVERFLOW: + // Shouldn't happen. We allocated buffer with packet size + // get it again if net buffer size was changed + debug.log(Level.DEBUG, "BUFFER_OVERFLOW"); + int appSize = engine.getSession().getApplicationBufferSize(); + ByteBuffer b = ByteBuffer.allocate(appSize + dst.position()); + dst.flip(); + b.put(dst); + dst = b; + break; // try again + case CLOSED: + debug.log(Level.DEBUG, "CLOSED"); + // fallthrough. There could be some remaining data in dst. + // CLOSED will be handled by the caller. + case OK: + dst.flip(); + final ByteBuffer dest = dst; + debug.log(Level.DEBUG, () -> "OK => produced: " + + dest.remaining() + + " not wrapped: " + + Utils.remaining(src)); + return new EngineResult(sslResult, dest); + case BUFFER_UNDERFLOW: + // Shouldn't happen. Doesn't returns when wrap() + // underflow handled externally + // assert false : "Buffer Underflow"; + debug.log(Level.DEBUG, "BUFFER_UNDERFLOW"); + return new EngineResult(sslResult); + default: + debug.log(Level.DEBUG, "ASSERT"); + assert false; + } + } + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java new file mode 100644 index 00000000000..32a3026dc15 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction; +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED; + +/** + * An implementation of FlowTube that wraps another FlowTube in an + * SSL flow. + * <p> + * The following diagram shows a typical usage of the SSLTube, where + * the SSLTube wraps a SocketTube on the right hand side, and is connected + * to an HttpConnection on the left hand side. + * + * <preformatted>{@code + * +---------- SSLTube -------------------------+ + * | | + * | +---SSLFlowDelegate---+ | + * HttpConnection | | | | SocketTube + * read sink <- SSLSubscriberW. <- Reader <- upstreamR.() <---- read source + * (a subscriber) | | \ / | | (a publisher) + * | | SSLEngine | | + * HttpConnection | | / \ | | SocketTube + * write source -> SSLSubscriptionW. -> upstreamW.() -> Writer ----> write sink + * (a publisher) | | | | (a subscriber) + * | +---------------------+ | + * | | + * +---------------------------------------------+ + * }</preformatted> + */ +public class SSLTube implements FlowTube { + + static final boolean DEBUG = Utils.DEBUG; // revisit: temporary developer's flag. + final System.Logger debug = + Utils.getDebugLogger(this::dbgString, DEBUG); + + private final FlowTube tube; + private final SSLSubscriberWrapper readSubscriber; + private final SSLSubscriptionWrapper writeSubscription; + private final SSLFlowDelegate sslDelegate; + private final SSLEngine engine; + private volatile boolean finished; + + public SSLTube(SSLEngine engine, Executor executor, FlowTube tube) { + Objects.requireNonNull(engine); + Objects.requireNonNull(executor); + this.tube = Objects.requireNonNull(tube); + writeSubscription = new SSLSubscriptionWrapper(); + readSubscriber = new SSLSubscriberWrapper(); + this.engine = engine; + sslDelegate = new SSLTubeFlowDelegate(engine, + executor, + readSubscriber, + tube); + } + + final class SSLTubeFlowDelegate extends SSLFlowDelegate { + SSLTubeFlowDelegate(SSLEngine engine, Executor executor, + SSLSubscriberWrapper readSubscriber, + FlowTube tube) { + super(engine, executor, readSubscriber, tube); + } + protected SchedulingAction enterReadScheduling() { + readSubscriber.processPendingSubscriber(); + return SchedulingAction.CONTINUE; + } + void connect(Flow.Subscriber<? super List<ByteBuffer>> downReader, + Flow.Subscriber<? super List<ByteBuffer>> downWriter) { + assert downWriter == tube; + assert downReader == readSubscriber; + + // Connect the read sink first. That's the left-hand side + // downstream subscriber from the HttpConnection (or more + // accurately, the SSLSubscriberWrapper that will wrap it + // when SSLTube::connectFlows is called. + reader.subscribe(downReader); + + // Connect the right hand side tube (the socket tube). + // + // The SSLFlowDelegate.writer publishes ByteBuffer to + // the SocketTube for writing on the socket, and the + // SSLFlowDelegate::upstreamReader subscribes to the + // SocketTube to receive ByteBuffers read from the socket. + // + // Basically this method is equivalent to: + // // connect the read source: + // // subscribe the SSLFlowDelegate upstream reader + // // to the socket tube publisher. + // tube.subscribe(upstreamReader()); + // // connect the write sink: + // // subscribe the socket tube write subscriber + // // with the SSLFlowDelegate downstream writer. + // writer.subscribe(tube); + tube.connectFlows(FlowTube.asTubePublisher(writer), + FlowTube.asTubeSubscriber(upstreamReader())); + + // Finally connect the write source. That's the left + // hand side publisher which will push ByteBuffer for + // writing and encryption to the SSLFlowDelegate. + // The writeSubscription is in fact the SSLSubscriptionWrapper + // that will wrap the subscription provided by the + // HttpConnection publisher when SSLTube::connectFlows + // is called. + upstreamWriter().onSubscribe(writeSubscription); + } + } + + public CompletableFuture<String> getALPN() { + return sslDelegate.alpn(); + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) { + readSubscriber.dropSubscription(); + readSubscriber.setDelegate(s); + s.onSubscribe(readSubscription); + } + + /** + * Tells whether, or not, this FlowTube has finished receiving data. + * + * @return true when one of this FlowTube Subscriber's OnError or onComplete + * methods have been invoked + */ + @Override + public boolean isFinished() { + return finished; + } + + private volatile Flow.Subscription readSubscription; + + // The DelegateWrapper wraps a subscribed {@code Flow.Subscriber} and + // tracks the subscriber's state. In particular it makes sure that + // onComplete/onError are not called before onSubscribed. + final static class DelegateWrapper implements FlowTube.TubeSubscriber { + private final FlowTube.TubeSubscriber delegate; + private final System.Logger debug; + volatile boolean subscribedCalled; + volatile boolean subscribedDone; + volatile boolean completed; + volatile Throwable error; + DelegateWrapper(Flow.Subscriber<? super List<ByteBuffer>> delegate, + System.Logger debug) { + this.delegate = FlowTube.asTubeSubscriber(delegate); + this.debug = debug; + } + + @Override + public void dropSubscription() { + if (subscribedCalled && !completed) { + delegate.dropSubscription(); + } + } + + @Override + public void onNext(List<ByteBuffer> item) { + assert subscribedCalled; + delegate.onNext(item); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + onSubscribe(delegate::onSubscribe, subscription); + } + + private void onSubscribe(Consumer<Flow.Subscription> method, + Flow.Subscription subscription) { + subscribedCalled = true; + method.accept(subscription); + Throwable x; + boolean finished; + synchronized (this) { + subscribedDone = true; + x = error; + finished = completed; + } + if (x != null) { + debug.log(Level.DEBUG, + "Subscriber completed before subscribe: forwarding %s", + (Object)x); + delegate.onError(x); + } else if (finished) { + debug.log(Level.DEBUG, + "Subscriber completed before subscribe: calling onComplete()"); + delegate.onComplete(); + } + } + + @Override + public void onError(Throwable t) { + if (completed) { + debug.log(Level.DEBUG, + "Subscriber already completed: ignoring %s", + (Object)t); + return; + } + boolean subscribed; + synchronized (this) { + if (completed) return; + error = t; + completed = true; + subscribed = subscribedDone; + } + if (subscribed) { + delegate.onError(t); + } else { + debug.log(Level.DEBUG, + "Subscriber not yet subscribed: stored %s", + (Object)t); + } + } + + @Override + public void onComplete() { + if (completed) return; + boolean subscribed; + synchronized (this) { + if (completed) return; + completed = true; + subscribed = subscribedDone; + } + if (subscribed) { + debug.log(Level.DEBUG, "DelegateWrapper: completing subscriber"); + delegate.onComplete(); + } else { + debug.log(Level.DEBUG, + "Subscriber not yet subscribed: stored completed=true"); + } + } + + @Override + public String toString() { + return "DelegateWrapper:" + delegate.toString(); + } + + } + + // Used to read data from the SSLTube. + final class SSLSubscriberWrapper implements FlowTube.TubeSubscriber { + private AtomicReference<DelegateWrapper> pendingDelegate = + new AtomicReference<>(); + private volatile DelegateWrapper subscribed; + private volatile boolean onCompleteReceived; + private final AtomicReference<Throwable> errorRef + = new AtomicReference<>(); + + // setDelegate can be called asynchronously when the SSLTube flow + // is connected. At this time the permanent subscriber (this class) + // may already be subscribed (readSubscription != null) or not. + // 1. If it's already subscribed (readSubscription != null), we + // are going to signal the SSLFlowDelegate reader, and make sure + // onSubscribed is called within the reader flow + // 2. If it's not yet subscribed (readSubscription == null), then + // we're going to wait for onSubscribe to be called. + // + void setDelegate(Flow.Subscriber<? super List<ByteBuffer>> delegate) { + debug.log(Level.DEBUG, "SSLSubscriberWrapper (reader) got delegate: %s", + delegate); + assert delegate != null; + DelegateWrapper delegateWrapper = new DelegateWrapper(delegate, debug); + DelegateWrapper previous; + Flow.Subscription subscription; + boolean handleNow; + synchronized (this) { + previous = pendingDelegate.getAndSet(delegateWrapper); + subscription = readSubscription; + handleNow = this.errorRef.get() != null || finished; + } + if (previous != null) { + previous.dropSubscription(); + } + if (subscription == null) { + debug.log(Level.DEBUG, "SSLSubscriberWrapper (reader) no subscription yet"); + return; + } + if (handleNow || !sslDelegate.resumeReader()) { + processPendingSubscriber(); + } + } + + // Can be called outside of the flow if an error has already been + // raise. Otherwise, must be called within the SSLFlowDelegate + // downstream reader flow. + // If there is a subscription, and if there is a pending delegate, + // calls dropSubscription() on the previous delegate (if any), + // then subscribe the pending delegate. + void processPendingSubscriber() { + Flow.Subscription subscription; + DelegateWrapper delegateWrapper, previous; + synchronized (this) { + delegateWrapper = pendingDelegate.get(); + if (delegateWrapper == null) return; + subscription = readSubscription; + previous = subscribed; + } + if (subscription == null) { + debug.log(Level.DEBUG, + "SSLSubscriberWrapper (reader) %s", + "processPendingSubscriber: no subscription yet"); + return; + } + delegateWrapper = pendingDelegate.getAndSet(null); + if (delegateWrapper == null) return; + if (previous != null) { + previous.dropSubscription(); + } + onNewSubscription(delegateWrapper, subscription); + } + + @Override + public void dropSubscription() { + DelegateWrapper subscriberImpl = subscribed; + if (subscriberImpl != null) { + subscriberImpl.dropSubscription(); + } + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + debug.log(Level.DEBUG, + "SSLSubscriberWrapper (reader) onSubscribe(%s)", + subscription); + onSubscribeImpl(subscription); + } + + // called in the reader flow, from onSubscribe. + private void onSubscribeImpl(Flow.Subscription subscription) { + assert subscription != null; + DelegateWrapper subscriberImpl, pending; + synchronized (this) { + readSubscription = subscription; + subscriberImpl = subscribed; + pending = pendingDelegate.get(); + } + + if (subscriberImpl == null && pending == null) { + debug.log(Level.DEBUG, + "SSLSubscriberWrapper (reader) onSubscribeImpl: %s", + "no delegate yet"); + return; + } + + if (pending == null) { + // There is no pending delegate, but we have a previously + // subscribed delegate. This is obviously a re-subscribe. + // We are in the downstream reader flow, so we should call + // onSubscribe directly. + debug.log(Level.DEBUG, + "SSLSubscriberWrapper (reader) onSubscribeImpl: %s", + "resubscribing"); + onNewSubscription(subscriberImpl, subscription); + } else { + // We have some pending subscriber: subscribe it now that we have + // a subscription. If we already had a previous delegate then + // it will get a dropSubscription(). + debug.log(Level.DEBUG, + "SSLSubscriberWrapper (reader) onSubscribeImpl: %s", + "subscribing pending"); + processPendingSubscriber(); + } + } + + private void onNewSubscription(DelegateWrapper subscriberImpl, + Flow.Subscription subscription) { + assert subscriberImpl != null; + assert subscription != null; + + Throwable failed; + boolean completed; + // reset any demand that may have been made by the previous + // subscriber + sslDelegate.resetReaderDemand(); + // send the subscription to the subscriber. + subscriberImpl.onSubscribe(subscription); + + // The following twisted logic is just here that we don't invoke + // onError before onSubscribe. It also prevents race conditions + // if onError is invoked concurrently with setDelegate. + synchronized (this) { + failed = this.errorRef.get(); + completed = finished; + subscribed = subscriberImpl; + } + if (failed != null) { + subscriberImpl.onError(failed); + } else if (completed) { + subscriberImpl.onComplete(); + } + } + + @Override + public void onNext(List<ByteBuffer> item) { + subscribed.onNext(item); + } + + public void onErrorImpl(Throwable throwable) { + // The following twisted logic is just here that we don't invoke + // onError before onSubscribe. It also prevents race conditions + // if onError is invoked concurrently with setDelegate. + // See setDelegate. + + errorRef.compareAndSet(null, throwable); + Throwable failed = errorRef.get(); + finished = true; + debug.log(Level.DEBUG, "%s: onErrorImpl: %s", this, throwable); + DelegateWrapper subscriberImpl; + synchronized (this) { + subscriberImpl = subscribed; + } + if (subscriberImpl != null) { + subscriberImpl.onError(failed); + } else { + debug.log(Level.DEBUG, "%s: delegate null, stored %s", this, failed); + } + // now if we have any pending subscriber, we should forward + // the error to them immediately as the read scheduler will + // already be stopped. + processPendingSubscriber(); + } + + @Override + public void onError(Throwable throwable) { + assert !finished && !onCompleteReceived; + onErrorImpl(throwable); + } + + private boolean handshaking() { + HandshakeStatus hs = engine.getHandshakeStatus(); + return !(hs == NOT_HANDSHAKING || hs == FINISHED); + } + + private boolean handshakeFailed() { + // sslDelegate can be null if we reach here + // during the initial handshake, as that happens + // within the SSLFlowDelegate constructor. + // In that case we will want to raise an exception. + return handshaking() + && (sslDelegate == null + || !sslDelegate.closeNotifyReceived()); + } + + @Override + public void onComplete() { + assert !finished && !onCompleteReceived; + onCompleteReceived = true; + DelegateWrapper subscriberImpl; + synchronized(this) { + subscriberImpl = subscribed; + } + + if (handshakeFailed()) { + debug.log(Level.DEBUG, + "handshake: %s, inbound done: %s outbound done: %s", + engine.getHandshakeStatus(), + engine.isInboundDone(), + engine.isOutboundDone()); + onErrorImpl(new SSLHandshakeException( + "Remote host terminated the handshake")); + } else if (subscriberImpl != null) { + finished = true; + subscriberImpl.onComplete(); + } + // now if we have any pending subscriber, we should complete + // them immediately as the read scheduler will already be stopped. + processPendingSubscriber(); + } + } + + @Override + public void connectFlows(TubePublisher writePub, + TubeSubscriber readSub) { + debug.log(Level.DEBUG, "connecting flows"); + readSubscriber.setDelegate(readSub); + writePub.subscribe(this); + } + + /** Outstanding write demand from the SSL Flow Delegate. */ + private final Demand writeDemand = new Demand(); + + final class SSLSubscriptionWrapper implements Flow.Subscription { + + volatile Flow.Subscription delegate; + + void setSubscription(Flow.Subscription sub) { + long demand = writeDemand.get(); // FIXME: isn't it a racy way of passing the demand? + delegate = sub; + debug.log(Level.DEBUG, "setSubscription: demand=%d", demand); + if (demand > 0) + sub.request(demand); + } + + @Override + public void request(long n) { + writeDemand.increase(n); + debug.log(Level.DEBUG, "request: n=%d", n); + Flow.Subscription sub = delegate; + if (sub != null && n > 0) { + sub.request(n); + } + } + + @Override + public void cancel() { + // TODO: no-op or error? + } + } + + /* Subscriber - writing side */ + @Override + public void onSubscribe(Flow.Subscription subscription) { + Objects.requireNonNull(subscription); + Flow.Subscription x = writeSubscription.delegate; + if (x != null) + x.cancel(); + + writeSubscription.setSubscription(subscription); + } + + @Override + public void onNext(List<ByteBuffer> item) { + Objects.requireNonNull(item); + boolean decremented = writeDemand.tryDecrement(); + assert decremented : "Unexpected writeDemand: "; + debug.log(Level.DEBUG, + "sending %d buffers to SSL flow delegate", item.size()); + sslDelegate.upstreamWriter().onNext(item); + } + + @Override + public void onError(Throwable throwable) { + Objects.requireNonNull(throwable); + sslDelegate.upstreamWriter().onError(throwable); + } + + @Override + public void onComplete() { + sslDelegate.upstreamWriter().onComplete(); + } + + @Override + public String toString() { + return dbgString(); + } + + final String dbgString() { + return "SSLTube(" + tube + ")"; + } + +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java new file mode 100644 index 00000000000..d3c409d26be --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2016, 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.util.Objects.requireNonNull; + +/** + * A scheduler of ( repeatable ) tasks that MUST be run sequentially. + * + * <p> This class can be used as a synchronization aid that assists a number of + * parties in running a task in a mutually exclusive fashion. + * + * <p> To run the task, a party invokes {@code runOrSchedule}. To permanently + * prevent the task from subsequent runs, the party invokes {@code stop}. + * + * <p> The parties can, but do not have to, operate in different threads. + * + * <p> The task can be either synchronous ( completes when its {@code run} + * method returns ), or asynchronous ( completed when its + * {@code DeferredCompleter} is explicitly completed ). + * + * <p> The next run of the task will not begin until the previous run has + * finished. + * + * <p> The task may invoke {@code runOrSchedule} itself, which may be a normal + * situation. + */ +public final class SequentialScheduler { + + /* + Since the task is fixed and known beforehand, no blocking synchronization + (locks, queues, etc.) is required. The job can be done solely using + nonblocking primitives. + + The machinery below addresses two problems: + + 1. Running the task in a sequential order (no concurrent runs): + + begin, end, begin, end... + + 2. Avoiding indefinite recursion: + + begin + end + begin + end + ... + + Problem #1 is solved with a finite state machine with 4 states: + + BEGIN, AGAIN, END, and STOP. + + Problem #2 is solved with a "state modifier" OFFLOAD. + + Parties invoke `runOrSchedule()` to signal the task must run. A party + that has invoked `runOrSchedule()` either begins the task or exploits the + party that is either beginning the task or ending it. + + The party that is trying to end the task either ends it or begins it + again. + + To avoid indefinite recursion, before re-running the task the + TryEndDeferredCompleter sets the OFFLOAD bit, signalling to its "child" + TryEndDeferredCompleter that this ("parent") TryEndDeferredCompleter is + available and the "child" must offload the task on to the "parent". Then + a race begins. Whichever invocation of TryEndDeferredCompleter.complete + manages to unset OFFLOAD bit first does not do the work. + + There is at most 1 thread that is beginning the task and at most 2 + threads that are trying to end it: "parent" and "child". In case of a + synchronous task "parent" and "child" are the same thread. + */ + + /** + * An interface to signal the completion of a {@link RestartableTask}. + * + * <p> The invocation of {@code complete} completes the task. The invocation + * of {@code complete} may restart the task, if an attempt has previously + * been made to run the task while it was already running. + * + * @apiNote {@code DeferredCompleter} is useful when a task is not necessary + * complete when its {@code run} method returns, but will complete at a + * later time, and maybe in different thread. This type exists for + * readability purposes at use-sites only. + */ + public static abstract class DeferredCompleter { + + /** Extensible from this (outer) class ONLY. */ + private DeferredCompleter() { } + + /** Completes the task. Must be called once, and once only. */ + public abstract void complete(); + } + + /** + * A restartable task. + */ + @FunctionalInterface + public interface RestartableTask { + + /** + * The body of the task. + * + * @param taskCompleter + * A completer that must be invoked once, and only once, + * when this task is logically finished + */ + void run(DeferredCompleter taskCompleter); + } + + /** + * A complete restartable task is one which is simple and self-contained. + * It completes once its {@code run} method returns. + */ + public static abstract class CompleteRestartableTask + implements RestartableTask + { + @Override + public final void run(DeferredCompleter taskCompleter) { + try { + run(); + } finally { + taskCompleter.complete(); + } + } + + /** The body of the task. */ + protected abstract void run(); + } + + /** + * A RestartableTask that runs its main loop within a + * synchronized block to place a memory barrier around it. + * Because the main loop can't run concurrently in two treads, + * then the lock shouldn't be contended and no deadlock should + * ever be possible. + */ + public static final class SynchronizedRestartableTask + extends CompleteRestartableTask { + private final Runnable mainLoop; + private final Object lock = new Object(); + public SynchronizedRestartableTask(Runnable mainLoop) { + this.mainLoop = mainLoop; + } + + @Override + protected void run() { + synchronized(lock) { + mainLoop.run(); + } + } + } + + private static final int OFFLOAD = 1; + private static final int AGAIN = 2; + private static final int BEGIN = 4; + private static final int STOP = 8; + private static final int END = 16; + + private final AtomicInteger state = new AtomicInteger(END); + private final RestartableTask restartableTask; + private final DeferredCompleter completer; + private final SchedulableTask schedulableTask; + + /** + * A simple task that can be pushed on an executor to execute + * {@code restartableTask.run(completer)}. + */ + private final class SchedulableTask implements Runnable { + @Override + public void run() { + restartableTask.run(completer); + } + } + + public SequentialScheduler(RestartableTask restartableTask) { + this.restartableTask = requireNonNull(restartableTask); + this.completer = new TryEndDeferredCompleter(); + this.schedulableTask = new SchedulableTask(); + } + + /** + * Runs or schedules the task to be run. + * + * @implSpec The recursion which is possible here must be bounded: + * + * <pre>{@code + * this.runOrSchedule() + * completer.complete() + * this.runOrSchedule() + * ... + * }</pre> + * + * @implNote The recursion in this implementation has the maximum + * depth of 1. + */ + public void runOrSchedule() { + runOrSchedule(schedulableTask, null); + } + + /** + * Runs or schedules the task to be run in the provided executor. + * + * <p> This method can be used when potential executing from a calling + * thread is not desirable. + * + * @param executor + * An executor in which to execute the task, if the task needs + * to be executed. + * + * @apiNote The given executor can be {@code null} in which case calling + * {@code deferOrSchedule(null)} is strictly equivalent to calling + * {@code runOrSchedule()}. + */ + public void deferOrSchedule(Executor executor) { // TODO: why this name? why not runOrSchedule? + runOrSchedule(schedulableTask, executor); + } + + private void runOrSchedule(SchedulableTask task, Executor executor) { + while (true) { + int s = state.get(); + if (s == END) { + if (state.compareAndSet(END, BEGIN)) { + break; + } + } else if ((s & BEGIN) != 0) { + // Tries to change the state to AGAIN, preserving OFFLOAD bit + if (state.compareAndSet(s, AGAIN | (s & OFFLOAD))) { + return; + } + } else if ((s & AGAIN) != 0 || s == STOP) { + /* In the case of AGAIN the scheduler does not provide + happens-before relationship between actions prior to + runOrSchedule() and actions that happen in task.run(). + The reason is that no volatile write is done in this case, + and the call piggybacks on the call that has actually set + AGAIN state. */ + return; + } else { + // Non-existent state, or the one that cannot be offloaded + throw new InternalError(String.valueOf(s)); + } + } + if (executor == null) { + task.run(); + } else { + executor.execute(task); + } + } + + /** The only concrete {@code DeferredCompleter} implementation. */ + private class TryEndDeferredCompleter extends DeferredCompleter { + + @Override + public void complete() { + while (true) { + int s; + while (((s = state.get()) & OFFLOAD) != 0) { + // Tries to offload ending of the task to the parent + if (state.compareAndSet(s, s & ~OFFLOAD)) { + return; + } + } + while (true) { + if ((s & OFFLOAD) != 0) { + /* OFFLOAD bit can never be observed here. Otherwise + it would mean there is another invocation of + "complete" that can run the task. */ + throw new InternalError(String.valueOf(s)); + } + if (s == BEGIN) { + if (state.compareAndSet(BEGIN, END)) { + return; + } + } else if (s == AGAIN) { + if (state.compareAndSet(AGAIN, BEGIN | OFFLOAD)) { + break; + } + } else if (s == STOP) { + return; + } else if (s == END) { + throw new IllegalStateException("Duplicate completion"); + } else { + // Non-existent state + throw new InternalError(String.valueOf(s)); + } + s = state.get(); + } + restartableTask.run(completer); + } + } + } + + /** + * Tells whether, or not, this scheduler has been permanently stopped. + * + * <p> Should be used from inside the task to poll the status of the + * scheduler, pretty much the same way as it is done for threads: + * <pre>{@code + * if (!Thread.currentThread().isInterrupted()) { + * ... + * } + * }</pre> + */ + public boolean isStopped() { + return state.get() == STOP; + } + + /** + * Stops this scheduler. Subsequent invocations of {@code runOrSchedule} + * are effectively no-ops. + * + * <p> If the task has already begun, this invocation will not affect it, + * unless the task itself uses {@code isStopped()} method to check the state + * of the handler. + */ + public void stop() { + state.set(STOP); + } + + /** + * Returns a new {@code SequentialScheduler} that executes the provided + * {@code mainLoop} from within a {@link SynchronizedRestartableTask}. + * + * @apiNote + * This is equivalent to calling + * {@code new SequentialScheduler(new SynchronizedRestartableTask(mainloop));} + * The main loop must not do any blocking operation. + * + * @param mainloop The main loop of the new sequential scheduler. + * @return a new {@code SequentialScheduler} that executes the provided + * {@code mainLoop} from within a {@link SynchronizedRestartableTask}. + */ + public static SequentialScheduler synchronizedScheduler(Runnable mainloop) { + return new SequentialScheduler(new SynchronizedRestartableTask(mainloop)); + } + +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java new file mode 100644 index 00000000000..e51cb7380e4 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.io.Closeable; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A wrapper for a Flow.Subscriber. This wrapper delivers data to the wrapped + * Subscriber which is supplied to the constructor. This class takes care of + * downstream flow control automatically and upstream flow control automatically + * by default. + * <p> + * Processing is done by implementing the {@link #incoming(List, boolean)} method + * which supplies buffers from upstream. This method (or any other method) + * can then call the outgoing() method to deliver processed buffers downstream. + * <p> + * Upstream error signals are delivered downstream directly. Cancellation from + * downstream is also propagated upstream immediately. + * <p> + * Each SubscriberWrapper has a {@link java.util.concurrent.CompletableFuture}{@code <Void>} + * which propagates completion/errors from downstream to upstream. Normal completion + * can only occur after onComplete() is called, but errors can be propagated upwards + * at any time. + */ +public abstract class SubscriberWrapper + implements FlowTube.TubeSubscriber, Closeable, Flow.Processor<List<ByteBuffer>,List<ByteBuffer>> + // TODO: SSLTube Subscriber will never change? Does this really need to be a TS? +{ + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + final System.Logger logger = + Utils.getDebugLogger(this::dbgString, DEBUG); + + public enum SchedulingAction { CONTINUE, RETURN, RESCHEDULE } + + volatile Flow.Subscription upstreamSubscription; + final SubscriptionBase downstreamSubscription; + volatile boolean upstreamCompleted; + volatile boolean downstreamCompleted; + volatile boolean completionAcknowledged; + private volatile Subscriber<? super List<ByteBuffer>> downstreamSubscriber; + // processed byte to send to the downstream subscriber. + private final ConcurrentLinkedQueue<List<ByteBuffer>> outputQ; + private final CompletableFuture<Void> cf; + private final SequentialScheduler pushScheduler; + private final AtomicReference<Throwable> errorRef = new AtomicReference<>(); + final AtomicLong upstreamWindow = new AtomicLong(0); + + /** + * Wraps the given downstream subscriber. For each call to {@link + * #onNext(List<ByteBuffer>) } the given filter function is invoked + * and the list (if not empty) returned is passed downstream. + * + * A {@code CompletableFuture} is supplied which can be used to signal an + * error from downstream and which terminates the wrapper or which signals + * completion of downstream activity which can be propagated upstream. Error + * completion can be signaled at any time, but normal completion must not be + * signaled before onComplete() is called. + */ + public SubscriberWrapper() + { + this.outputQ = new ConcurrentLinkedQueue<>(); + this.cf = new MinimalFuture<>(); + this.pushScheduler = + SequentialScheduler.synchronizedScheduler(new DownstreamPusher()); + this.downstreamSubscription = new SubscriptionBase(pushScheduler, + this::downstreamCompletion); + } + + @Override + public final void subscribe(Subscriber<? super List<ByteBuffer>> downstreamSubscriber) { + Objects.requireNonNull(downstreamSubscriber); + this.downstreamSubscriber = downstreamSubscriber; + } + + /** + * Wraps the given downstream wrapper in this. For each call to + * {@link #onNext(List<ByteBuffer>) } the incoming() method is called. + * + * The {@code downstreamCF} from the downstream wrapper is linked to this + * wrappers notifier. + * + * @param downstreamWrapper downstream destination + */ + public SubscriberWrapper(Subscriber<? super List<ByteBuffer>> downstreamWrapper) + { + this(); + subscribe(downstreamWrapper); + } + + /** + * Delivers data to be processed by this wrapper. Generated data to be sent + * downstream, must be provided to the {@link #outgoing(List, boolean)}} + * method. + * + * @param buffers a List of ByteBuffers. + * @param complete if true then no more data will be added to the list + */ + protected abstract void incoming(List<ByteBuffer> buffers, boolean complete); + + /** + * This method is called to determine the window size to use at any time. The + * current window is supplied together with the current downstream queue size. + * {@code 0} should be returned if no change is + * required or a positive integer which will be added to the current window. + * The default implementation maintains a downstream queue size of no greater + * than 5. The method can be overridden if required. + * + * @param currentWindow the current upstream subscription window + * @param downstreamQsize the current number of buffers waiting to be sent + * downstream + * + * @return value to add to currentWindow + */ + protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) { + if (downstreamQsize > 5) { + return 0; + } + + if (currentWindow == 0) { + return 1; + } else { + return 0; + } + } + + /** + * Override this if anything needs to be done after the upstream subscriber + * has subscribed + */ + protected void onSubscribe() { + } + + /** + * Override this if anything needs to be done before checking for error + * and processing the input queue. + * @return + */ + protected SchedulingAction enterScheduling() { + return SchedulingAction.CONTINUE; + } + + protected boolean signalScheduling() { + if (downstreamCompleted || pushScheduler.isStopped()) { + return false; + } + pushScheduler.runOrSchedule(); + return true; + } + + /** + * Delivers buffers of data downstream. After incoming() + * has been called complete == true signifying completion of the upstream + * subscription, data may continue to be delivered, up to when outgoing() is + * called complete == true, after which, the downstream subscription is + * completed. + * + * It's an error to call outgoing() with complete = true if incoming() has + * not previously been called with it. + */ + public void outgoing(ByteBuffer buffer, boolean complete) { + Objects.requireNonNull(buffer); + assert !complete || !buffer.hasRemaining(); + outgoing(List.of(buffer), complete); + } + + /** + * Sometime it might be necessary to complete the downstream subscriber + * before the upstream completes. For instance, when an SSL server + * sends a notify_close. In that case we should let the outgoing + * complete before upstream us completed. + * @return true, may be overridden by subclasses. + */ + public boolean closing() { + return false; + } + + public void outgoing(List<ByteBuffer> buffers, boolean complete) { + Objects.requireNonNull(buffers); + if (complete) { + assert Utils.remaining(buffers) == 0; + boolean closing = closing(); + logger.log(Level.DEBUG, + "completionAcknowledged upstreamCompleted:%s, downstreamCompleted:%s, closing:%s", + upstreamCompleted, downstreamCompleted, closing); + if (!upstreamCompleted && !closing) + throw new IllegalStateException("upstream not completed"); + completionAcknowledged = true; + } else { + logger.log(Level.DEBUG, () -> "Adding " + + Utils.remaining(buffers) + + " to outputQ queue"); + outputQ.add(buffers); + } + logger.log(Level.DEBUG, () -> "pushScheduler " + + (pushScheduler.isStopped() ? " is stopped!" : " is alive")); + pushScheduler.runOrSchedule(); + } + + /** + * Returns a CompletableFuture which completes when this wrapper completes. + * Normal completion happens with the following steps (in order): + * 1. onComplete() is called + * 2. incoming() called with complete = true + * 3. outgoing() may continue to be called normally + * 4. outgoing called with complete = true + * 5. downstream subscriber is called onComplete() + * + * If the subscription is canceled or onComplete() is invoked the + * CompletableFuture completes exceptionally. Exceptional completion + * also occurs if downstreamCF completes exceptionally. + */ + public CompletableFuture<Void> completion() { + return cf; + } + + /** + * Invoked whenever it 'may' be possible to push buffers downstream. + */ + class DownstreamPusher implements Runnable { + @Override + public void run() { + try { + run1(); + } catch (Throwable t) { + errorCommon(t); + } + } + + private void run1() { + if (downstreamCompleted) { + logger.log(Level.DEBUG, "DownstreamPusher: downstream is already completed"); + return; + } + switch (enterScheduling()) { + case CONTINUE: break; + case RESCHEDULE: pushScheduler.runOrSchedule(); return; + case RETURN: return; + default: + errorRef.compareAndSet(null, + new InternalError("unknown scheduling command")); + break; + } + // If there was an error, send it downstream. + Throwable error = errorRef.get(); + if (error != null) { + synchronized(this) { + if (downstreamCompleted) return; + downstreamCompleted = true; + } + logger.log(Level.DEBUG, + () -> "DownstreamPusher: forwarding error downstream: " + error); + pushScheduler.stop(); + outputQ.clear(); + downstreamSubscriber.onError(error); + return; + } + + // OK - no error, let's proceed + if (!outputQ.isEmpty()) { + logger.log(Level.DEBUG, + "DownstreamPusher: queue not empty, downstreamSubscription: %s", + downstreamSubscription); + } else { + logger.log(Level.DEBUG, + "DownstreamPusher: queue empty, downstreamSubscription: %s", + downstreamSubscription); + } + + final boolean dbgOn = logger.isLoggable(Level.DEBUG); + while (!outputQ.isEmpty() && downstreamSubscription.tryDecrement()) { + List<ByteBuffer> b = outputQ.poll(); + if (dbgOn) logger.log(Level.DEBUG, + "DownstreamPusher: Pushing " + + Utils.remaining(b) + + " bytes downstream"); + downstreamSubscriber.onNext(b); + } + upstreamWindowUpdate(); + checkCompletion(); + } + } + + void upstreamWindowUpdate() { + long downstreamQueueSize = outputQ.size(); + long n = upstreamWindowUpdate(upstreamWindow.get(), downstreamQueueSize); + if (n > 0) + upstreamRequest(n); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + if (upstreamSubscription != null) { + throw new IllegalStateException("Single shot publisher"); + } + this.upstreamSubscription = subscription; + upstreamRequest(upstreamWindowUpdate(0, 0)); + logger.log(Level.DEBUG, + "calling downstreamSubscriber::onSubscribe on %s", + downstreamSubscriber); + downstreamSubscriber.onSubscribe(downstreamSubscription); + onSubscribe(); + } + + @Override + public void onNext(List<ByteBuffer> item) { + logger.log(Level.DEBUG, "onNext"); + long prev = upstreamWindow.getAndDecrement(); + if (prev <= 0) + throw new IllegalStateException("invalid onNext call"); + incomingCaller(item, false); + upstreamWindowUpdate(); + } + + private void upstreamRequest(long n) { + logger.log(Level.DEBUG, "requesting %d", n); + upstreamWindow.getAndAdd(n); + upstreamSubscription.request(n); + } + + protected void requestMore() { + if (upstreamWindow.get() == 0) { + upstreamRequest(1); + } + } + + public long upstreamWindow() { + return upstreamWindow.get(); + } + + @Override + public void onError(Throwable throwable) { + logger.log(Level.DEBUG, () -> "onError: " + throwable); + errorCommon(Objects.requireNonNull(throwable)); + } + + protected boolean errorCommon(Throwable throwable) { + assert throwable != null; + if (errorRef.compareAndSet(null, throwable)) { + logger.log(Level.DEBUG, "error", throwable); + pushScheduler.runOrSchedule(); + upstreamCompleted = true; + cf.completeExceptionally(throwable); + return true; + } + return false; + } + + @Override + public void close() { + errorCommon(new RuntimeException("wrapper closed")); + } + + private void incomingCaller(List<ByteBuffer> l, boolean complete) { + try { + incoming(l, complete); + } catch(Throwable t) { + errorCommon(t); + } + } + + @Override + public void onComplete() { + logger.log(Level.DEBUG, () -> "upstream completed: " + toString()); + upstreamCompleted = true; + incomingCaller(Utils.EMPTY_BB_LIST, true); + // pushScheduler will call checkCompletion() + pushScheduler.runOrSchedule(); + } + + /** Adds the given data to the input queue. */ + public void addData(ByteBuffer l) { + if (upstreamSubscription == null) { + throw new IllegalStateException("can't add data before upstream subscriber subscribes"); + } + incomingCaller(List.of(l), false); + } + + void checkCompletion() { + if (downstreamCompleted || !upstreamCompleted) { + return; + } + if (!outputQ.isEmpty()) { + return; + } + if (errorRef.get() != null) { + pushScheduler.runOrSchedule(); + return; + } + if (completionAcknowledged) { + logger.log(Level.DEBUG, "calling downstreamSubscriber.onComplete()"); + downstreamSubscriber.onComplete(); + // Fix me subscriber.onComplete.run(); + downstreamCompleted = true; + cf.complete(null); + } + } + + // called from the downstream Subscription.cancel() + void downstreamCompletion() { + upstreamSubscription.cancel(); + cf.complete(null); + } + + public void resetDownstreamDemand() { + downstreamSubscription.demand.reset(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SubscriberWrapper:") + .append(" upstreamCompleted: ").append(Boolean.toString(upstreamCompleted)) + .append(" upstreamWindow: ").append(upstreamWindow.toString()) + .append(" downstreamCompleted: ").append(Boolean.toString(downstreamCompleted)) + .append(" completionAcknowledged: ").append(Boolean.toString(completionAcknowledged)) + .append(" outputQ size: ").append(Integer.toString(outputQ.size())) + //.append(" outputQ: ").append(outputQ.toString()) + .append(" cf: ").append(cf.toString()) + .append(" downstreamSubscription: ").append(downstreamSubscription.toString()); + + return sb.toString(); + } + + public String dbgString() { + return "SubscriberWrapper"; + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java new file mode 100644 index 00000000000..62b6f6d9bc7 --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.common; + +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Maintains subscription counter and provides primitives for + * - accessing window + * - reducing window when delivering items externally + * - resume delivery when window was zero previously + * + * @author mimcmah + */ +public class SubscriptionBase implements Flow.Subscription { + + final Demand demand = new Demand(); + + final SequentialScheduler scheduler; // when window was zero and is opened, run this + final Runnable cancelAction; // when subscription cancelled, run this + final AtomicBoolean cancelled; + + public SubscriptionBase(SequentialScheduler scheduler, Runnable cancelAction) { + this.scheduler = scheduler; + this.cancelAction = cancelAction; + this.cancelled = new AtomicBoolean(false); + } + + @Override + public void request(long n) { + if (demand.increase(n)) + scheduler.runOrSchedule(); + } + + + + @Override + public synchronized String toString() { + return "SubscriptionBase: window = " + demand.get() + + " cancelled = " + cancelled.toString(); + } + + /** + * Returns true if the window was reduced by 1. In that case + * items must be supplied to subscribers and the scheduler run + * externally. If the window could not be reduced by 1, then false + * is returned and the scheduler will run later when the window is updated. + */ + public boolean tryDecrement() { + return demand.tryDecrement(); + } + + public long window() { + return demand.get(); + } + + @Override + public void cancel() { + if (cancelled.getAndSet(true)) + return; + scheduler.stop(); + cancelAction.run(); + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java index 516c683a89f..ec0c4a65e14 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,7 +25,7 @@ package jdk.incubator.http.internal.common; -import jdk.internal.misc.InnocuousThread; +import jdk.incubator.http.HttpHeaders; import sun.net.NetProperties; import sun.net.util.IPAddressUtil; @@ -33,11 +33,12 @@ import javax.net.ssl.SSLParameters; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; -import java.io.UncheckedIOException; import java.io.PrintStream; +import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.net.InetSocketAddress; -import java.net.NetPermission; import java.net.URI; import java.net.URLPermission; import java.nio.ByteBuffer; @@ -48,29 +49,45 @@ import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.function.Predicate; -import jdk.incubator.http.HttpHeaders; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; /** * Miscellaneous utilities */ public final class Utils { + public static final boolean ASSERTIONSENABLED; + static { + boolean enabled = false; + assert enabled = true; + ASSERTIONSENABLED = enabled; + } +// public static final boolean TESTING; +// static { +// if (ASSERTIONSENABLED) { +// PrivilegedAction<String> action = () -> System.getProperty("test.src"); +// TESTING = AccessController.doPrivileged(action) != null; +// } else TESTING = false; +// } + public static final boolean DEBUG = // Revisit: temporary dev flag. + getBooleanProperty(DebugLogger.HTTP_NAME, false); + public static final boolean DEBUG_HPACK = // Revisit: temporary dev flag. + getBooleanProperty(DebugLogger.HPACK_NAME, false); + public static final boolean TESTING = DEBUG; + /** * Allocated buffer size. Must never be higher than 16K. But can be lower * if smaller allocation units preferred. HTTP/2 mandates that all * implementations support frame payloads of at least 16K. */ - public static final int DEFAULT_BUFSIZE = 16 * 1024; + private static final int DEFAULT_BUFSIZE = 16 * 1024; public static final int BUFSIZE = getIntegerNetProperty( "jdk.httpclient.bufsize", DEFAULT_BUFSIZE @@ -84,13 +101,17 @@ public final class Utils { public static final Predicate<String> ALLOWED_HEADERS = header -> !Utils.DISALLOWED_HEADERS_SET.contains(header); - public static final Predicate<String> - ALL_HEADERS = header -> true; - public static ByteBuffer getBuffer() { return ByteBuffer.allocate(BUFSIZE); } + public static Throwable getCompletionCause(Throwable x) { + if (!(x instanceof CompletionException) + && !(x instanceof ExecutionException)) return x; + final Throwable cause = x.getCause(); + return cause == null ? x : cause; + } + public static IOException getIOException(Throwable t) { if (t instanceof IOException) { return (IOException) t; @@ -102,39 +123,44 @@ public final class Utils { return new IOException(t); } - /** - * We use the same buffer for reading all headers and dummy bodies in an Exchange. - */ - public static ByteBuffer getExchangeBuffer() { - ByteBuffer buf = getBuffer(); - // Force a read the first time it is used - buf.limit(0); - return buf; - } - - /** - * Puts position to limit and limit to capacity so we can resume reading - * into this buffer, but if required > 0 then limit may be reduced so that - * no more than required bytes are read next time. - */ - static void resumeChannelRead(ByteBuffer buf, int required) { - int limit = buf.limit(); - buf.position(limit); - int capacity = buf.capacity() - limit; - if (required > 0 && required < capacity) { - buf.limit(limit + required); - } else { - buf.limit(buf.capacity()); - } - } - private Utils() { } - public static ExecutorService innocuousThreadPool() { - return Executors.newCachedThreadPool( - (r) -> InnocuousThread.newThread("DefaultHttpClient", r)); + /** + * Returns the security permissions required to connect to the proxy, or + * {@code null} if none is required or applicable. + */ + public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) { + if (proxyAddress == null) + return null; + + StringBuilder sb = new StringBuilder(); + sb.append("socket://") + .append(proxyAddress.getHostString()).append(":") + .append(proxyAddress.getPort()); + String urlString = sb.toString(); + return new URLPermission(urlString, "CONNECT"); } + /** + * Returns the security permission required for the given details. + */ + public static URLPermission permissionForServer(URI uri, + String method, + Stream<String> headers) { + String urlString = new StringBuilder() + .append(uri.getScheme()).append("://") + .append(uri.getAuthority()) + .append(uri.getPath()).toString(); + + StringBuilder actionStringBuilder = new StringBuilder(method); + String collected = headers.collect(joining(",")); + if (!collected.isEmpty()) { + actionStringBuilder.append(":").append(collected); + } + return new URLPermission(urlString, actionStringBuilder.toString()); + } + + // ABNF primitives defined in RFC 7230 private static final boolean[] tchar = new boolean[256]; private static final boolean[] fieldvchar = new boolean[256]; @@ -217,55 +243,6 @@ public final class Utils { return accepted; } - /** - * Returns the security permission required for the given details. - * If method is CONNECT, then uri must be of form "scheme://host:port" - */ - public static URLPermission getPermission(URI uri, - String method, - Map<String, List<String>> headers) { - StringBuilder sb = new StringBuilder(); - - String urlstring, actionstring; - - if (method.equals("CONNECT")) { - urlstring = uri.toString(); - actionstring = "CONNECT"; - } else { - sb.append(uri.getScheme()) - .append("://") - .append(uri.getAuthority()) - .append(uri.getPath()); - urlstring = sb.toString(); - - sb = new StringBuilder(); - sb.append(method); - if (headers != null && !headers.isEmpty()) { - sb.append(':'); - Set<String> keys = headers.keySet(); - boolean first = true; - for (String key : keys) { - if (!first) { - sb.append(','); - } - sb.append(key); - first = false; - } - } - actionstring = sb.toString(); - } - return new URLPermission(urlstring, actionstring); - } - - public static void checkNetPermission(String target) { - SecurityManager sm = System.getSecurityManager(); - if (sm == null) { - return; - } - NetPermission np = new NetPermission(target); - sm.checkPermission(np); - } - public static int getIntegerNetProperty(String name, int defaultValue) { return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> NetProperties.getInteger(name, defaultValue)); @@ -276,6 +253,11 @@ public final class Utils { NetProperties.get(name)); } + static boolean getBooleanProperty(String name, boolean def) { + return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> + Boolean.parseBoolean(System.getProperty(name, String.valueOf(def)))); + } + public static SSLParameters copySSLParameters(SSLParameters p) { SSLParameters p1 = new SSLParameters(); p1.setAlgorithmConstraints(p.getAlgorithmConstraints()); @@ -313,7 +295,7 @@ public final class Utils { t.printStackTrace(p); s = bos.toString("US-ASCII"); } catch (UnsupportedEncodingException ex) { - // can't happen + throw new InternalError(ex); // Can't happen } return s; } @@ -337,28 +319,47 @@ public final class Utils { return srcLen - src.remaining(); } - // copy up to amount from src to dst, but no more - public static int copyUpTo(ByteBuffer src, ByteBuffer dst, int amount) { - int toCopy = Math.min(src.remaining(), Math.min(dst.remaining(), amount)); - copy(src, dst, toCopy); - return toCopy; - } + /** Threshold beyond which data is no longer copied into the current + * buffer, if that buffer has enough unused space. */ + private static final int COPY_THRESHOLD = 8192; /** - * Copy amount bytes from src to dst. at least amount must be - * available in both dst and in src + * Adds the data from buffersToAdd to currentList. Either 1) appends the + * data from a particular buffer to the last buffer in the list ( if + * there is enough unused space ), or 2) adds it to the list. + * + * @return the number of bytes added */ - public static void copy(ByteBuffer src, ByteBuffer dst, int amount) { - int excess = src.remaining() - amount; - assert excess >= 0; - if (excess > 0) { - int srclimit = src.limit(); - src.limit(srclimit - excess); - dst.put(src); - src.limit(srclimit); - } else { - dst.put(src); + public static long accumulateBuffers(List<ByteBuffer> currentList, + List<ByteBuffer> buffersToAdd) { + long accumulatedBytes = 0; + for (ByteBuffer bufferToAdd : buffersToAdd) { + int remaining = bufferToAdd.remaining(); + if (remaining <= 0) + continue; + int listSize = currentList.size(); + if (listSize == 0) { + currentList.add(bufferToAdd); + accumulatedBytes = remaining; + continue; + } + + ByteBuffer lastBuffer = currentList.get(listSize - 1); + int freeSpace = lastBuffer.capacity() - lastBuffer.limit(); + if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) { + // append the new data to the unused space in the last buffer + int position = lastBuffer.position(); + int limit = lastBuffer.limit(); + lastBuffer.position(limit); + lastBuffer.limit(limit + remaining); + lastBuffer.put(bufferToAdd); + lastBuffer.position(position); + } else { + currentList.add(bufferToAdd); + } + accumulatedBytes += remaining; } + return accumulatedBytes; } public static ByteBuffer copy(ByteBuffer src) { @@ -378,34 +379,75 @@ public final class Utils { return Arrays.toString(source.toArray()); } - public static int remaining(ByteBuffer[] bufs) { - int remain = 0; + public static long remaining(ByteBuffer[] bufs) { + long remain = 0; for (ByteBuffer buf : bufs) { remain += buf.remaining(); } return remain; } - public static int remaining(List<ByteBuffer> bufs) { - int remain = 0; - for (ByteBuffer buf : bufs) { - remain += buf.remaining(); + public static boolean hasRemaining(List<ByteBuffer> bufs) { + synchronized (bufs) { + for (ByteBuffer buf : bufs) { + if (buf.hasRemaining()) + return true; + } + } + return false; + } + + public static long remaining(List<ByteBuffer> bufs) { + long remain = 0; + synchronized (bufs) { + for (ByteBuffer buf : bufs) { + remain += buf.remaining(); + } } return remain; } - public static int remaining(ByteBufferReference[] refs) { - int remain = 0; + public static int remaining(List<ByteBuffer> bufs, int max) { + long remain = 0; + synchronized (bufs) { + for (ByteBuffer buf : bufs) { + remain += buf.remaining(); + if (remain > max) { + throw new IllegalArgumentException("too many bytes"); + } + } + } + return (int) remain; + } + + public static long remaining(ByteBufferReference[] refs) { + long remain = 0; for (ByteBufferReference ref : refs) { remain += ref.get().remaining(); } return remain; } - // assumes buffer was written into starting at position zero - static void unflip(ByteBuffer buf) { - buf.position(buf.limit()); - buf.limit(buf.capacity()); + public static int remaining(ByteBufferReference[] refs, int max) { + long remain = 0; + for (ByteBufferReference ref : refs) { + remain += ref.get().remaining(); + if (remain > max) { + throw new IllegalArgumentException("too many bytes"); + } + } + return (int) remain; + } + + public static int remaining(ByteBuffer[] refs, int max) { + long remain = 0; + for (ByteBuffer b : refs) { + remain += b.remaining(); + if (remain > max) { + throw new IllegalArgumentException("too many bytes"); + } + } + return (int) remain; } public static void close(Closeable... closeables) { @@ -416,80 +458,11 @@ public final class Utils { } } - public static void close(Throwable t, Closeable... closeables) { - for (Closeable c : closeables) { - try { - ExceptionallyCloseable.close(t, c); - } catch (IOException ignored) { } - } - } - - /** - * Returns an array with the same buffers, but starting at position zero - * in the array. - */ - public static ByteBuffer[] reduce(ByteBuffer[] bufs, int start, int number) { - if (start == 0 && number == bufs.length) { - return bufs; - } - ByteBuffer[] nbufs = new ByteBuffer[number]; - int j = 0; - for (int i=start; i<start+number; i++) { - nbufs[j++] = bufs[i]; - } - return nbufs; - } - - static String asString(ByteBuffer buf) { - byte[] b = new byte[buf.remaining()]; - buf.get(b); - return new String(b, StandardCharsets.US_ASCII); - } - - /** - * Returns a single threaded executor which uses one invocation - * of the parent executor to execute tasks (in sequence). - * - * Use a null valued Runnable to terminate. - */ - // TODO: this is a blocking way of doing this; - public static Executor singleThreadExecutor(Executor parent) { - BlockingQueue<Optional<Runnable>> queue = new LinkedBlockingQueue<>(); - parent.execute(() -> { - while (true) { - try { - Optional<Runnable> o = queue.take(); - if (!o.isPresent()) { - return; - } - o.get().run(); - } catch (InterruptedException ex) { - return; - } - } - }); - return new Executor() { - @Override - public void execute(Runnable command) { - queue.offer(Optional.ofNullable(command)); - } - }; - } - - private static void executeInline(Runnable r) { - r.run(); - } - - static Executor callingThreadExecutor() { - return Utils::executeInline; - } - // Put all these static 'empty' singletons here - @SuppressWarnings("rawtypes") - public static final CompletableFuture[] EMPTY_CFARRAY = new CompletableFuture[0]; - public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0); public static final ByteBuffer[] EMPTY_BB_ARRAY = new ByteBuffer[0]; + public static final List<ByteBuffer> EMPTY_BB_LIST = List.of(); + public static final ByteBufferReference[] EMPTY_BBR_ARRAY = new ByteBufferReference[0]; public static ByteBuffer slice(ByteBuffer buffer, int amount) { ByteBuffer newb = buffer.slice(); @@ -515,4 +488,155 @@ public final class Utils { public static UncheckedIOException unchecked(IOException e) { return new UncheckedIOException(e); } + + /** + * Get a logger for debug HTTP traces. + * + * The logger should only be used with levels whose severity is + * {@code <= DEBUG}. By default, this logger will forward all messages + * logged to an internal logger named "jdk.internal.httpclient.debug". + * In addition, if the property -Djdk.internal.httpclient.debug=true is set, + * it will print the messages on stderr. + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") + * + * @return A logger for HTTP internal debug traces + */ + public static Logger getDebugLogger(Supplier<String> dbgTag) { + return getDebugLogger(dbgTag, DEBUG); + } + + /** + * Get a logger for debug HTTP traces.The logger should only be used + * with levels whose severity is {@code <= DEBUG}. + * + * By default, this logger will forward all messages logged to an internal + * logger named "jdk.internal.httpclient.debug". + * In addition, if the message severity level is >= to + * the provided {@code errLevel} it will print the messages on stderr. + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @apiNote To obtain a logger that will always print things on stderr in + * addition to forwarding to the internal logger, use + * {@code getDebugLogger(this::dbgTag, Level.ALL);}. + * This is also equivalent to calling + * {@code getDebugLogger(this::dbgTag, true);}. + * To obtain a logger that will only forward to the internal logger, + * use {@code getDebugLogger(this::dbgTag, Level.OFF);}. + * This is also equivalent to calling + * {@code getDebugLogger(this::dbgTag, false);}. + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") + * @param errLevel The level above which messages will be also printed on + * stderr (in addition to be forwarded to the internal logger). + * + * @return A logger for HTTP internal debug traces + */ + static Logger getDebugLogger(Supplier<String> dbgTag, Level errLevel) { + return DebugLogger.createHttpLogger(dbgTag, Level.OFF, errLevel); + } + + /** + * Get a logger for debug HTTP traces.The logger should only be used + * with levels whose severity is {@code <= DEBUG}. + * + * By default, this logger will forward all messages logged to an internal + * logger named "jdk.internal.httpclient.debug". + * In addition, the provided boolean {@code on==true}, it will print the + * messages on stderr. + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @apiNote To obtain a logger that will always print things on stderr in + * addition to forwarding to the internal logger, use + * {@code getDebugLogger(this::dbgTag, true);}. + * This is also equivalent to calling + * {@code getDebugLogger(this::dbgTag, Level.ALL);}. + * To obtain a logger that will only forward to the internal logger, + * use {@code getDebugLogger(this::dbgTag, false);}. + * This is also equivalent to calling + * {@code getDebugLogger(this::dbgTag, Level.OFF);}. + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))") + * @param on Whether messages should also be printed on + * stderr (in addition to be forwarded to the internal logger). + * + * @return A logger for HTTP internal debug traces + */ + public static Logger getDebugLogger(Supplier<String> dbgTag, boolean on) { + Level errLevel = on ? Level.ALL : Level.OFF; + return getDebugLogger(dbgTag, errLevel); + } + + /** + * Get a logger for debug HPACK traces.The logger should only be used + * with levels whose severity is {@code <= DEBUG}. + * + * By default, this logger will forward all messages logged to an internal + * logger named "jdk.internal.httpclient.hpack.debug". + * In addition, if the message severity level is >= to + * the provided {@code outLevel} it will print the messages on stdout. + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @apiNote To obtain a logger that will always print things on stdout in + * addition to forwarding to the internal logger, use + * {@code getHpackLogger(this::dbgTag, Level.ALL);}. + * This is also equivalent to calling + * {@code getHpackLogger(this::dbgTag, true);}. + * To obtain a logger that will only forward to the internal logger, + * use {@code getHpackLogger(this::dbgTag, Level.OFF);}. + * This is also equivalent to calling + * {@code getHpackLogger(this::dbgTag, false);}. + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") + * @param outLevel The level above which messages will be also printed on + * stdout (in addition to be forwarded to the internal logger). + * + * @return A logger for HPACK internal debug traces + */ + public static Logger getHpackLogger(Supplier<String> dbgTag, Level outLevel) { + Level errLevel = Level.OFF; + return DebugLogger.createHpackLogger(dbgTag, outLevel, errLevel); + } + + /** + * Get a logger for debug HPACK traces.The logger should only be used + * with levels whose severity is {@code <= DEBUG}. + * + * By default, this logger will forward all messages logged to an internal + * logger named "jdk.internal.httpclient.hpack.debug". + * In addition, the provided boolean {@code on==true}, it will print the + * messages on stdout. + * The logger will add some decoration to the printed message, in the form of + * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>} + * + * @apiNote To obtain a logger that will always print things on stdout in + * addition to forwarding to the internal logger, use + * {@code getHpackLogger(this::dbgTag, true);}. + * This is also equivalent to calling + * {@code getHpackLogger(this::dbgTag, Level.ALL);}. + * To obtain a logger that will only forward to the internal logger, + * use {@code getHpackLogger(this::dbgTag, false);}. + * This is also equivalent to calling + * {@code getHpackLogger(this::dbgTag, Level.OFF);}. + * + * @param dbgTag A lambda that returns a string that identifies the caller + * (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)") + * @param on Whether messages should also be printed on + * stdout (in addition to be forwarded to the internal logger). + * + * @return A logger for HPACK internal debug traces + */ + public static Logger getHpackLogger(Supplier<String> dbgTag, boolean on) { + Level outLevel = on ? Level.ALL : Level.OFF; + return getHpackLogger(dbgTag, outLevel); + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java index 8e4fbb89889..3c8c8158596 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,19 +25,19 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; +import java.nio.ByteBuffer; +import java.util.List; public class ContinuationFrame extends HeaderFrame { public static final int TYPE = 0x9; - public ContinuationFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) { + public ContinuationFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) { super(streamid, flags, headerBlocks); } - public ContinuationFrame(int streamid, ByteBufferReference headersBlock) { - this(streamid, 0, new ByteBufferReference[]{headersBlock}); + public ContinuationFrame(int streamid, ByteBuffer headersBlock) { + this(streamid, 0, List.of(headersBlock)); } @Override diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java index 70d390a0c53..e5af442a784 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,9 +25,11 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; import jdk.incubator.http.internal.common.Utils; +import java.nio.ByteBuffer; +import java.util.List; + public class DataFrame extends Http2Frame { public static final int TYPE = 0x0; @@ -37,20 +39,20 @@ public class DataFrame extends Http2Frame { public static final int PADDED = 0x8; private int padLength; - private final ByteBufferReference[] data; + private final List<ByteBuffer> data; private final int dataLength; - public DataFrame(int streamid, int flags, ByteBufferReference data) { - this(streamid, flags, new ByteBufferReference[]{data}); + public DataFrame(int streamid, int flags, ByteBuffer data) { + this(streamid, flags, List.of(data)); } - public DataFrame(int streamid, int flags, ByteBufferReference[] data) { + public DataFrame(int streamid, int flags, List<ByteBuffer> data) { super(streamid, flags); this.data = data; - this.dataLength = Utils.remaining(data); + this.dataLength = Utils.remaining(data, Integer.MAX_VALUE); } - public DataFrame(int streamid, int flags, ByteBufferReference[] data, int padLength) { + public DataFrame(int streamid, int flags, List<ByteBuffer> data, int padLength) { this(streamid, flags, data); if (padLength > 0) { setPadLength(padLength); @@ -78,7 +80,7 @@ public class DataFrame extends Http2Frame { return super.flagAsString(flag); } - public ByteBufferReference[] getData() { + public List<ByteBuffer> getData() { return data; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java index e6a247cb5d8..22634fc6145 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -88,8 +88,4 @@ public abstract class ErrorFrame extends Http2Frame { public int getErrorCode() { return this.errorCode; } - - public void setErrorCode(int errorCode) { - this.errorCode = errorCode; - } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java index bc465110d66..1b8ff4ac0b1 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,15 +25,16 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; import jdk.incubator.http.internal.common.Log; import jdk.incubator.http.internal.common.Utils; import java.io.IOException; +import java.lang.System.Logger.Level; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; +import java.util.Queue; /** * Frames Decoder @@ -46,7 +47,9 @@ import java.util.List; */ public class FramesDecoder { - + static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. + static final System.Logger DEBUG_LOGGER = + Utils.getDebugLogger("FramesDecoder"::toString, DEBUG); @FunctionalInterface public interface FrameProcessor { @@ -56,14 +59,14 @@ public class FramesDecoder { private final FrameProcessor frameProcessor; private final int maxFrameSize; - private ByteBufferReference currentBuffer; // current buffer either null or hasRemaining + private ByteBuffer currentBuffer; // current buffer either null or hasRemaining - private final java.util.Queue<ByteBufferReference> tailBuffers = new ArrayDeque<>(); + private final ArrayDeque<ByteBuffer> tailBuffers = new ArrayDeque<>(); private int tailSize = 0; private boolean slicedToDataFrame = false; - private final List<ByteBufferReference> prepareToRelease = new ArrayList<>(); + private final List<ByteBuffer> prepareToRelease = new ArrayList<>(); // if true - Frame Header was parsed (9 bytes consumed) and subsequent fields have meaning // otherwise - stopped at frames boundary @@ -72,6 +75,7 @@ public class FramesDecoder { private int frameType; private int frameFlags; private int frameStreamid; + private boolean closed; /** * Creates Frame Decoder @@ -92,25 +96,63 @@ public class FramesDecoder { this.maxFrameSize = Math.min(Math.max(16 * 1024, maxFrameSize), 16 * 1024 * 1024 - 1); } + /** Threshold beyond which data is no longer copied into the current buffer, + * if that buffer has enough unused space. */ + private static final int COPY_THRESHOLD = 8192; + /** - * put next buffer into queue, - * if frame decoding is possible - decode all buffers and invoke FrameProcessor + * Adds the data from the given buffer, and performs frame decoding if + * possible. Either 1) appends the data from the given buffer to the + * current buffer ( if there is enough unused space ), or 2) adds it to the + * next buffer in the queue. * - * @param buffer - * @throws IOException + * If there is enough data to perform frame decoding then, all buffers are + * decoded and the FrameProcessor is invoked. */ - public void decode(ByteBufferReference buffer) throws IOException { - int remaining = buffer.get().remaining(); + public void decode(ByteBuffer inBoundBuffer) throws IOException { + if (closed) { + DEBUG_LOGGER.log(Level.DEBUG, "closed: ignoring buffer (%s bytes)", + inBoundBuffer.remaining()); + inBoundBuffer.position(inBoundBuffer.limit()); + return; + } + int remaining = inBoundBuffer.remaining(); + DEBUG_LOGGER.log(Level.DEBUG, "decodes: %d", remaining); if (remaining > 0) { if (currentBuffer == null) { - currentBuffer = buffer; + currentBuffer = inBoundBuffer; } else { - tailBuffers.add(buffer); - tailSize += remaining; + ByteBuffer b = currentBuffer; + if (!tailBuffers.isEmpty()) { + b = tailBuffers.getLast(); + } + + int limit = b.limit(); + int freeSpace = b.capacity() - limit; + if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) { + // append the new data to the unused space in the current buffer + int position = b.position(); + b.position(limit); + b.limit(limit + inBoundBuffer.remaining()); + b.put(inBoundBuffer); + b.position(position); + if (b != currentBuffer) + tailSize += remaining; + DEBUG_LOGGER.log(Level.DEBUG, "copied: %d", remaining); + } else { + DEBUG_LOGGER.log(Level.DEBUG, "added: %d", remaining); + tailBuffers.add(inBoundBuffer); + tailSize += remaining; + } } } + DEBUG_LOGGER.log(Level.DEBUG, "Tail size is now: %d, current=", + tailSize, + (currentBuffer == null ? 0 : + currentBuffer.remaining())); Http2Frame frame; while ((frame = nextFrame()) != null) { + DEBUG_LOGGER.log(Level.DEBUG, "Got frame: %s", frame); frameProcessor.processFrame(frame); frameProcessed(); } @@ -121,21 +163,29 @@ public class FramesDecoder { if (currentBuffer == null) { return null; // no data at all } + long available = currentBuffer.remaining() + tailSize; if (!frameHeaderParsed) { - if (currentBuffer.get().remaining() + tailSize >= Http2Frame.FRAME_HEADER_SIZE) { + if (available >= Http2Frame.FRAME_HEADER_SIZE) { parseFrameHeader(); if (frameLength > maxFrameSize) { // connection error return new MalformedFrame(ErrorFrame.FRAME_SIZE_ERROR, - "Frame type("+frameType+") " +"length("+frameLength+") exceeds MAX_FRAME_SIZE("+ maxFrameSize+")"); + "Frame type("+frameType+") " + +"length("+frameLength + +") exceeds MAX_FRAME_SIZE(" + + maxFrameSize+")"); } frameHeaderParsed = true; } else { - return null; // no data for frame header + DEBUG_LOGGER.log(Level.DEBUG, + "Not enough data to parse header, needs: %d, has: %d", + Http2Frame.FRAME_HEADER_SIZE, available); + return null; } } + available = currentBuffer == null ? 0 : currentBuffer.remaining() + tailSize; if ((frameLength == 0) || - (currentBuffer != null && currentBuffer.get().remaining() + tailSize >= frameLength)) { + (currentBuffer != null && available >= frameLength)) { Http2Frame frame = parseFrameBody(); frameHeaderParsed = false; // frame == null means we have to skip this frame and try parse next @@ -143,19 +193,21 @@ public class FramesDecoder { return frame; } } else { + DEBUG_LOGGER.log(Level.DEBUG, + "Not enough data to parse frame body, needs: %d, has: %d", + frameLength, available); return null; // no data for the whole frame header } } } private void frameProcessed() { - prepareToRelease.forEach(ByteBufferReference::clear); prepareToRelease.clear(); } private void parseFrameHeader() throws IOException { int x = getInt(); - this.frameLength = x >> 8; + this.frameLength = (x >>> 8) & 0x00ffffff; this.frameType = x & 0xff; this.frameFlags = getByte(); this.frameStreamid = getInt() & 0x7fffffff; @@ -165,29 +217,27 @@ public class FramesDecoder { // move next buffer from tailBuffers to currentBuffer if required private void nextBuffer() { - if (!currentBuffer.get().hasRemaining()) { + if (!currentBuffer.hasRemaining()) { if (!slicedToDataFrame) { prepareToRelease.add(currentBuffer); } slicedToDataFrame = false; currentBuffer = tailBuffers.poll(); if (currentBuffer != null) { - tailSize -= currentBuffer.get().remaining(); + tailSize -= currentBuffer.remaining(); } } } public int getByte() { - ByteBuffer buf = currentBuffer.get(); - int res = buf.get() & 0xff; + int res = currentBuffer.get() & 0xff; nextBuffer(); return res; } public int getShort() { - ByteBuffer buf = currentBuffer.get(); - if (buf.remaining() >= 2) { - int res = buf.getShort() & 0xffff; + if (currentBuffer.remaining() >= 2) { + int res = currentBuffer.getShort() & 0xffff; nextBuffer(); return res; } @@ -197,9 +247,8 @@ public class FramesDecoder { } public int getInt() { - ByteBuffer buf = currentBuffer.get(); - if (buf.remaining() >= 4) { - int res = buf.getInt(); + if (currentBuffer.remaining() >= 4) { + int res = currentBuffer.getInt(); nextBuffer(); return res; } @@ -215,9 +264,8 @@ public class FramesDecoder { byte[] bytes = new byte[n]; int offset = 0; while (n > 0) { - ByteBuffer buf = currentBuffer.get(); - int length = Math.min(n, buf.remaining()); - buf.get(bytes, offset, length); + int length = Math.min(n, currentBuffer.remaining()); + currentBuffer.get(bytes, offset, length); offset += length; n -= length; nextBuffer(); @@ -226,36 +274,48 @@ public class FramesDecoder { } - private ByteBufferReference[] getBuffers(boolean isDataFrame, int bytecount) { - List<ByteBufferReference> res = new ArrayList<>(); + private List<ByteBuffer> getBuffers(boolean isDataFrame, int bytecount) { + List<ByteBuffer> res = new ArrayList<>(); while (bytecount > 0) { - ByteBuffer buf = currentBuffer.get(); - int remaining = buf.remaining(); + int remaining = currentBuffer.remaining(); int extract = Math.min(remaining, bytecount); ByteBuffer extractedBuf; if (isDataFrame) { - extractedBuf = Utils.slice(buf, extract); + extractedBuf = Utils.slice(currentBuffer, extract); slicedToDataFrame = true; } else { // Header frames here // HPACK decoding should performed under lock and immediately after frame decoding. // in that case it is safe to release original buffer, // because of sliced buffer has a very short life - extractedBuf = Utils.slice(buf, extract); + extractedBuf = Utils.slice(currentBuffer, extract); } - res.add(ByteBufferReference.of(extractedBuf)); + res.add(extractedBuf); bytecount -= extract; nextBuffer(); } - return res.toArray(new ByteBufferReference[0]); + return res; + } + + public void close(String msg) { + closed = true; + tailBuffers.clear(); + int bytes = tailSize; + ByteBuffer b = currentBuffer; + if (b != null) { + bytes += b.remaining(); + b.position(b.limit()); + } + tailSize = 0; + currentBuffer = null; + DEBUG_LOGGER.log(Level.DEBUG, "closed %s, ignoring %d bytes", msg, bytes); } public void skipBytes(int bytecount) { while (bytecount > 0) { - ByteBuffer buf = currentBuffer.get(); - int remaining = buf.remaining(); + int remaining = currentBuffer.remaining(); int extract = Math.min(remaining, bytecount); - buf.position(buf.position() + extract); + currentBuffer.position(currentBuffer.position() + extract); bytecount -= remaining; nextBuffer(); } @@ -296,12 +356,13 @@ public class FramesDecoder { private Http2Frame parseDataFrame(int frameLength, int streamid, int flags) { // non-zero stream if (streamid == 0) { - return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, "zero streamId for DataFrame"); + return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, + "zero streamId for DataFrame"); } int padLength = 0; if ((flags & DataFrame.PADDED) != 0) { padLength = getByte(); - if(padLength >= frameLength) { + if (padLength >= frameLength) { return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, "the length of the padding is the length of the frame payload or greater"); } @@ -317,7 +378,8 @@ public class FramesDecoder { private Http2Frame parseHeadersFrame(int frameLength, int streamid, int flags) { // non-zero stream if (streamid == 0) { - return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, "zero streamId for HeadersFrame"); + return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, + "zero streamId for HeadersFrame"); } int padLength = 0; if ((flags & HeadersFrame.PADDED) != 0) { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java index dd7b1f03560..85efbfe1cd0 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,12 +25,8 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; - import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -45,25 +41,24 @@ public class FramesEncoder { public FramesEncoder() { } - public ByteBufferReference[] encodeFrames(List<HeaderFrame> frames) { - List<ByteBufferReference> refs = new ArrayList<>(frames.size() * 2); + public List<ByteBuffer> encodeFrames(List<HeaderFrame> frames) { + List<ByteBuffer> bufs = new ArrayList<>(frames.size() * 2); for (HeaderFrame f : frames) { - refs.addAll(Arrays.asList(encodeFrame(f))); + bufs.addAll(encodeFrame(f)); } - return refs.toArray(new ByteBufferReference[0]); + return bufs; } - public ByteBufferReference encodeConnectionPreface(byte[] preface, SettingsFrame frame) { + public ByteBuffer encodeConnectionPreface(byte[] preface, SettingsFrame frame) { final int length = frame.length(); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length + preface.length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length + preface.length); buf.put(preface); putSettingsFrame(buf, frame, length); buf.flip(); - return ref; + return buf; } - public ByteBufferReference[] encodeFrame(Http2Frame frame) { + public List<ByteBuffer> encodeFrame(Http2Frame frame) { switch (frame.type()) { case DataFrame.TYPE: return encodeDataFrame((DataFrame) frame); @@ -93,47 +88,45 @@ public class FramesEncoder { private static final int NO_FLAGS = 0; private static final int ZERO_STREAM = 0; - private ByteBufferReference[] encodeDataFrame(DataFrame frame) { + private List<ByteBuffer> encodeDataFrame(DataFrame frame) { // non-zero stream assert frame.streamid() != 0; - ByteBufferReference ref = encodeDataFrameStart(frame); + ByteBuffer buf = encodeDataFrameStart(frame); if (frame.getFlag(DataFrame.PADDED)) { - return joinWithPadding(ref, frame.getData(), frame.getPadLength()); + return joinWithPadding(buf, frame.getData(), frame.getPadLength()); } else { - return join(ref, frame.getData()); + return join(buf, frame.getData()); } } - private ByteBufferReference encodeDataFrameStart(DataFrame frame) { + private ByteBuffer encodeDataFrameStart(DataFrame frame) { boolean isPadded = frame.getFlag(DataFrame.PADDED); final int length = frame.getDataLength() + (isPadded ? (frame.getPadLength() + 1) : 0); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0)); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0)); putHeader(buf, length, DataFrame.TYPE, frame.getFlags(), frame.streamid()); if (isPadded) { buf.put((byte) frame.getPadLength()); } buf.flip(); - return ref; + return buf; } - private ByteBufferReference[] encodeHeadersFrame(HeadersFrame frame) { + private List<ByteBuffer> encodeHeadersFrame(HeadersFrame frame) { // non-zero stream assert frame.streamid() != 0; - ByteBufferReference ref = encodeHeadersFrameStart(frame); + ByteBuffer buf = encodeHeadersFrameStart(frame); if (frame.getFlag(HeadersFrame.PADDED)) { - return joinWithPadding(ref, frame.getHeaderBlock(), frame.getPadLength()); + return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength()); } else { - return join(ref, frame.getHeaderBlock()); + return join(buf, frame.getHeaderBlock()); } } - private ByteBufferReference encodeHeadersFrameStart(HeadersFrame frame) { + private ByteBuffer encodeHeadersFrameStart(HeadersFrame frame) { boolean isPadded = frame.getFlag(HeadersFrame.PADDED); boolean hasPriority = frame.getFlag(HeadersFrame.PRIORITY); final int length = frame.getHeaderLength() + (isPadded ? (frame.getPadLength() + 1) : 0) + (hasPriority ? 5 : 0); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0) + (hasPriority ? 5 : 0)); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0) + (hasPriority ? 5 : 0)); putHeader(buf, length, HeadersFrame.TYPE, frame.getFlags(), frame.streamid()); if (isPadded) { buf.put((byte) frame.getPadLength()); @@ -142,51 +135,47 @@ public class FramesEncoder { putPriority(buf, frame.getExclusive(), frame.getStreamDependency(), frame.getWeight()); } buf.flip(); - return ref; + return buf; } - private ByteBufferReference[] encodePriorityFrame(PriorityFrame frame) { + private List<ByteBuffer> encodePriorityFrame(PriorityFrame frame) { // non-zero stream; no flags assert frame.streamid() != 0; final int length = 5; - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putHeader(buf, length, PriorityFrame.TYPE, NO_FLAGS, frame.streamid()); putPriority(buf, frame.exclusive(), frame.streamDependency(), frame.weight()); buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodeResetFrame(ResetFrame frame) { + private List<ByteBuffer> encodeResetFrame(ResetFrame frame) { // non-zero stream; no flags assert frame.streamid() != 0; final int length = 4; - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putHeader(buf, length, ResetFrame.TYPE, NO_FLAGS, frame.streamid()); buf.putInt(frame.getErrorCode()); buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodeSettingsFrame(SettingsFrame frame) { + private List<ByteBuffer> encodeSettingsFrame(SettingsFrame frame) { // only zero stream assert frame.streamid() == 0; final int length = frame.length(); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putSettingsFrame(buf, frame, length); buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodePushPromiseFrame(PushPromiseFrame frame) { + private List<ByteBuffer> encodePushPromiseFrame(PushPromiseFrame frame) { // non-zero stream assert frame.streamid() != 0; boolean isPadded = frame.getFlag(PushPromiseFrame.PADDED); final int length = frame.getHeaderLength() + (isPadded ? 5 : 4); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 5 : 4)); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 5 : 4)); putHeader(buf, length, PushPromiseFrame.TYPE, frame.getFlags(), frame.streamid()); if (isPadded) { buf.put((byte) frame.getPadLength()); @@ -195,31 +184,29 @@ public class FramesEncoder { buf.flip(); if (frame.getFlag(PushPromiseFrame.PADDED)) { - return joinWithPadding(ref, frame.getHeaderBlock(), frame.getPadLength()); + return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength()); } else { - return join(ref, frame.getHeaderBlock()); + return join(buf, frame.getHeaderBlock()); } } - private ByteBufferReference[] encodePingFrame(PingFrame frame) { + private List<ByteBuffer> encodePingFrame(PingFrame frame) { // only zero stream assert frame.streamid() == 0; final int length = 8; - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putHeader(buf, length, PingFrame.TYPE, frame.getFlags(), ZERO_STREAM); buf.put(frame.getData()); buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodeGoAwayFrame(GoAwayFrame frame) { + private List<ByteBuffer> encodeGoAwayFrame(GoAwayFrame frame) { // only zero stream; no flags assert frame.streamid() == 0; byte[] debugData = frame.getDebugData(); final int length = 8 + debugData.length; - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putHeader(buf, length, GoAwayFrame.TYPE, NO_FLAGS, ZERO_STREAM); buf.putInt(frame.getLastStream()); buf.putInt(frame.getErrorCode()); @@ -227,45 +214,50 @@ public class FramesEncoder { buf.put(debugData); } buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodeWindowUpdateFrame(WindowUpdateFrame frame) { + private List<ByteBuffer> encodeWindowUpdateFrame(WindowUpdateFrame frame) { // any stream; no flags final int length = 4; - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length); putHeader(buf, length, WindowUpdateFrame.TYPE, NO_FLAGS, frame.streamid); buf.putInt(frame.getUpdate()); buf.flip(); - return new ByteBufferReference[]{ref}; + return List.of(buf); } - private ByteBufferReference[] encodeContinuationFrame(ContinuationFrame frame) { + private List<ByteBuffer> encodeContinuationFrame(ContinuationFrame frame) { // non-zero stream; assert frame.streamid() != 0; final int length = frame.getHeaderLength(); - ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE); - ByteBuffer buf = ref.get(); + ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE); putHeader(buf, length, ContinuationFrame.TYPE, frame.getFlags(), frame.streamid()); buf.flip(); - return join(ref, frame.getHeaderBlock()); + return join(buf, frame.getHeaderBlock()); } - private ByteBufferReference[] joinWithPadding(ByteBufferReference ref, ByteBufferReference[] data, int padLength) { - ByteBufferReference[] references = new ByteBufferReference[2 + data.length]; - references[0] = ref; - System.arraycopy(data, 0, references, 1, data.length); - assert references[references.length - 1] == null; - references[references.length - 1] = getPadding(padLength); - return references; + private List<ByteBuffer> joinWithPadding(ByteBuffer buf, List<ByteBuffer> data, int padLength) { + int len = data.size(); + if (len == 0) return List.of(buf, getPadding(padLength)); + else if (len == 1) return List.of(buf, data.get(0), getPadding(padLength)); + else if (len == 2) return List.of(buf, data.get(0), data.get(1), getPadding(padLength)); + List<ByteBuffer> res = new ArrayList<>(len+2); + res.add(buf); + res.addAll(data); + res.add(getPadding(padLength)); + return res; } - private ByteBufferReference[] join(ByteBufferReference ref, ByteBufferReference[] data) { - ByteBufferReference[] references = new ByteBufferReference[1 + data.length]; - references[0] = ref; - System.arraycopy(data, 0, references, 1, data.length); - return references; + private List<ByteBuffer> join(ByteBuffer buf, List<ByteBuffer> data) { + int len = data.size(); + if (len == 0) return List.of(buf); + else if (len == 1) return List.of(buf, data.get(0)); + else if (len == 2) return List.of(buf, data.get(0), data.get(1)); + List<ByteBuffer> joined = new ArrayList<>(len + 1); + joined.add(buf); + joined.addAll(data); + return joined; } private void putSettingsFrame(ByteBuffer buf, SettingsFrame frame, int length) { @@ -287,15 +279,15 @@ public class FramesEncoder { buf.put((byte) weight); } - private ByteBufferReference getBuffer(int capacity) { - return ByteBufferReference.of(ByteBuffer.allocate(capacity)); + private ByteBuffer getBuffer(int capacity) { + return ByteBuffer.allocate(capacity); } - public ByteBufferReference getPadding(int length) { + public ByteBuffer getPadding(int length) { if (length > 255) { throw new IllegalArgumentException("Padding too big"); } - return ByteBufferReference.of(ByteBuffer.allocate(length)); // zeroed! + return ByteBuffer.allocate(length); // zeroed! } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java index b29fed84147..0dd10f2a6be 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java index cc243a7d14b..628e43e6aa7 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,10 +25,10 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; import jdk.incubator.http.internal.common.Utils; import java.nio.ByteBuffer; +import java.util.List; /** * Either a HeadersFrame or a ContinuationFrame @@ -36,19 +36,15 @@ import java.nio.ByteBuffer; public abstract class HeaderFrame extends Http2Frame { final int headerLength; - final ByteBufferReference[] headerBlocks; + final List<ByteBuffer> headerBlocks; public static final int END_STREAM = 0x1; public static final int END_HEADERS = 0x4; - public HeaderFrame(int streamid, int flags, ByteBufferReference headerBlock) { - this(streamid, flags, new ByteBufferReference[]{headerBlock}); - } - - public HeaderFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) { + public HeaderFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) { super(streamid, flags); this.headerBlocks = headerBlocks; - this.headerLength = Utils.remaining(headerBlocks); + this.headerLength = Utils.remaining(headerBlocks, Integer.MAX_VALUE); } @Override @@ -63,7 +59,7 @@ public abstract class HeaderFrame extends Http2Frame { } - public ByteBufferReference[] getHeaderBlock() { + public List<ByteBuffer> getHeaderBlock() { return headerBlocks; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java index 2efc5791d29..8c59edcfb18 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,8 +25,8 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; +import java.nio.ByteBuffer; +import java.util.List; public class HeadersFrame extends HeaderFrame { @@ -43,19 +43,19 @@ public class HeadersFrame extends HeaderFrame { private int weight; private boolean exclusive; - public HeadersFrame(int streamid, int flags, ByteBufferReference[] headerBlocks, int padLength) { + public HeadersFrame(int streamid, int flags, List<ByteBuffer> headerBlocks, int padLength) { super(streamid, flags, headerBlocks); if (padLength > 0) { setPadLength(padLength); } } - public HeadersFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) { + public HeadersFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) { super(streamid, flags, headerBlocks); } - public HeadersFrame(int streamid, int flags, ByteBufferReference headerBlock) { - this(streamid, flags, new ByteBufferReference[]{headerBlock}); + public HeadersFrame(int streamid, int flags, ByteBuffer headerBlock) { + this(streamid, flags, List.of(headerBlock)); } @Override diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java index 423edbade5a..f4930342a0f 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -49,10 +49,6 @@ public abstract class Http2Frame { flags |= flag; } - public void setFlags(int flags) { - this.flags = flags; - } - public int getFlags() { return flags; } @@ -61,16 +57,16 @@ public abstract class Http2Frame { return (flags & flag) != 0; } - public void clearFlag(int flag) { - flags &= 0xffffffff ^ flag; - } +// public void clearFlag(int flag) { +// flags &= 0xffffffff ^ flag; +// } public void streamid(int streamid) { this.streamid = streamid; } - public String typeAsString() { + private String typeAsString() { return asString(type()); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java index 3e0ac828149..f365dc7f0e4 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2015, 2017, 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. 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. + */ + package jdk.incubator.http.internal.frame; public class MalformedFrame extends Http2Frame { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java index 90daa44ca9a..984ee492cb6 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java index b966b79ebab..640adb4883c 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java index 9231865e138..c7f1b7fb7ac 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java index 95c11955856..b5d53966bee 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,8 +25,8 @@ package jdk.incubator.http.internal.frame; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; +import java.nio.ByteBuffer; +import java.util.List; public class PushPromiseFrame extends HeaderFrame { @@ -39,7 +39,7 @@ public class PushPromiseFrame extends HeaderFrame { public static final int END_HEADERS = 0x4; public static final int PADDED = 0x8; - public PushPromiseFrame(int streamid, int flags, int promisedStream, ByteBufferReference[] buffers, int padLength) { + public PushPromiseFrame(int streamid, int flags, int promisedStream, List<ByteBuffer> buffers, int padLength) { super(streamid, flags, buffers); this.promisedStream = promisedStream; if(padLength > 0 ) { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java index 63a9ee8a96a..e5d2dfc49ef 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java index 4cf32c03a65..12b237d8ad8 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java index ed9547b428d..b6ab8e142de 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java index c2f30f01f4b..7b42cb7205b 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java index df89d2f7037..f8e46fd434d 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java index 9841204e50c..bbf7001ead3 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java @@ -24,20 +24,22 @@ */ package jdk.incubator.http.internal.hpack; +import jdk.incubator.http.internal.hpack.HPACK.Logger; import jdk.internal.vm.annotation.Stable; import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.ProtocolException; import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicLong; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL; import static java.lang.String.format; import static java.util.Objects.requireNonNull; /** * Decodes headers from their binary representation. * - * <p>Typical lifecycle looks like this: + * <p> Typical lifecycle looks like this: * * <p> {@link #Decoder(int) new Decoder} * ({@link #setMaxCapacity(int) setMaxCapacity}? @@ -62,6 +64,9 @@ import static java.util.Objects.requireNonNull; */ public final class Decoder { + private final Logger logger; + private static final AtomicLong DECODERS_IDS = new AtomicLong(); + @Stable private static final State[] states = new State[256]; @@ -92,6 +97,7 @@ public final class Decoder { } } + private final long id; private final HeaderTable table; private State state = State.READY; @@ -111,9 +117,8 @@ public final class Decoder { * header table. * * <p> The value has to be agreed between decoder and encoder out-of-band, - * e.g. by a protocol that uses HPACK (see <a - * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table - * Size</a>). + * e.g. by a protocol that uses HPACK + * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>). * * @param capacity * a non-negative integer @@ -122,8 +127,24 @@ public final class Decoder { * if capacity is negative */ public Decoder(int capacity) { - setMaxCapacity(capacity); - table = new HeaderTable(capacity); + id = DECODERS_IDS.incrementAndGet(); + logger = HPACK.getLogger().subLogger("Decoder#" + id); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("new decoder with maximum table size %s", + capacity)); + } + if (logger.isLoggable(NORMAL)) { + /* To correlate with logging outside HPACK, knowing + hashCode/toString is important */ + logger.log(NORMAL, () -> { + String hashCode = Integer.toHexString( + System.identityHashCode(this)); + return format("toString='%s', identityHashCode=%s", + toString(), hashCode); + }); + } + setMaxCapacity0(capacity); + table = new HeaderTable(capacity, logger.subLogger("HeaderTable")); integerReader = new IntegerReader(); stringReader = new StringReader(); name = new StringBuilder(512); @@ -134,9 +155,8 @@ public final class Decoder { * Sets a maximum capacity of the header table. * * <p> The value has to be agreed between decoder and encoder out-of-band, - * e.g. by a protocol that uses HPACK (see <a - * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table - * Size</a>). + * e.g. by a protocol that uses HPACK + * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>). * * @param capacity * a non-negative integer @@ -145,6 +165,14 @@ public final class Decoder { * if capacity is negative */ public void setMaxCapacity(int capacity) { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("setting maximum table size to %s", + capacity)); + } + setMaxCapacity0(capacity); + } + + private void setMaxCapacity0(int capacity) { if (capacity < 0) { throw new IllegalArgumentException("capacity >= 0: " + capacity); } @@ -155,8 +183,8 @@ public final class Decoder { /** * Decodes a header block from the given buffer to the given callback. * - * <p> Suppose a header block is represented by a sequence of {@code - * ByteBuffer}s in the form of {@code Iterator<ByteBuffer>}. And the + * <p> Suppose a header block is represented by a sequence of + * {@code ByteBuffer}s in the form of {@code Iterator<ByteBuffer>}. And the * consumer of decoded headers is represented by the callback. Then to * decode the header block, the following approach might be used: * @@ -174,7 +202,7 @@ public final class Decoder { * * <p> Once the method is invoked with {@code endOfHeaderBlock == true}, the * current header block is deemed ended, and inconsistencies, if any, are - * reported immediately by throwing an {@code UncheckedIOException}. + * reported immediately by throwing an {@code IOException}. * * <p> Each callback method is called only after the implementation has * processed the corresponding bytes. If the bytes revealed a decoding @@ -200,25 +228,32 @@ public final class Decoder { * * @param consumer * the callback - * @throws UncheckedIOException + * @throws IOException * in case of a decoding error * @throws NullPointerException * if either headerBlock or consumer are null */ - public void decode(ByteBuffer headerBlock, boolean endOfHeaderBlock, - DecodingCallback consumer) { + public void decode(ByteBuffer headerBlock, + boolean endOfHeaderBlock, + DecodingCallback consumer) throws IOException { requireNonNull(headerBlock, "headerBlock"); requireNonNull(consumer, "consumer"); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("reading %s, end of header block? %s", + headerBlock, endOfHeaderBlock)); + } while (headerBlock.hasRemaining()) { proceed(headerBlock, consumer); } if (endOfHeaderBlock && state != State.READY) { - throw new UncheckedIOException( - new ProtocolException("Unexpected end of header block")); + logger.log(NORMAL, () -> format("unexpected end of %s representation", + state)); + throw new IOException("Unexpected end of header block"); } } - private void proceed(ByteBuffer input, DecodingCallback action) { + private void proceed(ByteBuffer input, DecodingCallback action) + throws IOException { switch (state) { case READY: resumeReady(input); @@ -239,14 +274,17 @@ public final class Decoder { resumeSizeUpdate(input, action); break; default: - throw new InternalError( - "Unexpected decoder state: " + String.valueOf(state)); + throw new InternalError("Unexpected decoder state: " + state); } } private void resumeReady(ByteBuffer input) { int b = input.get(input.position()) & 0xff; // absolute read State s = states[b]; + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("next binary representation %s (first byte 0x%02x)", + s, b)); + } switch (s) { case INDEXED: integerReader.configure(7); @@ -292,20 +330,36 @@ public final class Decoder { // | 1 | Index (7+) | // +---+---------------------------+ // - private void resumeIndexed(ByteBuffer input, DecodingCallback action) { + private void resumeIndexed(ByteBuffer input, DecodingCallback action) + throws IOException { if (!integerReader.read(input)) { return; } intValue = integerReader.get(); integerReader.reset(); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("indexed %s", intValue)); + } try { - HeaderTable.HeaderField f = table.get(intValue); + HeaderTable.HeaderField f = getHeaderFieldAt(intValue); action.onIndexed(intValue, f.name, f.value); } finally { state = State.READY; } } + private HeaderTable.HeaderField getHeaderFieldAt(int index) + throws IOException + { + HeaderTable.HeaderField f; + try { + f = table.get(index); + } catch (IndexOutOfBoundsException e) { + throw new IOException("header fields table index", e); + } + return f; + } + // 0 1 2 3 4 5 6 7 // +---+---+---+---+---+---+---+---+ // | 0 | 0 | 0 | 0 | Index (4+) | @@ -328,15 +382,24 @@ public final class Decoder { // | Value String (Length octets) | // +-------------------------------+ // - private void resumeLiteral(ByteBuffer input, DecodingCallback action) { + private void resumeLiteral(ByteBuffer input, DecodingCallback action) + throws IOException { if (!completeReading(input)) { return; } try { if (firstValueIndex) { - HeaderTable.HeaderField f = table.get(intValue); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal without indexing ('%s', '%s')", + intValue, value)); + } + HeaderTable.HeaderField f = getHeaderFieldAt(intValue); action.onLiteral(intValue, f.name, value, valueHuffmanEncoded); } else { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal without indexing ('%s', '%s')", + name, value)); + } action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -367,7 +430,9 @@ public final class Decoder { // | Value String (Length octets) | // +-------------------------------+ // - private void resumeLiteralWithIndexing(ByteBuffer input, DecodingCallback action) { + private void resumeLiteralWithIndexing(ByteBuffer input, + DecodingCallback action) + throws IOException { if (!completeReading(input)) { return; } @@ -381,17 +446,22 @@ public final class Decoder { String n; String v = value.toString(); if (firstValueIndex) { - HeaderTable.HeaderField f = table.get(intValue); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal with incremental indexing ('%s', '%s')", + intValue, value)); + } + HeaderTable.HeaderField f = getHeaderFieldAt(intValue); n = f.name; action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded); } else { n = name.toString(); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal with incremental indexing ('%s', '%s')", + n, value)); + } action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded); } table.put(n, v); - } catch (IllegalArgumentException | IllegalStateException e) { - throw new UncheckedIOException( - (IOException) new ProtocolException().initCause(e)); } finally { cleanUpAfterReading(); } @@ -419,15 +489,25 @@ public final class Decoder { // | Value String (Length octets) | // +-------------------------------+ // - private void resumeLiteralNeverIndexed(ByteBuffer input, DecodingCallback action) { + private void resumeLiteralNeverIndexed(ByteBuffer input, + DecodingCallback action) + throws IOException { if (!completeReading(input)) { return; } try { if (firstValueIndex) { - HeaderTable.HeaderField f = table.get(intValue); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal never indexed ('%s', '%s')", + intValue, value)); + } + HeaderTable.HeaderField f = getHeaderFieldAt(intValue); action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded); } else { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("literal never indexed ('%s', '%s')", + name, value)); + } action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded); } } finally { @@ -440,16 +520,21 @@ public final class Decoder { // | 0 | 0 | 1 | Max size (5+) | // +---+---------------------------+ // - private void resumeSizeUpdate(ByteBuffer input, DecodingCallback action) { + private void resumeSizeUpdate(ByteBuffer input, + DecodingCallback action) throws IOException { if (!integerReader.read(input)) { return; } intValue = integerReader.get(); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("dynamic table size update %s", + intValue)); + } assert intValue >= 0; if (intValue > capacity) { - throw new UncheckedIOException(new ProtocolException( - format("Received capacity exceeds expected: " + - "capacity=%s, expected=%s", intValue, capacity))); + throw new IOException( + format("Received capacity exceeds expected: capacity=%s, expected=%s", + intValue, capacity)); } integerReader.reset(); try { @@ -460,7 +545,7 @@ public final class Decoder { } } - private boolean completeReading(ByteBuffer input) { + private boolean completeReading(ByteBuffer input) throws IOException { if (!firstValueRead) { if (firstValueIndex) { if (!integerReader.read(input)) { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java index 55b6270d3fc..3d1395ebe24 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -35,10 +35,10 @@ import java.nio.ByteBuffer; * * @apiNote * - * <p> The callback provides methods for all possible <a - * href="https://tools.ietf.org/html/rfc7541#section-6">binary - * representations</a>. This could be useful for implementing an intermediary, - * logging, debugging, etc. + * <p> The callback provides methods for all possible + * <a href="https://tools.ietf.org/html/rfc7541#section-6">binary representations</a>. + * This could be useful for implementing an intermediary, logging, debugging, + * etc. * * <p> The callback is an interface in order to interoperate with lambdas (in * the most common use case): @@ -98,7 +98,8 @@ public interface DecodingCallback { * @see #onLiteralNeverIndexed(int, CharSequence, CharSequence, boolean) * @see #onLiteralNeverIndexed(CharSequence, boolean, CharSequence, boolean) */ - default void onDecoded(CharSequence name, CharSequence value, + default void onDecoded(CharSequence name, + CharSequence value, boolean sensitive) { onDecoded(name, value); } @@ -142,8 +143,10 @@ public interface DecodingCallback { * @param valueHuffman * if the {@code value} was Huffman encoded */ - default void onLiteral(int index, CharSequence name, - CharSequence value, boolean valueHuffman) { + default void onLiteral(int index, + CharSequence name, + CharSequence value, + boolean valueHuffman) { onDecoded(name, value, false); } @@ -166,8 +169,10 @@ public interface DecodingCallback { * @param valueHuffman * if the {@code value} was Huffman encoded */ - default void onLiteral(CharSequence name, boolean nameHuffman, - CharSequence value, boolean valueHuffman) { + default void onLiteral(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { onDecoded(name, value, false); } @@ -190,7 +195,8 @@ public interface DecodingCallback { * @param valueHuffman * if the {@code value} was Huffman encoded */ - default void onLiteralNeverIndexed(int index, CharSequence name, + default void onLiteralNeverIndexed(int index, + CharSequence name, CharSequence value, boolean valueHuffman) { onDecoded(name, value, true); @@ -215,8 +221,10 @@ public interface DecodingCallback { * @param valueHuffman * if the {@code value} was Huffman encoded */ - default void onLiteralNeverIndexed(CharSequence name, boolean nameHuffman, - CharSequence value, boolean valueHuffman) { + default void onLiteralNeverIndexed(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { onDecoded(name, value, true); } @@ -241,7 +249,8 @@ public interface DecodingCallback { */ default void onLiteralWithIndexing(int index, CharSequence name, - CharSequence value, boolean valueHuffman) { + CharSequence value, + boolean valueHuffman) { onDecoded(name, value, false); } @@ -264,8 +273,10 @@ public interface DecodingCallback { * @param valueHuffman * if the {@code value} was Huffman encoded */ - default void onLiteralWithIndexing(CharSequence name, boolean nameHuffman, - CharSequence value, boolean valueHuffman) { + default void onLiteralWithIndexing(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { onDecoded(name, value, false); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java index cbb4a3546b9..cd91af4c4f2 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -24,27 +24,32 @@ */ package jdk.incubator.http.internal.hpack; +import jdk.incubator.http.internal.hpack.HPACK.Logger; + import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL; /** * Encodes headers to their binary representation. * - * <p>Typical lifecycle looks like this: + * <p> Typical lifecycle looks like this: * * <p> {@link #Encoder(int) new Encoder} * ({@link #setMaxCapacity(int) setMaxCapacity}? * {@link #encode(ByteBuffer) encode})* * - * <p> Suppose headers are represented by {@code Map<String, List<String>>}. A - * supplier and a consumer of {@link ByteBuffer}s in forms of {@code - * Supplier<ByteBuffer>} and {@code Consumer<ByteBuffer>} respectively. Then to - * encode headers, the following approach might be used: + * <p> Suppose headers are represented by {@code Map<String, List<String>>}. + * A supplier and a consumer of {@link ByteBuffer}s in forms of + * {@code Supplier<ByteBuffer>} and {@code Consumer<ByteBuffer>} respectively. + * Then to encode headers, the following approach might be used: * * <pre>{@code * for (Map.Entry<String, List<String>> h : headers.entrySet()) { @@ -61,10 +66,9 @@ import static java.util.Objects.requireNonNull; * } * }</pre> * - * <p> Though the specification <a - * href="https://tools.ietf.org/html/rfc7541#section-2"> does not define</a> how - * an encoder is to be implemented, a default implementation is provided by the - * method {@link #header(CharSequence, CharSequence, boolean)}. + * <p> Though the specification <a href="https://tools.ietf.org/html/rfc7541#section-2">does not define</a> + * how an encoder is to be implemented, a default implementation is provided by + * the method {@link #header(CharSequence, CharSequence, boolean)}. * * <p> To provide a custom encoding implementation, {@code Encoder} has to be * extended. A subclass then can access methods for encoding using specific @@ -85,8 +89,8 @@ import static java.util.Objects.requireNonNull; * the resulting header block afterwards. * * <p> Splitting the encoding operation into header set up and header encoding, - * separates long lived arguments ({@code name}, {@code value}, {@code - * sensitivity}, etc.) from the short lived ones (e.g. {@code buffer}), + * separates long lived arguments ({@code name}, {@code value}, + * {@code sensitivity}, etc.) from the short lived ones (e.g. {@code buffer}), * simplifying each operation itself. * * @implNote @@ -99,9 +103,13 @@ import static java.util.Objects.requireNonNull; */ public class Encoder { + private static final AtomicLong ENCODERS_IDS = new AtomicLong(); + // TODO: enum: no huffman/smart huffman/always huffman private static final boolean DEFAULT_HUFFMAN = true; + private final Logger logger; + private final long id; private final IndexedWriter indexedWriter = new IndexedWriter(); private final LiteralWriter literalWriter = new LiteralWriter(); private final LiteralNeverIndexedWriter literalNeverIndexedWriter @@ -129,9 +137,8 @@ public class Encoder { * header table. * * <p> The value has to be agreed between decoder and encoder out-of-band, - * e.g. by a protocol that uses HPACK (see <a - * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table - * Size</a>). + * e.g. by a protocol that uses HPACK + * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>). * * @param maxCapacity * a non-negative integer @@ -140,14 +147,33 @@ public class Encoder { * if maxCapacity is negative */ public Encoder(int maxCapacity) { + id = ENCODERS_IDS.incrementAndGet(); + this.logger = HPACK.getLogger().subLogger("Encoder#" + id); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("new encoder with maximum table size %s", + maxCapacity)); + } + if (logger.isLoggable(EXTRA)) { + /* To correlate with logging outside HPACK, knowing + hashCode/toString is important */ + logger.log(EXTRA, () -> { + String hashCode = Integer.toHexString( + System.identityHashCode(this)); + /* Since Encoder can be subclassed hashCode AND identity + hashCode might be different. So let's print both. */ + return format("toString='%s', hashCode=%s, identityHashCode=%s", + toString(), hashCode(), hashCode); + }); + } if (maxCapacity < 0) { - throw new IllegalArgumentException("maxCapacity >= 0: " + maxCapacity); + throw new IllegalArgumentException( + "maxCapacity >= 0: " + maxCapacity); } // Initial maximum capacity update mechanics minCapacity = Long.MAX_VALUE; currCapacity = -1; - setMaxCapacity(maxCapacity); - headerTable = new HeaderTable(lastCapacity); + setMaxCapacity0(maxCapacity); + headerTable = new HeaderTable(lastCapacity, logger.subLogger("HeaderTable")); } /** @@ -176,6 +202,10 @@ public class Encoder { * Sets up the given header {@code (name, value)} with possibly sensitive * value. * + * <p> If the {@code value} is sensitive (think security, secrecy, etc.) + * this encoder will compress it using a special representation + * (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3. Literal Header Field Never Indexed</a>). + * * <p> Fixates {@code name} and {@code value} for the duration of encoding. * * @param name @@ -193,8 +223,13 @@ public class Encoder { * @see #header(CharSequence, CharSequence) * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean) */ - public void header(CharSequence name, CharSequence value, + public void header(CharSequence name, + CharSequence value, boolean sensitive) throws IllegalStateException { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("encoding ('%s', '%s'), sensitive: %s", + name, value, sensitive)); + } // Arguably a good balance between complexity of implementation and // efficiency of encoding requireNonNull(name, "name"); @@ -222,9 +257,8 @@ public class Encoder { * Sets a maximum capacity of the header table. * * <p> The value has to be agreed between decoder and encoder out-of-band, - * e.g. by a protocol that uses HPACK (see <a - * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table - * Size</a>). + * e.g. by a protocol that uses HPACK + * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>). * * <p> May be called any number of times after or before a complete header * has been encoded. @@ -242,11 +276,23 @@ public class Encoder { * hasn't yet started to encode it */ public void setMaxCapacity(int capacity) { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("setting maximum table size to %s", + capacity)); + } + setMaxCapacity0(capacity); + } + + private void setMaxCapacity0(int capacity) { checkEncoding(); if (capacity < 0) { throw new IllegalArgumentException("capacity >= 0: " + capacity); } int calculated = calculateCapacity(capacity); + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("actual maximum table size will be %s", + calculated)); + } if (calculated < 0 || calculated > capacity) { throw new IllegalArgumentException( format("0 <= calculated <= capacity: calculated=%s, capacity=%s", @@ -263,9 +309,22 @@ public class Encoder { minCapacity = Math.min(minCapacity, lastCapacity); } + /** + * Calculates actual capacity to be used by this encoder in response to + * a request to update maximum table size. + * + * <p> Default implementation does not add anything to the headers table, + * hence this method returns {@code 0}. + * + * <p> It is an error to return a value {@code c}, where {@code c < 0} or + * {@code c > maxCapacity}. + * + * @param maxCapacity + * upper bound + * + * @return actual capacity + */ protected int calculateCapacity(int maxCapacity) { - // Default implementation of the Encoder won't add anything to the - // table, therefore no need for a table space return 0; } @@ -298,7 +357,10 @@ public class Encoder { if (!encoding) { throw new IllegalStateException("A header hasn't been set up"); } - if (!prependWithCapacityUpdate(headerBlock)) { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("writing to %s", headerBlock)); + } + if (!prependWithCapacityUpdate(headerBlock)) { // TODO: log return false; } boolean done = writer.write(headerTable, headerBlock); @@ -339,21 +401,35 @@ public class Encoder { protected final void indexed(int index) throws IndexOutOfBoundsException { checkEncoding(); + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("indexed %s", index)); + } encoding = true; writer = indexedWriter.index(index); } - protected final void literal(int index, CharSequence value, + protected final void literal(int index, + CharSequence value, boolean useHuffman) throws IndexOutOfBoundsException { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("literal without indexing ('%s', '%s')", + index, value)); + } checkEncoding(); encoding = true; writer = literalWriter .index(index).value(value, useHuffman); } - protected final void literal(CharSequence name, boolean nameHuffman, - CharSequence value, boolean valueHuffman) { + protected final void literal(CharSequence name, + boolean nameHuffman, + CharSequence value, + boolean valueHuffman) { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("literal without indexing ('%s', '%s')", + name, value)); + } checkEncoding(); encoding = true; writer = literalWriter @@ -364,6 +440,10 @@ public class Encoder { CharSequence value, boolean valueHuffman) throws IndexOutOfBoundsException { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("literal never indexed ('%s', '%s')", + index, value)); + } checkEncoding(); encoding = true; writer = literalNeverIndexedWriter @@ -374,6 +454,10 @@ public class Encoder { boolean nameHuffman, CharSequence value, boolean valueHuffman) { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("literal never indexed ('%s', '%s')", + name, value)); + } checkEncoding(); encoding = true; writer = literalNeverIndexedWriter @@ -384,6 +468,10 @@ public class Encoder { CharSequence value, boolean valueHuffman) throws IndexOutOfBoundsException { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("literal with incremental indexing ('%s', '%s')", + index, value)); + } checkEncoding(); encoding = true; writer = literalWithIndexingWriter @@ -394,6 +482,10 @@ public class Encoder { boolean nameHuffman, CharSequence value, boolean valueHuffman) { + if (logger.isLoggable(EXTRA)) { // TODO: include huffman info? + logger.log(EXTRA, () -> format("literal with incremental indexing ('%s', '%s')", + name, value)); + } checkEncoding(); encoding = true; writer = literalWithIndexingWriter @@ -402,6 +494,10 @@ public class Encoder { protected final void sizeUpdate(int capacity) throws IllegalArgumentException { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("dynamic table size update %s", + capacity)); + } checkEncoding(); // Ensure subclass follows the contract if (capacity > this.maxCapacity) { @@ -420,7 +516,7 @@ public class Encoder { return headerTable; } - protected final void checkEncoding() { + protected final void checkEncoding() { // TODO: better name e.g. checkIfEncodingInProgress() if (encoding) { throw new IllegalStateException( "Previous encoding operation hasn't finished yet"); diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java new file mode 100644 index 00000000000..00e5c654d6a --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017, 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. 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. + */ +package jdk.incubator.http.internal.hpack; + +import jdk.incubator.http.internal.common.Utils; +import jdk.incubator.http.internal.hpack.HPACK.Logger.Level; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.Supplier; + +import static java.lang.String.format; +import static java.util.stream.Collectors.joining; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NONE; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL; + +/** + * Internal utilities and stuff. + */ +public final class HPACK { + + private static final RootLogger LOGGER; + private static final Map<String, Level> logLevels = + Map.of("NORMAL", NORMAL, "EXTRA", EXTRA); + + static { + String PROPERTY = "jdk.internal.httpclient.hpack.log.level"; + + String value = AccessController.doPrivileged( + (PrivilegedAction<String>) () -> System.getProperty(PROPERTY)); + + if (value == null) { + LOGGER = new RootLogger(NONE); + } else { + String upperCasedValue = value.toUpperCase(); + Level l = logLevels.get(upperCasedValue); + if (l == null) { + LOGGER = new RootLogger(NONE); + LOGGER.log(System.Logger.Level.INFO, + () -> format("%s value '%s' not recognized (use %s); logging disabled", + PROPERTY, value, logLevels.keySet().stream().collect(joining(", ")))); + } else { + LOGGER = new RootLogger(l); + LOGGER.log(System.Logger.Level.DEBUG, + () -> format("logging level %s", l)); + } + } + } + + public static Logger getLogger() { + return LOGGER; + } + + private HPACK() { } + + /** + * The purpose of this logger is to provide means of diagnosing issues _in + * the HPACK implementation_. It's not a general purpose logger. + */ + // implements System.Logger to make it possible to skip this class + // when looking for the Caller. + public static class Logger implements System.Logger { + + /** + * Log detail level. + */ + public enum Level { + + NONE(0, System.Logger.Level.OFF), + NORMAL(1, System.Logger.Level.DEBUG), + EXTRA(2, System.Logger.Level.TRACE); + + private final int level; + final System.Logger.Level systemLevel; + + Level(int i, System.Logger.Level system) { + level = i; + systemLevel = system; + } + + public final boolean implies(Level other) { + return this.level >= other.level; + } + } + + private final String name; + private final Level level; + private final String path; + private final System.Logger logger; + + private Logger(String path, String name, Level level) { + this(path, name, level, null); + } + + private Logger(String p, String name, Level level, System.Logger logger) { + this.path = p; + this.name = name; + this.level = level; + this.logger = Utils.getHpackLogger(path::toString, level.systemLevel); + } + + public final String getName() { + return name; + } + + @Override + public boolean isLoggable(System.Logger.Level level) { + return logger.isLoggable(level); + } + + @Override + public void log(System.Logger.Level level, ResourceBundle bundle, String msg, Throwable thrown) { + logger.log(level, bundle, msg,thrown); + } + + @Override + public void log(System.Logger.Level level, ResourceBundle bundle, String format, Object... params) { + logger.log(level, bundle, format, params); + } + + /* + * Usual performance trick for logging, reducing performance overhead in + * the case where logging with the specified level is a NOP. + */ + + public boolean isLoggable(Level level) { + return this.level.implies(level); + } + + public void log(Level level, Supplier<String> s) { + if (this.level.implies(level)) { + logger.log(level.systemLevel, s); + } + } + + public Logger subLogger(String name) { + return new Logger(path + "/" + name, name, level); + } + + } + + private static final class RootLogger extends Logger { + + protected RootLogger(Level level) { + super("hpack", "hpack", level); + } + + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java index 970ad42b54d..30f4304c48e 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java @@ -24,6 +24,7 @@ */ package jdk.incubator.http.internal.hpack; +import jdk.incubator.http.internal.hpack.HPACK.Logger; import jdk.internal.vm.annotation.Stable; import java.util.HashMap; @@ -32,6 +33,8 @@ import java.util.Map; import java.util.NoSuchElementException; import static java.lang.String.format; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA; +import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL; // // Header Table combined from two tables: static and dynamic. @@ -122,11 +125,13 @@ final class HeaderTable { } } + private final Logger logger; private final Table dynamicTable = new Table(0); private int maxSize; private int size; - public HeaderTable(int maxSize) { + public HeaderTable(int maxSize, Logger logger) { + this.logger = logger; setMaxSize(maxSize); } @@ -211,21 +216,41 @@ final class HeaderTable { } private void put(HeaderField h) { + if (logger.isLoggable(NORMAL)) { + logger.log(NORMAL, () -> format("adding ('%s', '%s')", + h.name, h.value)); + } int entrySize = sizeOf(h); + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("size of ('%s', '%s') is %s", + h.name, h.value, entrySize)); + } while (entrySize > maxSize - size && size != 0) { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("insufficient space %s, must evict entry", + (maxSize - size))); + } evictEntry(); } if (entrySize > maxSize - size) { + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("not adding ('%s, '%s'), too big", + h.name, h.value)); + } return; } size += entrySize; dynamicTable.add(h); + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("('%s, '%s') added", h.name, h.value)); + logger.log(EXTRA, this::toString); + } } void setMaxSize(int maxSize) { if (maxSize < 0) { - throw new IllegalArgumentException - ("maxSize >= 0: maxSize=" + maxSize); + throw new IllegalArgumentException( + "maxSize >= 0: maxSize=" + maxSize); } while (maxSize < size && size != 0) { evictEntry(); @@ -237,22 +262,29 @@ final class HeaderTable { HeaderField evictEntry() { HeaderField f = dynamicTable.remove(); - size -= sizeOf(f); + int s = sizeOf(f); + this.size -= s; + if (logger.isLoggable(EXTRA)) { + logger.log(EXTRA, () -> format("evicted entry ('%s', '%s') of size %s", + f.name, f.value, s)); + logger.log(EXTRA, this::toString); + } return f; } @Override public String toString() { double used = maxSize == 0 ? 0 : 100 * (((double) size) / maxSize); - return format("entries: %d; used %s/%s (%.1f%%)", dynamicTable.size(), - size, maxSize, used); + return format("dynamic length: %d, full length: %s, used space: %s/%s (%.1f%%)", + dynamicTable.size(), length(), size, maxSize, used); } - int checkIndex(int index) { - if (index < 1 || index > STATIC_TABLE_LENGTH + dynamicTable.size()) { - throw new IllegalArgumentException( + private int checkIndex(int index) { + int len = length(); + if (index < 1 || index > len) { + throw new IndexOutOfBoundsException( format("1 <= index <= length(): index=%s, length()=%s", - index, length())); + index, len)); } return index; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java index c9c572027cc..ee43b81bf90 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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,7 +25,6 @@ package jdk.incubator.http.internal.hpack; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.ByteBuffer; import static java.lang.String.format; @@ -51,16 +50,18 @@ public final class Huffman { reset(); } - public void read(ByteBuffer source, Appendable destination, - boolean isLast) { + public void read(ByteBuffer source, + Appendable destination, + boolean isLast) throws IOException { read(source, destination, true, isLast); } // Takes 'isLast' rather than returns whether the reading is done or // not, for more informative exceptions. - void read(ByteBuffer source, Appendable destination, boolean reportEOS, - boolean isLast) { - + void read(ByteBuffer source, + Appendable destination, + boolean reportEOS, /* reportEOS is exposed for tests */ + boolean isLast) throws IOException { Node c = curr; int l = len; /* @@ -77,16 +78,20 @@ public final class Huffman { l++; if (c.isLeaf()) { if (reportEOS && c.isEOSPath) { - throw new IllegalArgumentException("Encountered EOS"); + throw new IOException("Encountered EOS"); + } + char ch; + try { + ch = c.getChar(); + } catch (IllegalStateException e) { + source.position(pos); // do we need this? + throw new IOException(e); } try { - destination.append(c.getChar()); - } catch (RuntimeException | Error e) { - source.position(pos); - throw e; + destination.append(ch); } catch (IOException e) { - source.position(pos); - throw new UncheckedIOException(e); + source.position(pos); // do we need this? + throw e; } c = INSTANCE.root; l = 0; @@ -107,11 +112,11 @@ public final class Huffman { return; // it's ok, some extra padding bits } if (c.isEOSPath) { - throw new IllegalArgumentException( + throw new IOException( "Padding is too long (len=" + len + ") " + "or unexpected end of data"); } - throw new IllegalArgumentException( + throw new IOException( "Not a EOS prefix padding or unexpected end of data"); } @@ -509,8 +514,8 @@ public final class Huffman { * @throws NullPointerException * if the value is null * @throws IndexOutOfBoundsException - * if any invocation of {@code value.charAt(i)}, where {@code start - * <= i < end} would throw an IndexOutOfBoundsException + * if any invocation of {@code value.charAt(i)}, where + * {@code start <= i < end} would throw an IndexOutOfBoundsException */ public int lengthOf(CharSequence value, int start, int end) { int len = 0; diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java index c2ddc54062d..dbbd22a5762 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,7 +25,6 @@ package jdk.incubator.http.internal.hpack; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.ByteBuffer; // @@ -47,14 +46,15 @@ final class ISO_8859_1 { public static final class Reader { - public void read(ByteBuffer source, Appendable destination) { + public void read(ByteBuffer source, Appendable destination) + throws IOException { for (int i = 0, len = source.remaining(); i < len; i++) { char c = (char) (source.get() & 0xff); try { destination.append(c); } catch (IOException e) { - throw new UncheckedIOException - ("Error appending to the destination", e); + throw new IOException( + "Error appending to the destination", e); } } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java index 1f9062824f0..7369df25f77 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -73,7 +73,8 @@ abstract class IndexNameValueWriter implements BinaryRepresentationWriter { return false; } } else { - if (!intWriter.write(destination) || !nameWriter.write(destination)) { + if (!intWriter.write(destination) || + !nameWriter.write(destination)) { return false; } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java index 2605355a31a..e1a8df59376 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java index ff59963a8ff..ff0e9a1a773 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -24,6 +24,7 @@ */ package jdk.incubator.http.internal.hpack; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -73,7 +74,7 @@ final class IntegerReader { return this; } - public boolean read(ByteBuffer input) { + public boolean read(ByteBuffer input) throws IOException { if (state == NEW) { throw new IllegalStateException("Configure first"); } @@ -105,7 +106,7 @@ final class IntegerReader { i = input.get(); long increment = b * (i & 127); if (r + increment > maxValue) { - throw new IllegalArgumentException(format( + throw new IOException(format( "Integer overflow: maxValue=%,d, value=%,d", maxValue, r + increment)); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java index 783a31b88af..33253730a5a 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java index 245df4807e9..b2a7e15b39d 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java index 518eb69c461..d1145eda792 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java index 1a7b31bf538..f04866b99a2 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java index a61bc1cc169..0c7895c40e7 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java index 61cb392d022..68d3bfb3efb 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -24,6 +24,7 @@ */ package jdk.incubator.http.internal.hpack; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @@ -51,7 +52,7 @@ final class StringReader { private boolean huffman; private int remainingLength; - boolean read(ByteBuffer input, Appendable output) { + boolean read(ByteBuffer input, Appendable output) throws IOException { if (state == DONE) { return true; } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java index f18eb5dc81d..a9c1421fdd9 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -62,7 +62,9 @@ final class StringWriter { return configure(input, 0, input.length(), huffman); } - StringWriter configure(CharSequence input, int start, int end, + StringWriter configure(CharSequence input, + int start, + int end, boolean huffman) { if (start < 0 || end < 0 || end > input.length() || start > end) { throw new IndexOutOfBoundsException( diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java index c6a2cf1c9e1..094904aa581 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java index 7f662922bc0..07a9406c15f 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java @@ -31,11 +31,13 @@ import jdk.incubator.http.WebSocket.Builder; import jdk.incubator.http.WebSocket.Listener; import jdk.incubator.http.internal.common.Pair; +import java.net.ProxySelector; import java.net.URI; import java.time.Duration; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import static java.util.Objects.requireNonNull; @@ -44,16 +46,37 @@ import static jdk.incubator.http.internal.common.Pair.pair; public final class BuilderImpl implements Builder { private final HttpClient client; - private final URI uri; - private final Listener listener; - private final Collection<Pair<String, String>> headers = new LinkedList<>(); - private final Collection<String> subprotocols = new LinkedList<>(); + private URI uri; + private Listener listener; + private final Optional<ProxySelector> proxySelector; + private final Collection<Pair<String, String>> headers; + private final Collection<String> subprotocols; private Duration timeout; - public BuilderImpl(HttpClient client, URI uri, Listener listener) { - this.client = requireNonNull(client, "client"); - this.uri = requireNonNull(uri, "uri"); - this.listener = requireNonNull(listener, "listener"); + public BuilderImpl(HttpClient client, ProxySelector proxySelector) + { + this(client, null, null, Optional.ofNullable(proxySelector), + new LinkedList<>(), new LinkedList<>(), null); + } + + private BuilderImpl(HttpClient client, + URI uri, + Listener listener, + Optional<ProxySelector> proxySelector, + Collection<Pair<String, String>> headers, + Collection<String> subprotocols, + Duration timeout) { + this.client = client; + this.uri = uri; + this.listener = listener; + this.proxySelector = proxySelector; + // If a proxy selector was supplied by the user, it should be present + // on the client and should be the same that what we got as an argument + assert !client.proxy().isPresent() + || client.proxy().equals(proxySelector); + this.headers = headers; + this.subprotocols = subprotocols; + this.timeout = timeout; } @Override @@ -65,8 +88,7 @@ public final class BuilderImpl implements Builder { } @Override - public Builder subprotocols(String mostPreferred, - String... lesserPreferred) + public Builder subprotocols(String mostPreferred, String... lesserPreferred) { requireNonNull(mostPreferred, "mostPreferred"); requireNonNull(lesserPreferred, "lesserPreferred"); @@ -89,8 +111,13 @@ public final class BuilderImpl implements Builder { } @Override - public CompletableFuture<WebSocket> buildAsync() { - return WebSocketImpl.newInstanceAsync(this); + public CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener) { + this.uri = requireNonNull(uri, "uri"); + this.listener = requireNonNull(listener, "listener"); + // A snapshot of builder inaccessible for further modification + // from the outside + BuilderImpl copy = immutableCopy(); + return WebSocketImpl.newInstanceAsync(copy); } HttpClient getClient() { return client; } @@ -104,4 +131,19 @@ public final class BuilderImpl implements Builder { Collection<String> getSubprotocols() { return subprotocols; } Duration getConnectTimeout() { return timeout; } + + Optional<ProxySelector> getProxySelector() { return proxySelector; } + + private BuilderImpl immutableCopy() { + @SuppressWarnings({"unchecked", "rawtypes"}) + BuilderImpl copy = new BuilderImpl( + client, + uri, + listener, + proxySelector, + List.of(this.headers.toArray(new Pair[0])), + List.of(this.subprotocols.toArray(new String[0])), + timeout); + return copy; + } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java index cec8fcc331c..ff1fa2e23d0 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java deleted file mode 100644 index 1ccec2d4e8a..00000000000 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2016, 2017, 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. 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. - */ - -package jdk.incubator.http.internal.websocket; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import static java.util.Objects.requireNonNull; - -/* - * A synchronization aid that assists a number of parties in running a task - * in a mutually exclusive fashion. - * - * To run the task, a party invokes `handle`. To permanently prevent the task - * from subsequent runs, the party invokes `stop`. - * - * The parties do not have to operate in different threads. - * - * The task can be either synchronous or asynchronous. - * - * If the task is synchronous, it is represented with `Runnable`. - * The handler invokes `Runnable.run` to run the task. - * - * If the task is asynchronous, it is represented with `Consumer<Runnable>`. - * The handler invokes `Consumer.accept(end)` to begin the task. The task - * invokes `end.run()` when it has ended. - * - * The next run of the task will not begin until the previous run has finished. - * - * The task may invoke `handle()` by itself, it's a normal situation. - */ -public final class CooperativeHandler { - - /* - Since the task is fixed and known beforehand, no blocking synchronization - (locks, queues, etc.) is required. The job can be done solely using - nonblocking primitives. - - The machinery below addresses two problems: - - 1. Running the task in a sequential order (no concurrent runs): - - begin, end, begin, end... - - 2. Avoiding indefinite recursion: - - begin - end - begin - end - ... - - Problem #1 is solved with a finite state machine with 4 states: - - BEGIN, AGAIN, END, and STOP. - - Problem #2 is solved with a "state modifier" OFFLOAD. - - Parties invoke `handle()` to signal the task must run. A party that has - invoked `handle()` either begins the task or exploits the party that is - either beginning the task or ending it. - - The party that is trying to end the task either ends it or begins it - again. - - To avoid indefinite recursion, before re-running the task tryEnd() sets - OFFLOAD bit, signalling to its "child" tryEnd() that this ("parent") - tryEnd() is available and the "child" must offload the task on to the - "parent". Then a race begins. Whichever invocation of tryEnd() manages - to unset OFFLOAD bit first does not do the work. - - There is at most 1 thread that is beginning the task and at most 2 - threads that are trying to end it: "parent" and "child". In case of a - synchronous task "parent" and "child" are the same thread. - */ - - private static final int OFFLOAD = 1; - private static final int AGAIN = 2; - private static final int BEGIN = 4; - private static final int STOP = 8; - private static final int END = 16; - - private final AtomicInteger state = new AtomicInteger(END); - private final Consumer<Runnable> begin; - - public CooperativeHandler(Runnable task) { - this(asyncOf(task)); - } - - public CooperativeHandler(Consumer<Runnable> begin) { - this.begin = requireNonNull(begin); - } - - /* - * Runs the task (though maybe by a different party). - * - * The recursion which is possible here will have the maximum depth of 1: - * - * this.handle() - * begin.accept() - * this.handle() - */ - public void handle() { - while (true) { - int s = state.get(); - if (s == END) { - if (state.compareAndSet(END, BEGIN)) { - break; - } - } else if ((s & BEGIN) != 0) { - // Tries to change the state to AGAIN, preserving OFFLOAD bit - if (state.compareAndSet(s, AGAIN | (s & OFFLOAD))) { - return; - } - } else if ((s & AGAIN) != 0 || s == STOP) { - return; - } else { - throw new InternalError(String.valueOf(s)); - } - } - begin.accept(this::tryEnd); - } - - private void tryEnd() { - while (true) { - int s; - while (((s = state.get()) & OFFLOAD) != 0) { - // Tries to offload ending of the task to the parent - if (state.compareAndSet(s, s & ~OFFLOAD)) { - return; - } - } - while (true) { - if (s == BEGIN) { - if (state.compareAndSet(BEGIN, END)) { - return; - } - } else if (s == AGAIN) { - if (state.compareAndSet(AGAIN, BEGIN | OFFLOAD)) { - break; - } - } else if (s == STOP) { - return; - } else { - throw new InternalError(String.valueOf(s)); - } - s = state.get(); - } - begin.accept(this::tryEnd); - } - } - - /* - * Checks whether or not this handler has been permanently stopped. - * - * Should be used from inside the task to poll the status of the handler, - * pretty much the same way as it is done for threads: - * - * if (!Thread.currentThread().isInterrupted()) { - * ... - * } - */ - public boolean isStopped() { - return state.get() == STOP; - } - - /* - * Signals this handler to ignore subsequent invocations to `handle()`. - * - * If the task has already begun, this invocation will not affect it, - * unless the task itself uses `isStopped()` method to check the state - * of the handler. - */ - public void stop() { - state.set(STOP); - } - - private static Consumer<Runnable> asyncOf(Runnable task) { - requireNonNull(task); - return ender -> { - task.run(); - ender.run(); - }; - } -} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java index aa4f7cf6a03..a66bab23228 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java index 1fe63664499..33f42622aad 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java @@ -184,7 +184,7 @@ class FrameConsumer implements Frame.Consumer { part = determinePart(isLast); boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT; if (!text) { - output.onBinary(part, data.slice()); + output.onBinary(data.slice(), part); data.position(data.limit()); // Consume } else { boolean binaryNonEmpty = data.hasRemaining(); @@ -199,7 +199,7 @@ class FrameConsumer implements Frame.Consumer { if (!(binaryNonEmpty && !textData.hasRemaining())) { // If there's a binary data, that result in no text, then we // don't deliver anything - output.onText(part, textData); + output.onText(textData, part); } } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java index e93740afa52..1015dd967a5 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -34,9 +34,9 @@ import java.nio.ByteBuffer; */ interface MessageStreamConsumer { - void onText(MessagePart part, CharSequence data); + void onText(CharSequence data, MessagePart part); - void onBinary(MessagePart part, ByteBuffer data); + void onBinary(ByteBuffer data, MessagePart part); void onPing(ByteBuffer data); @@ -44,11 +44,11 @@ interface MessageStreamConsumer { void onClose(int statusCode, CharSequence reason); - void onError(Exception e); - /* * Indicates the end of stream has been reached and there will be no further * messages. */ void onComplete(); + + void onError(Throwable e); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java index 4af7771d8c2..183ba5ecb1d 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java @@ -25,11 +25,6 @@ package jdk.incubator.http.internal.websocket; -import jdk.incubator.http.internal.common.MinimalFuture; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpClient.Version; import jdk.incubator.http.HttpHeaders; @@ -37,11 +32,22 @@ import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import jdk.incubator.http.HttpResponse.BodyHandler; import jdk.incubator.http.WebSocketHandshakeException; +import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Pair; +import jdk.incubator.http.internal.common.Utils; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLPermission; import java.nio.charset.StandardCharsets; +import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; import java.security.SecureRandom; import java.time.Duration; import java.util.Base64; @@ -54,12 +60,14 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.lang.String.format; import static jdk.incubator.http.internal.common.Utils.isValidName; +import static jdk.incubator.http.internal.common.Utils.permissionForProxy; import static jdk.incubator.http.internal.common.Utils.stringOf; -final class OpeningHandshake { +public class OpeningHandshake { private static final String HEADER_CONNECTION = "Connection"; private static final String HEADER_UPGRADE = "Upgrade"; @@ -68,19 +76,20 @@ final class OpeningHandshake { private static final String HEADER_KEY = "Sec-WebSocket-Key"; private static final String HEADER_PROTOCOL = "Sec-WebSocket-Protocol"; private static final String HEADER_VERSION = "Sec-WebSocket-Version"; + private static final String VERSION = "13"; // WebSocket's lucky number - private static final Set<String> FORBIDDEN_HEADERS; + private static final Set<String> ILLEGAL_HEADERS; static { - FORBIDDEN_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - FORBIDDEN_HEADERS.addAll(List.of(HEADER_ACCEPT, - HEADER_EXTENSIONS, - HEADER_KEY, - HEADER_PROTOCOL, - HEADER_VERSION)); + ILLEGAL_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + ILLEGAL_HEADERS.addAll(List.of(HEADER_ACCEPT, + HEADER_EXTENSIONS, + HEADER_KEY, + HEADER_PROTOCOL, + HEADER_VERSION)); } - private static final SecureRandom srandom = new SecureRandom(); + private static final SecureRandom random = new SecureRandom(); private final MessageDigest sha1; private final HttpClient client; @@ -99,7 +108,10 @@ final class OpeningHandshake { private final Collection<String> subprotocols; private final String nonce; - OpeningHandshake(BuilderImpl b) { + public OpeningHandshake(BuilderImpl b) { + checkURI(b.getUri()); + Proxy proxy = proxyFor(b.getProxySelector(), b.getUri()); + checkPermissions(b, proxy); this.client = b.getClient(); URI httpURI = createRequestURI(b.getUri()); HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(httpURI); @@ -108,7 +120,7 @@ final class OpeningHandshake { requestBuilder.timeout(connectTimeout); } for (Pair<String, String> p : b.getHeaders()) { - if (FORBIDDEN_HEADERS.contains(p.first)) { + if (ILLEGAL_HEADERS.contains(p.first)) { throw illegal("Illegal header: " + p.first); } requestBuilder.header(p.first, p.second); @@ -118,7 +130,7 @@ final class OpeningHandshake { String p = this.subprotocols.stream().collect(Collectors.joining(", ")); requestBuilder.header(HEADER_PROTOCOL, p); } - requestBuilder.header(HEADER_VERSION, "13"); // WebSocket's lucky number + requestBuilder.header(HEADER_VERSION, VERSION); this.nonce = createNonce(); requestBuilder.header(HEADER_KEY, this.nonce); // Setting request version to HTTP/1.1 forcibly, since it's not possible @@ -130,6 +142,7 @@ final class OpeningHandshake { r.isWebSocket(true); r.setSystemHeader(HEADER_UPGRADE, "websocket"); r.setSystemHeader(HEADER_CONNECTION, "Upgrade"); + r.setProxy(proxy); } private static Collection<String> createRequestSubprotocols( @@ -153,15 +166,9 @@ final class OpeningHandshake { * * https://tools.ietf.org/html/rfc6455#section-3 */ - private static URI createRequestURI(URI uri) { - // TODO: check permission for WebSocket URI and translate it into - // http/https permission - String s = uri.getScheme(); // The scheme might be null (i.e. undefined) - if (!("ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s)) - || uri.getFragment() != null) - { - throw illegal("Bad URI: " + uri); - } + static URI createRequestURI(URI uri) { + String s = uri.getScheme(); + assert "ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s); String scheme = "ws".equalsIgnoreCase(s) ? "http" : "https"; try { return new URI(scheme, @@ -177,9 +184,11 @@ final class OpeningHandshake { } } - CompletableFuture<Result> send() { - return client.sendAsync(this.request, BodyHandler.<Void>discard(null)) - .thenCompose(this::resultFrom); + public CompletableFuture<Result> send() { + PrivilegedAction<CompletableFuture<Result>> pa = () -> + client.sendAsync(this.request, BodyHandler.<Void>discard(null)) + .thenCompose(this::resultFrom); + return AccessController.doPrivileged(pa); } /* @@ -188,11 +197,11 @@ final class OpeningHandshake { static final class Result { final String subprotocol; - final RawChannel channel; + final TransportSupplier transport; - private Result(String subprotocol, RawChannel channel) { + private Result(String subprotocol, TransportSupplier transport) { this.subprotocol = subprotocol; - this.channel = channel; + this.transport = transport; } } @@ -240,7 +249,10 @@ final class OpeningHandshake { if (!connection.equalsIgnoreCase("Upgrade")) { throw checkFailed("Bad response field: " + HEADER_CONNECTION); } - requireAbsent(headers, HEADER_VERSION); + Optional<String> version = requireAtMostOne(headers, HEADER_VERSION); + if (version.isPresent() && !version.get().equals(VERSION)) { + throw checkFailed("Bad response field: " + HEADER_VERSION); + } requireAbsent(headers, HEADER_EXTENSIONS); String x = this.nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; this.sha1.update(x.getBytes(StandardCharsets.ISO_8859_1)); @@ -251,7 +263,7 @@ final class OpeningHandshake { } String subprotocol = checkAndReturnSubprotocol(headers); RawChannel channel = ((RawChannel.Provider) response).rawChannel(); - return new Result(subprotocol, channel); + return new Result(subprotocol, new TransportSupplier(channel)); } private String checkAndReturnSubprotocol(HttpHeaders responseHeaders) @@ -284,6 +296,18 @@ final class OpeningHandshake { } } + private static Optional<String> requireAtMostOne(HttpHeaders responseHeaders, + String headerName) + { + List<String> values = responseHeaders.allValues(headerName); + if (values.size() > 1) { + throw checkFailed(format("Response field '%s' multivalued: %s", + headerName, + stringOf(values))); + } + return values.stream().findFirst(); + } + private static String requireSingle(HttpHeaders responseHeaders, String headerName) { @@ -300,15 +324,69 @@ final class OpeningHandshake { private static String createNonce() { byte[] bytes = new byte[16]; - OpeningHandshake.srandom.nextBytes(bytes); + OpeningHandshake.random.nextBytes(bytes); return Base64.getEncoder().encodeToString(bytes); } + private static CheckFailedException checkFailed(String message) { + throw new CheckFailedException(message); + } + + private static URI checkURI(URI uri) { + String scheme = uri.getScheme(); + if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme))) + throw illegal("invalid URI scheme: " + scheme); + if (uri.getHost() == null) + throw illegal("URI must contain a host: " + uri); + if (uri.getFragment() != null) + throw illegal("URI must not contain a fragment: " + uri); + return uri; + } + private static IllegalArgumentException illegal(String message) { return new IllegalArgumentException(message); } - private static CheckFailedException checkFailed(String message) { - throw new CheckFailedException(message); + /** + * Returns the proxy for the given URI when sent through the given client, + * or {@code null} if none is required or applicable. + */ + private static Proxy proxyFor(Optional<ProxySelector> selector, URI uri) { + if (!selector.isPresent()) { + return null; + } + URI requestURI = createRequestURI(uri); // Based on the HTTP scheme + List<Proxy> pl = selector.get().select(requestURI); + if (pl.isEmpty()) { + return null; + } + Proxy proxy = pl.get(0); + if (proxy.type() != Proxy.Type.HTTP) { + return null; + } + return proxy; + } + + /** + * Performs the necessary security permissions checks to connect ( possibly + * through a proxy ) to the builders WebSocket URI. + * + * @throws SecurityException if the security manager denies access + */ + static void checkPermissions(BuilderImpl b, Proxy proxy) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return; + } + Stream<String> headers = b.getHeaders().stream().map(p -> p.first).distinct(); + URLPermission perm1 = Utils.permissionForServer(b.getUri(), "", headers); + sm.checkPermission(perm1); + if (proxy == null) { + return; + } + URLPermission perm2 = permissionForProxy((InetSocketAddress) proxy.address()); + if (perm2 != null) { + sm.checkPermission(perm2); + } } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java index c1dc19f756e..ba717bca398 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java @@ -71,12 +71,13 @@ abstract class OutgoingMessage { * so it would be possible to defer the work it does until the most * convenient moment (up to the point where sentTo is invoked). */ - protected void contextualize(Context context) { + protected boolean contextualize(Context context) { // masking and charset decoding should be performed here rather than in // the constructor (as of today) if (context.isCloseSent()) { throw new IllegalStateException("Close sent"); } + return true; } protected boolean sendTo(RawChannel channel) throws IOException { @@ -115,7 +116,7 @@ abstract class OutgoingMessage { } @Override - protected void contextualize(Context context) { + protected boolean contextualize(Context context) { super.contextualize(context); if (context.isPreviousBinary() && !context.isPreviousLast()) { throw new IllegalStateException("Unexpected text message"); @@ -125,6 +126,7 @@ abstract class OutgoingMessage { context.setPreviousBinary(false); context.setPreviousText(true); context.setPreviousLast(isLast); + return true; } } @@ -139,7 +141,7 @@ abstract class OutgoingMessage { } @Override - protected void contextualize(Context context) { + protected boolean contextualize(Context context) { super.contextualize(context); if (context.isPreviousText() && !context.isPreviousLast()) { throw new IllegalStateException("Unexpected binary message"); @@ -150,6 +152,7 @@ abstract class OutgoingMessage { context.setPreviousText(false); context.setPreviousBinary(true); context.setPreviousLast(isLast); + return true; } } @@ -195,9 +198,13 @@ abstract class OutgoingMessage { } @Override - protected void contextualize(Context context) { - super.contextualize(context); - context.setCloseSent(); + protected boolean contextualize(Context context) { + if (context.isCloseSent()) { + return false; + } else { + context.setCloseSent(); + return true; + } } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java index 7a749645242..d93106353cf 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java index 8812d442794..d9371c58ac2 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java @@ -25,10 +25,12 @@ package jdk.incubator.http.internal.websocket; +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.SequentialScheduler; + import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; -import java.util.concurrent.atomic.AtomicLong; /* * Receives incoming data from the channel on demand and converts it into a @@ -49,15 +51,15 @@ import java.util.concurrent.atomic.AtomicLong; * * even if `request(long n)` is called from inside these invocations. */ -final class Receiver { +public class Receiver { private final MessageStreamConsumer messageConsumer; private final RawChannel channel; private final FrameConsumer frameConsumer; private final Frame.Reader reader = new Frame.Reader(); private final RawChannel.RawEvent event = createHandler(); - private final AtomicLong demand = new AtomicLong(); - private final CooperativeHandler handler; + protected final Demand demand = new Demand(); /* Exposed for testing purposes */ + private final SequentialScheduler pushScheduler; private ByteBuffer data; private volatile int state; @@ -66,7 +68,7 @@ final class Receiver { private static final int AVAILABLE = 1; private static final int WAITING = 2; - Receiver(MessageStreamConsumer messageConsumer, RawChannel channel) { + public Receiver(MessageStreamConsumer messageConsumer, RawChannel channel) { this.messageConsumer = messageConsumer; this.channel = channel; this.frameConsumer = new FrameConsumer(this.messageConsumer); @@ -74,7 +76,12 @@ final class Receiver { // To ensure the initial non-final `data` will be visible // (happens-before) when `handler` invokes `pushContinuously` // the following assignment is done last: - handler = new CooperativeHandler(this::pushContinuously); + pushScheduler = createScheduler(); + } + + /* Exposed for testing purposes */ + protected SequentialScheduler createScheduler() { + return new SequentialScheduler(new PushContinuouslyTask()); } private RawChannel.RawEvent createHandler() { @@ -88,21 +95,25 @@ final class Receiver { @Override public void handle() { state = AVAILABLE; - handler.handle(); + pushScheduler.runOrSchedule(); } }; } - void request(long n) { - if (n < 0L) { - throw new IllegalArgumentException("Negative: " + n); + public void request(long n) { + if (demand.increase(n)) { + pushScheduler.runOrSchedule(); } - demand.accumulateAndGet(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i); - handler.handle(); } + /* + * Why is this method needed? Since Receiver operates through callbacks + * this method allows to abstract out what constitutes as a message being + * received (i.e. to decide outside this type when exactly one should + * decrement the demand). + */ void acknowledge() { - long x = demand.decrementAndGet(); + long x = demand.decreaseAndGet(1); if (x < 0) { throw new InternalError(String.valueOf(x)); } @@ -112,61 +123,66 @@ final class Receiver { * Stops the machinery from reading and delivering messages permanently, * regardless of the current demand and data availability. */ - void close() { - handler.stop(); + public void close() throws IOException { + pushScheduler.stop(); + channel.shutdownInput(); } - private void pushContinuously() { - while (!handler.isStopped()) { - if (data.hasRemaining()) { - if (demand.get() > 0) { - try { - int oldPos = data.position(); - reader.readFrame(data, frameConsumer); - int newPos = data.position(); - assert oldPos != newPos : data; // reader always consumes bytes - } catch (FailWebSocketException e) { - handler.stop(); - messageConsumer.onError(e); + private class PushContinuouslyTask + extends SequentialScheduler.CompleteRestartableTask + { + @Override + public void run() { + while (!pushScheduler.isStopped()) { + if (data.hasRemaining()) { + if (!demand.isFulfilled()) { + try { + int oldPos = data.position(); + reader.readFrame(data, frameConsumer); + int newPos = data.position(); + assert oldPos != newPos : data; // reader always consumes bytes + } catch (Throwable e) { + pushScheduler.stop(); + messageConsumer.onError(e); + } + continue; } - continue; + break; } - break; - } - switch (state) { - case WAITING: - return; - case UNREGISTERED: - try { - state = WAITING; - channel.registerEvent(event); - } catch (IOException e) { - handler.stop(); - messageConsumer.onError(e); - } - return; - case AVAILABLE: - try { - data = channel.read(); - } catch (IOException e) { - handler.stop(); - messageConsumer.onError(e); + switch (state) { + case WAITING: return; - } - if (data == null) { // EOF - handler.stop(); - messageConsumer.onComplete(); + case UNREGISTERED: + try { + state = WAITING; + channel.registerEvent(event); + } catch (Throwable e) { + pushScheduler.stop(); + messageConsumer.onError(e); + } return; - } else if (!data.hasRemaining()) { // No data at the moment - // Pretty much a "goto", reusing the existing code path - // for registration - state = UNREGISTERED; - } - continue; - default: - throw new InternalError(String.valueOf(state)); + case AVAILABLE: + try { + data = channel.read(); + } catch (Throwable e) { + pushScheduler.stop(); + messageConsumer.onError(e); + return; + } + if (data == null) { // EOF + pushScheduler.stop(); + messageConsumer.onComplete(); + return; + } else if (!data.hasRemaining()) { // No data at the moment + // Pretty much a "goto", reusing the existing code path + // for registration + state = UNREGISTERED; + } + continue; + default: + throw new InternalError(String.valueOf(state)); + } } } } } - diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java index 4991515cbb6..e15374eb1f9 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java @@ -40,7 +40,7 @@ import static java.util.Objects.requireNonNull; * to accept a new message. Until then, the transmitter is considered "busy" and * an IllegalStateException will be thrown on each attempt to invoke send. */ -final class Transmitter { +public class Transmitter { /* This flag is used solely for assertions */ private final AtomicBoolean busy = new AtomicBoolean(); @@ -49,8 +49,8 @@ final class Transmitter { private final RawChannel channel; private final RawChannel.RawEvent event; - Transmitter(RawChannel channel) { - this.channel = requireNonNull(channel); + public Transmitter(RawChannel channel) { + this.channel = channel; this.event = createHandler(); } @@ -59,7 +59,9 @@ final class Transmitter { * A {@code StackOverflowError} may thus occur if there's a possibility * that this method is called again by the supplied handler. */ - void send(OutgoingMessage message, Consumer<Exception> completionHandler) { + public void send(OutgoingMessage message, + Consumer<Exception> completionHandler) + { requireNonNull(message); requireNonNull(completionHandler); if (!busy.compareAndSet(false, true)) { @@ -68,6 +70,10 @@ final class Transmitter { send0(message, completionHandler); } + public void close() throws IOException { + channel.shutdownOutput(); + } + private RawChannel.RawEvent createHandler() { return new RawChannel.RawEvent() { diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java new file mode 100644 index 00000000000..d433a208d9a --- /dev/null +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package jdk.incubator.http.internal.websocket; + +import java.io.IOException; + +/* + * Abstracts out I/O channel for the WebSocket implementation. The latter then + * deals with input and output streams of messages and does not have to + * understand the state machine of channels (e.g. how exactly they are closed). + * Mocking this type will allow testing WebSocket message exchange in isolation. + */ +public class TransportSupplier { + + protected final RawChannel channel; /* Exposed for testing purposes */ + private final Object lock = new Object(); + private Transmitter transmitter; + private Receiver receiver; + private boolean receiverShutdown; + private boolean transmitterShutdown; + private boolean closed; + + public TransportSupplier(RawChannel channel) { + this.channel = channel; + } + + public Receiver receiver(MessageStreamConsumer consumer) { + synchronized (lock) { + if (receiver == null) { + receiver = newReceiver(consumer); + } + return receiver; + } + } + + public Transmitter transmitter() { + synchronized (lock) { + if (transmitter == null) { + transmitter = newTransmitter(); + } + return transmitter; + } + } + + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new Receiver(consumer, channel) { + @Override + public void close() throws IOException { + synchronized (lock) { + if (!closed) { + try { + super.close(); + } finally { + receiverShutdown = true; + if (transmitterShutdown) { + closed = true; + channel.close(); + } + } + } + } + } + }; + } + + protected Transmitter newTransmitter() { + return new Transmitter(channel) { + @Override + public void close() throws IOException { + synchronized (lock) { + if (!closed) { + try { + super.close(); + } finally { + transmitterShutdown = true; + if (receiverShutdown) { + closed = true; + channel.close(); + } + } + } + } + } + }; + } +} diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java index 0a166ccf288..81485783b2f 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java index 74d1445fc29..043b97eff66 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java @@ -26,8 +26,13 @@ package jdk.incubator.http.internal.websocket; import jdk.incubator.http.WebSocket; +import jdk.incubator.http.internal.common.Demand; import jdk.incubator.http.internal.common.Log; +import jdk.incubator.http.internal.common.MinimalFuture; import jdk.incubator.http.internal.common.Pair; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter; +import jdk.incubator.http.internal.common.Utils; import jdk.incubator.http.internal.websocket.OpeningHandshake.Result; import jdk.incubator.http.internal.websocket.OutgoingMessage.Binary; import jdk.incubator.http.internal.websocket.OutgoingMessage.Close; @@ -37,6 +42,7 @@ import jdk.incubator.http.internal.websocket.OutgoingMessage.Pong; import jdk.incubator.http.internal.websocket.OutgoingMessage.Text; import java.io.IOException; +import java.lang.ref.Reference; import java.net.ProtocolException; import java.net.URI; import java.nio.ByteBuffer; @@ -44,229 +50,140 @@ import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.CompletableFuture.failedFuture; +import static jdk.incubator.http.internal.common.MinimalFuture.failedFuture; import static jdk.incubator.http.internal.common.Pair.pair; import static jdk.incubator.http.internal.websocket.StatusCodes.CLOSED_ABNORMALLY; import static jdk.incubator.http.internal.websocket.StatusCodes.NO_STATUS_CODE; import static jdk.incubator.http.internal.websocket.StatusCodes.isLegalToSendFromClient; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.BINARY; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.CLOSE; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.ERROR; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.IDLE; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.OPEN; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.PING; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.PONG; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.TEXT; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.WAITING; /* * A WebSocket client. */ -final class WebSocketImpl implements WebSocket { +public final class WebSocketImpl implements WebSocket { + + enum State { + OPEN, + IDLE, + WAITING, + TEXT, + BINARY, + PING, + PONG, + CLOSE, + ERROR; + } + + private volatile boolean inputClosed; + private volatile boolean outputClosed; + + private final AtomicReference<State> state = new AtomicReference<>(OPEN); + + /* Components of calls to Listener's methods */ + private MessagePart part; + private ByteBuffer binaryData; + private CharSequence text; + private int statusCode; + private String reason; + private final AtomicReference<Throwable> error = new AtomicReference<>(); private final URI uri; private final String subprotocol; - private final RawChannel channel; private final Listener listener; - /* - * Whether or not Listener.onClose or Listener.onError has been already - * invoked. We keep track of this since only one of these methods is invoked - * and it is invoked at most once. - */ - private boolean lastMethodInvoked; private final AtomicBoolean outstandingSend = new AtomicBoolean(); - private final CooperativeHandler sendHandler = - new CooperativeHandler(this::sendFirst); + private final SequentialScheduler sendScheduler = new SequentialScheduler(new SendTask()); private final Queue<Pair<OutgoingMessage, CompletableFuture<WebSocket>>> queue = new ConcurrentLinkedQueue<>(); private final Context context = new OutgoingMessage.Context(); private final Transmitter transmitter; private final Receiver receiver; + private final SequentialScheduler receiveScheduler = new SequentialScheduler(new ReceiveTask()); + private final Demand demand = new Demand(); - /* - * Whether or not the WebSocket has been closed. When a WebSocket has been - * closed it means that no further messages can be sent or received. - * A closure can be triggered by: - * - * 1. abort() - * 2. "Failing the WebSocket Connection" (i.e. a fatal error) - * 3. Completion of the Closing handshake - */ - private final AtomicBoolean closed = new AtomicBoolean(); - - /* - * This lock is enforcing sequential ordering of invocations to listener's - * methods. It is supposed to be uncontended. The only contention that can - * happen is when onOpen, an asynchronous onError (not related to reading - * from the channel, e.g. an error from automatic Pong reply) or onClose - * (related to abort) happens. Since all of the above are one-shot actions, - * the said contention is insignificant. - */ - private final Object lock = new Object(); - - private final CompletableFuture<?> closeReceived = new CompletableFuture<>(); - private final CompletableFuture<?> closeSent = new CompletableFuture<>(); - - static CompletableFuture<WebSocket> newInstanceAsync(BuilderImpl b) { + public static CompletableFuture<WebSocket> newInstanceAsync(BuilderImpl b) { Function<Result, WebSocket> newWebSocket = r -> { - WebSocketImpl ws = new WebSocketImpl(b.getUri(), - r.subprotocol, - r.channel, - b.getListener()); - // The order of calls might cause a subtle effects, like CF will be - // returned from the buildAsync _after_ onOpen has been signalled. - // This means if onOpen is lengthy, it might cause some problems. - ws.signalOpen(); + WebSocket ws = newInstance(b.getUri(), + r.subprotocol, + b.getListener(), + r.transport); + // Make sure we don't release the builder until this lambda + // has been executed. The builder has a strong reference to + // the HttpClientFacade, and we want to keep that live until + // after the raw channel is created and passed to WebSocketImpl. + Reference.reachabilityFence(b); return ws; }; OpeningHandshake h; try { h = new OpeningHandshake(b); - } catch (IllegalArgumentException e) { + } catch (Throwable e) { return failedFuture(e); } return h.send().thenApply(newWebSocket); } - WebSocketImpl(URI uri, - String subprotocol, - RawChannel channel, - Listener listener) - { + /* Exposed for testing purposes */ + static WebSocket newInstance(URI uri, + String subprotocol, + Listener listener, + TransportSupplier transport) { + WebSocketImpl ws = new WebSocketImpl(uri, subprotocol, listener, transport); + // This initialisation is outside of the constructor for the sake of + // safe publication of WebSocketImpl.this + ws.signalOpen(); + return ws; + } + + private WebSocketImpl(URI uri, + String subprotocol, + Listener listener, + TransportSupplier transport) { this.uri = requireNonNull(uri); this.subprotocol = requireNonNull(subprotocol); - this.channel = requireNonNull(channel); this.listener = requireNonNull(listener); - this.transmitter = new Transmitter(channel); - this.receiver = new Receiver(messageConsumerOf(listener), channel); - - // Set up the Closing Handshake action - CompletableFuture.allOf(closeReceived, closeSent) - .whenComplete((result, error) -> { - try { - channel.close(); - } catch (IOException e) { - Log.logError(e); - } finally { - closed.set(true); - } - }); - } - - /* - * This initialisation is outside of the constructor for the sake of - * safe publication. - */ - private void signalOpen() { - synchronized (lock) { - // TODO: might hold lock longer than needed causing prolonged - // contention? substitute lock for ConcurrentLinkedQueue<Runnable>? - try { - listener.onOpen(this); - } catch (Exception e) { - signalError(e); - } - } - } - - private void signalError(Throwable error) { - synchronized (lock) { - if (lastMethodInvoked) { - Log.logError(error); - } else { - lastMethodInvoked = true; - receiver.close(); - try { - listener.onError(this, error); - } catch (Exception e) { - Log.logError(e); - } - } - } - } - - /* - * Processes a Close event that came from the channel. Invoked at most once. - */ - private void processClose(int statusCode, String reason) { - receiver.close(); - try { - channel.shutdownInput(); - } catch (IOException e) { - Log.logError(e); - } - boolean alreadyCompleted = !closeReceived.complete(null); - if (alreadyCompleted) { - // This CF is supposed to be completed only once, the first time a - // Close message is received. No further messages are pulled from - // the socket. - throw new InternalError(); - } - int code; - if (statusCode == NO_STATUS_CODE || statusCode == CLOSED_ABNORMALLY) { - code = NORMAL_CLOSURE; - } else { - code = statusCode; - } - CompletionStage<?> readyToClose = signalClose(statusCode, reason); - if (readyToClose == null) { - readyToClose = CompletableFuture.completedFuture(null); - } - readyToClose.whenComplete((r, error) -> { - enqueueClose(new Close(code, "")) - .whenComplete((r1, error1) -> { - if (error1 != null) { - Log.logError(error1); - } - }); - }); - } - - /* - * Signals a Close event (might not correspond to anything happened on the - * channel, e.g. `abort()`). - */ - private CompletionStage<?> signalClose(int statusCode, String reason) { - synchronized (lock) { - if (lastMethodInvoked) { - Log.logTrace("Close: {0}, ''{1}''", statusCode, reason); - } else { - lastMethodInvoked = true; - receiver.close(); - try { - return listener.onClose(this, statusCode, reason); - } catch (Exception e) { - Log.logError(e); - } - } - } - return null; + this.transmitter = transport.transmitter(); + this.receiver = transport.receiver(new SignallingMessageConsumer()); } @Override - public CompletableFuture<WebSocket> sendText(CharSequence message, - boolean isLast) - { + public CompletableFuture<WebSocket> sendText(CharSequence message, boolean isLast) { return enqueueExclusively(new Text(message, isLast)); } @Override - public CompletableFuture<WebSocket> sendBinary(ByteBuffer message, - boolean isLast) - { + public CompletableFuture<WebSocket> sendBinary(ByteBuffer message, boolean isLast) { return enqueueExclusively(new Binary(message, isLast)); } @Override public CompletableFuture<WebSocket> sendPing(ByteBuffer message) { - return enqueueExclusively(new Ping(message)); + return enqueue(new Ping(message)); } @Override public CompletableFuture<WebSocket> sendPong(ByteBuffer message) { - return enqueueExclusively(new Pong(message)); + return enqueue(new Pong(message)); } @Override - public CompletableFuture<WebSocket> sendClose(int statusCode, - String reason) { + public CompletableFuture<WebSocket> sendClose(int statusCode, String reason) { if (!isLegalToSendFromClient(statusCode)) { return failedFuture( new IllegalArgumentException("statusCode: " + statusCode)); @@ -277,27 +194,33 @@ final class WebSocketImpl implements WebSocket { } catch (IllegalArgumentException e) { return failedFuture(e); } + outputClosed = true; return enqueueClose(msg); } /* - * Sends a Close message with the given contents and then shuts down the - * channel for writing since no more messages are expected to be sent after - * this. Invoked at most once. + * Sends a Close message, then shuts down the transmitter since no more + * messages are expected to be sent after this. */ private CompletableFuture<WebSocket> enqueueClose(Close m) { - return enqueue(m).whenComplete((r, error) -> { - try { - channel.shutdownOutput(); - } catch (IOException e) { - Log.logError(e); - } - boolean alreadyCompleted = !closeSent.complete(null); - if (alreadyCompleted) { - // Shouldn't happen as this callback must run at most once - throw new InternalError(); - } - }); + // TODO: MUST be a CF created once and shared across sendClose, otherwise + // a second sendClose may prematurely close the channel + return enqueue(m) + .orTimeout(60, TimeUnit.SECONDS) + .whenComplete((r, error) -> { + try { + transmitter.close(); + } catch (IOException e) { + Log.logError(e); + } + if (error instanceof TimeoutException) { + try { + receiver.close(); + } catch (IOException e) { + Log.logError(e); + } + } + }); } /* @@ -307,60 +230,75 @@ final class WebSocketImpl implements WebSocket { * completes. This method is used to enforce "one outstanding send * operation" policy. */ - private CompletableFuture<WebSocket> enqueueExclusively(OutgoingMessage m) - { - if (closed.get()) { - return failedFuture(new IllegalStateException("Closed")); - } + private CompletableFuture<WebSocket> enqueueExclusively(OutgoingMessage m) { if (!outstandingSend.compareAndSet(false, true)) { - return failedFuture(new IllegalStateException("Outstanding send")); + return failedFuture(new IllegalStateException("Send pending")); } return enqueue(m).whenComplete((r, e) -> outstandingSend.set(false)); } private CompletableFuture<WebSocket> enqueue(OutgoingMessage m) { - CompletableFuture<WebSocket> cf = new CompletableFuture<>(); + CompletableFuture<WebSocket> cf = new MinimalFuture<>(); boolean added = queue.add(pair(m, cf)); if (!added) { // The queue is supposed to be unbounded throw new InternalError(); } - sendHandler.handle(); + sendScheduler.runOrSchedule(); return cf; } /* - * This is the main sending method. It may be run in different threads, - * but never concurrently. + * This is a message sending task. It pulls messages from the queue one by + * one and sends them. It may be run in different threads, but never + * concurrently. */ - private void sendFirst(Runnable whenSent) { - Pair<OutgoingMessage, CompletableFuture<WebSocket>> p = queue.poll(); - if (p == null) { - whenSent.run(); - return; - } - OutgoingMessage message = p.first; - CompletableFuture<WebSocket> cf = p.second; - try { - message.contextualize(context); - Consumer<Exception> h = e -> { - if (e == null) { - cf.complete(WebSocketImpl.this); - } else { - cf.completeExceptionally(e); + private class SendTask implements SequentialScheduler.RestartableTask { + + @Override + public void run(DeferredCompleter taskCompleter) { + Pair<OutgoingMessage, CompletableFuture<WebSocket>> p = queue.poll(); + if (p == null) { + taskCompleter.complete(); + return; + } + OutgoingMessage message = p.first; + CompletableFuture<WebSocket> cf = p.second; + try { + if (!message.contextualize(context)) { // Do not send the message + cf.complete(null); + repeat(taskCompleter); + return; } - sendHandler.handle(); - whenSent.run(); - }; - transmitter.send(message, h); - } catch (Exception t) { - cf.completeExceptionally(t); + Consumer<Exception> h = e -> { + if (e == null) { + cf.complete(WebSocketImpl.this); + } else { + cf.completeExceptionally(e); + } + repeat(taskCompleter); + }; + transmitter.send(message, h); + } catch (Throwable t) { + cf.completeExceptionally(t); + repeat(taskCompleter); + } + } + + private void repeat(DeferredCompleter taskCompleter) { + taskCompleter.complete(); + // More than a single message may have been enqueued while + // the task has been busy with the current message, but + // there is only a single signal recorded + sendScheduler.runOrSchedule(); } } @Override public void request(long n) { - receiver.request(n); + if (demand.increase(n)) { + receiveScheduler.runOrSchedule(); + } } @Override @@ -369,137 +307,299 @@ final class WebSocketImpl implements WebSocket { } @Override - public boolean isClosed() { - return closed.get(); + public boolean isOutputClosed() { + return outputClosed; } @Override - public void abort() throws IOException { - try { - channel.close(); - } finally { - closed.set(true); - signalClose(CLOSED_ABNORMALLY, ""); - } + public boolean isInputClosed() { + return inputClosed; + } + + @Override + public void abort() { + inputClosed = true; + outputClosed = true; + receiveScheduler.stop(); + close(); } @Override public String toString() { return super.toString() - + "[" + (closed.get() ? "CLOSED" : "OPEN") + "]: " + uri - + (!subprotocol.isEmpty() ? ", subprotocol=" + subprotocol : ""); + + "[uri=" + uri + + (!subprotocol.isEmpty() ? ", subprotocol=" + subprotocol : "") + + "]"; } - private MessageStreamConsumer messageConsumerOf(Listener listener) { - // Synchronization performed here in every method is not for the sake of - // ordering invocations to this consumer, after all they are naturally - // ordered in the channel. The reason is to avoid an interference with - // any unrelated to the channel calls to onOpen, onClose and onError. - return new MessageStreamConsumer() { + /* + * The assumptions about order is as follows: + * + * - state is never changed more than twice inside the `run` method: + * x --(1)--> IDLE --(2)--> y (otherwise we're loosing events, or + * overwriting parts of messages creating a mess since there's no + * queueing) + * - OPEN is always the first state + * - no messages are requested/delivered before onOpen is called (this + * is implemented by making WebSocket instance accessible first in + * onOpen) + * - after the state has been observed as CLOSE/ERROR, the scheduler + * is stopped + */ + private class ReceiveTask extends SequentialScheduler.CompleteRestartableTask { - @Override - public void onText(MessagePart part, CharSequence data) { - receiver.acknowledge(); - synchronized (WebSocketImpl.this.lock) { - try { - listener.onText(WebSocketImpl.this, data, part); - } catch (Exception e) { - signalError(e); - } - } - } + // Receiver only asked here and nowhere else because we must make sure + // onOpen is invoked first and no messages become pending before onOpen + // finishes - @Override - public void onBinary(MessagePart part, ByteBuffer data) { - receiver.acknowledge(); - synchronized (WebSocketImpl.this.lock) { - try { - listener.onBinary(WebSocketImpl.this, data.slice(), part); - } catch (Exception e) { - signalError(e); - } - } - } - - @Override - public void onPing(ByteBuffer data) { - receiver.acknowledge(); - // Let's make a full copy of this tiny data. What we want here - // is to rule out a possibility the shared data we send might be - // corrupted the by processing in the listener. - ByteBuffer slice = data.slice(); - ByteBuffer copy = ByteBuffer.allocate(data.remaining()) - .put(data) - .flip(); - // Non-exclusive send; - CompletableFuture<WebSocket> pongSent = enqueue(new Pong(copy)); - pongSent.whenComplete( - (r, error) -> { - if (error != null) { - WebSocketImpl.this.signalError(error); + @Override + public void run() { + while (true) { + State s = state.get(); + try { + switch (s) { + case OPEN: + processOpen(); + tryChangeState(OPEN, IDLE); + break; + case TEXT: + processText(); + tryChangeState(TEXT, IDLE); + break; + case BINARY: + processBinary(); + tryChangeState(BINARY, IDLE); + break; + case PING: + processPing(); + tryChangeState(PING, IDLE); + break; + case PONG: + processPong(); + tryChangeState(PONG, IDLE); + break; + case CLOSE: + processClose(); + return; + case ERROR: + processError(); + return; + case IDLE: + if (demand.tryDecrement() + && tryChangeState(IDLE, WAITING)) { + receiver.request(1); } + return; + case WAITING: + // For debugging spurious signalling: when there was a + // signal, but apparently nothing has changed + return; + default: + throw new InternalError(String.valueOf(s)); + } + } catch (Throwable t) { + signalError(t); + } + } + } + + private void processError() throws IOException { + receiver.close(); + receiveScheduler.stop(); + Throwable err = error.get(); + if (err instanceof FailWebSocketException) { + int code1 = ((FailWebSocketException) err).getStatusCode(); + err = new ProtocolException().initCause(err); + enqueueClose(new Close(code1, "")) + .whenComplete( + (r, e) -> { + if (e != null) { + Log.logError(e); + } + }); + } + listener.onError(WebSocketImpl.this, err); + } + + private void processClose() throws IOException { + receiver.close(); + receiveScheduler.stop(); + CompletionStage<?> readyToClose; + readyToClose = listener.onClose(WebSocketImpl.this, statusCode, reason); + if (readyToClose == null) { + readyToClose = MinimalFuture.completedFuture(null); + } + int code; + if (statusCode == NO_STATUS_CODE || statusCode == CLOSED_ABNORMALLY) { + code = NORMAL_CLOSURE; + } else { + code = statusCode; + } + readyToClose.whenComplete((r, e) -> { + enqueueClose(new Close(code, "")) + .whenComplete((r1, e1) -> { + if (e1 != null) { + Log.logError(e1); + } + }); + }); + } + + private void processPong() { + listener.onPong(WebSocketImpl.this, binaryData); + } + + private void processPing() { + // Let's make a full copy of this tiny data. What we want here + // is to rule out a possibility the shared data we send might be + // corrupted by processing in the listener. + ByteBuffer slice = binaryData.slice(); + ByteBuffer copy = ByteBuffer.allocate(binaryData.remaining()) + .put(binaryData) + .flip(); + // Non-exclusive send; + CompletableFuture<WebSocket> pongSent = enqueue(new Pong(copy)); + pongSent.whenComplete( + (r, e) -> { + if (e != null) { + signalError(Utils.getCompletionCause(e)); } - ); - synchronized (WebSocketImpl.this.lock) { - try { - listener.onPing(WebSocketImpl.this, slice); - } catch (Exception e) { - signalError(e); } - } - } + ); + listener.onPing(WebSocketImpl.this, slice); + } - @Override - public void onPong(ByteBuffer data) { - receiver.acknowledge(); - synchronized (WebSocketImpl.this.lock) { - try { - listener.onPong(WebSocketImpl.this, data.slice()); - } catch (Exception e) { - signalError(e); - } - } - } + private void processBinary() { + listener.onBinary(WebSocketImpl.this, binaryData, part); + } - @Override - public void onClose(int statusCode, CharSequence reason) { - receiver.acknowledge(); - processClose(statusCode, reason.toString()); - } + private void processText() { + listener.onText(WebSocketImpl.this, text, part); + } - @Override - public void onError(Exception error) { - // An signalError doesn't necessarily mean we must signalClose - // the WebSocket. However, if it's something the WebSocket - // Specification recognizes as a reason for "Failing the - // WebSocket Connection", then we must do so, but BEFORE - // notifying the Listener. - if (!(error instanceof FailWebSocketException)) { - signalError(error); - } else { - Exception ex = (Exception) new ProtocolException().initCause(error); - int code = ((FailWebSocketException) error).getStatusCode(); - enqueueClose(new Close(code, "")) - .whenComplete((r, e) -> { - if (e != null) { - ex.addSuppressed(e); - } - try { - channel.close(); - } catch (IOException e1) { - ex.addSuppressed(e1); - } finally { - closed.set(true); - } - signalError(ex); - }); - } - } + private void processOpen() { + listener.onOpen(WebSocketImpl.this); + } + } - @Override - public void onComplete() { - processClose(CLOSED_ABNORMALLY, ""); + private void signalOpen() { + receiveScheduler.runOrSchedule(); + } + + private void signalError(Throwable error) { + inputClosed = true; + outputClosed = true; + if (!this.error.compareAndSet(null, error) || !trySetState(ERROR)) { + Log.logError(error); + } else { + close(); + } + } + + private void close() { + try { + try { + receiver.close(); + } finally { + transmitter.close(); } - }; + } catch (Throwable t) { + Log.logError(t); + } + } + + /* + * Signals a Close event (might not correspond to anything happened on the + * channel, i.e. might be synthetic). + */ + private void signalClose(int statusCode, String reason) { + inputClosed = true; + this.statusCode = statusCode; + this.reason = reason; + if (!trySetState(CLOSE)) { + Log.logTrace("Close: {0}, ''{1}''", statusCode, reason); + } else { + try { + receiver.close(); + } catch (Throwable t) { + Log.logError(t); + } + } + } + + private class SignallingMessageConsumer implements MessageStreamConsumer { + + @Override + public void onText(CharSequence data, MessagePart part) { + receiver.acknowledge(); + text = data; + WebSocketImpl.this.part = part; + tryChangeState(WAITING, TEXT); + } + + @Override + public void onBinary(ByteBuffer data, MessagePart part) { + receiver.acknowledge(); + binaryData = data; + WebSocketImpl.this.part = part; + tryChangeState(WAITING, BINARY); + } + + @Override + public void onPing(ByteBuffer data) { + receiver.acknowledge(); + binaryData = data; + tryChangeState(WAITING, PING); + } + + @Override + public void onPong(ByteBuffer data) { + receiver.acknowledge(); + binaryData = data; + tryChangeState(WAITING, PONG); + } + + @Override + public void onClose(int statusCode, CharSequence reason) { + receiver.acknowledge(); + signalClose(statusCode, reason.toString()); + } + + @Override + public void onComplete() { + receiver.acknowledge(); + signalClose(CLOSED_ABNORMALLY, ""); + } + + @Override + public void onError(Throwable error) { + signalError(error); + } + } + + private boolean trySetState(State newState) { + while (true) { + State currentState = state.get(); + if (currentState == ERROR || currentState == CLOSE) { + return false; + } else if (state.compareAndSet(currentState, newState)) { + receiveScheduler.runOrSchedule(); + return true; + } + } + } + + private boolean tryChangeState(State expectedState, State newState) { + State witness = state.compareAndExchange(expectedState, newState); + if (witness == expectedState) { + receiveScheduler.runOrSchedule(); + return true; + } + // This should be the only reason for inability to change the state from + // IDLE to WAITING: the state has changed to terminal + if (witness != ERROR && witness != CLOSE) { + throw new InternalError(); + } + return false; } } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java index 33c45195f21..799f00250ce 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 jdk.incubator.http.internal.websocket; +import java.net.Proxy; + /* * https://tools.ietf.org/html/rfc6455#section-4.1 */ @@ -41,4 +43,9 @@ public interface WebSocketRequest { * WebSocket specification. */ void setSystemHeader(String name, String value); + + /* + * Sets the proxy for this request. + */ + void setProxy(Proxy proxy); } diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java index 312e74923be..15b99d35cee 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/module-info.java b/src/jdk.incubator.httpclient/share/classes/module-info.java index 49e8f69f33c..471bc39f067 100644 --- a/src/jdk.incubator.httpclient/share/classes/module-info.java +++ b/src/jdk.incubator.httpclient/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -33,4 +33,3 @@ module jdk.incubator.httpclient { exports jdk.incubator.http; } - diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 7071399994e..a8d5eee330b 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -172,8 +172,6 @@ java/net/MulticastSocket/Test.java 7145658 macosx-a java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-all -java/net/httpclient/websocket/ConnectionHandover.java 8188895 windows-all - ############################################################################ # jdk_nio diff --git a/test/jdk/com/sun/net/httpserver/EchoHandler.java b/test/jdk/com/sun/net/httpserver/EchoHandler.java index 0b9de1f3d62..77033a216f4 100644 --- a/test/jdk/com/sun/net/httpserver/EchoHandler.java +++ b/test/jdk/com/sun/net/httpserver/EchoHandler.java @@ -66,8 +66,8 @@ public class EchoHandler implements HttpHandler { t.sendResponseHeaders(200, in.length); OutputStream os = t.getResponseBody(); os.write(in); - close(os); - close(is); + close(t, os); + close(t, is); } else { OutputStream os = t.getResponseBody(); byte[] buf = new byte[64 * 1024]; @@ -84,15 +84,21 @@ public class EchoHandler implements HttpHandler { String s = Integer.toString(count); os.write(s.getBytes()); } - close(os); - close(is); + close(t, os); + close(t, is); } } protected void close(OutputStream os) throws IOException { - os.close(); + os.close(); } protected void close(InputStream is) throws IOException { - is.close(); + is.close(); + } + protected void close(HttpExchange t, OutputStream os) throws IOException { + close(os); + } + protected void close(HttpExchange t, InputStream is) throws IOException { + close(is); } } diff --git a/test/jdk/java/net/httpclient/APIErrors.java b/test/jdk/java/net/httpclient/APIErrors.java deleted file mode 100644 index ea541f00b56..00000000000 --- a/test/jdk/java/net/httpclient/APIErrors.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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 8087112 - * @modules jdk.incubator.httpclient - * java.logging - * jdk.httpserver - * @library /lib/testlibrary/ - * @build jdk.testlibrary.SimpleSSLContext ProxyServer - * @build TestKit - * @compile ../../../com/sun/net/httpserver/LogFilter.java - * @compile ../../../com/sun/net/httpserver/FileServerHandler.java - * @run main/othervm APIErrors - * @summary Basic checks for appropriate errors/exceptions thrown from the API - */ - -import com.sun.net.httpserver.HttpContext; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsServer; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.PasswordAuthentication; -import java.net.ProxySelector; -import java.net.URI; -import jdk.incubator.http.HttpClient; -import jdk.incubator.http.HttpRequest; -import jdk.incubator.http.HttpResponse; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ExecutorService; -import java.util.function.Supplier; -import static jdk.incubator.http.HttpResponse.BodyHandler.discard; - -public class APIErrors { - - static ExecutorService serverExecutor = Executors.newCachedThreadPool(); - static String httproot, fileuri; - static List<HttpClient> clients = new LinkedList<>(); - - public static void main(String[] args) throws Exception { - HttpServer server = createServer(); - - int port = server.getAddress().getPort(); - System.out.println("HTTP server port = " + port); - - httproot = "http://127.0.0.1:" + port + "/files/"; - fileuri = httproot + "foo.txt"; - - HttpClient client = HttpClient.newHttpClient(); - - try { - test1(); - test2(); - //test3(); - } finally { - server.stop(0); - serverExecutor.shutdownNow(); - for (HttpClient c : clients) - ((ExecutorService)c.executor()).shutdownNow(); - } - } - - static void checkNonNull(Supplier<?> r) { - if (r.get() == null) - throw new RuntimeException("Unexpected null return:"); - } - - static void assertTrue(Supplier<Boolean> r) { - if (r.get() == false) - throw new RuntimeException("Assertion failure:"); - } - - // HttpClient.Builder - static void test1() throws Exception { - System.out.println("Test 1"); - HttpClient.Builder cb = HttpClient.newBuilder(); - TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(-1)); - TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(0)); - TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(257)); - TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(500)); - TestKit.assertNotThrows(() -> cb.priority(1)); - TestKit.assertNotThrows(() -> cb.priority(256)); - TestKit.assertNotThrows(() -> { - clients.add(cb.build()); - clients.add(cb.build()); - }); - } - - static void test2() throws Exception { - System.out.println("Test 2"); - HttpClient.Builder cb = HttpClient.newBuilder(); - InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 5000); - cb.proxy(ProxySelector.of(addr)); - HttpClient c = cb.build(); - clients.add(c); - checkNonNull(()-> c.executor()); - assertTrue(()-> c.followRedirects() == HttpClient.Redirect.NEVER); - assertTrue(()-> !c.authenticator().isPresent()); - } - - static URI accessibleURI() { - return URI.create(fileuri); - } - - static HttpRequest request() { - return HttpRequest.newBuilder(accessibleURI()).GET().build(); - } - -// static void test3() throws Exception { -// System.out.println("Test 3"); -// TestKit.assertThrows(IllegalStateException.class, ()-> { -// try { -// HttpRequest r1 = request(); -// HttpResponse<Object> resp = r1.response(discard(null)); -// HttpResponse<Object> resp1 = r1.response(discard(null)); -// } catch (IOException |InterruptedException e) { -// throw new RuntimeException(e); -// } -// }); -// -// TestKit.assertThrows(IllegalStateException.class, ()-> { -// try { -// HttpRequest r1 = request(); -// HttpResponse<Object> resp = r1.response(discard(null)); -// HttpResponse<Object> resp1 = r1.responseAsync(discard(null)).get(); -// } catch (IOException |InterruptedException | ExecutionException e) { -// throw new RuntimeException(e); -// } -// }); -// TestKit.assertThrows(IllegalStateException.class, ()-> { -// try { -// HttpRequest r1 = request(); -// HttpResponse<Object> resp1 = r1.responseAsync(discard(null)).get(); -// HttpResponse<Object> resp = r1.response(discard(null)); -// } catch (IOException |InterruptedException | ExecutionException e) { -// throw new RuntimeException(e); -// } -// }); -// } - - static class Auth extends java.net.Authenticator { - int count = 0; - @Override - protected PasswordAuthentication getPasswordAuthentication() { - if (count++ == 0) { - return new PasswordAuthentication("user", "passwd".toCharArray()); - } else { - return new PasswordAuthentication("user", "goober".toCharArray()); - } - } - int count() { - return count; - } - } - - static HttpServer createServer() throws Exception { - HttpServer s = HttpServer.create(new InetSocketAddress(0), 0); - if (s instanceof HttpsServer) - throw new RuntimeException ("should not be httpsserver"); - - String root = System.getProperty("test.src") + "/docs"; - s.createContext("/files", new FileServerHandler(root)); - s.setExecutor(serverExecutor); - s.start(); - - return s; - } -} diff --git a/test/jdk/java/net/httpclient/AbstractNoBody.java b/test/jdk/java/net/httpclient/AbstractNoBody.java new file mode 100644 index 00000000000..1ddb79ab2b1 --- /dev/null +++ b/test/jdk/java/net/httpclient/AbstractNoBody.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, 2017, 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.incubator.http.HttpClient; +import javax.net.ssl.SSLContext; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; + +public abstract class AbstractNoBody { + + SSLContext sslContext; + HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpsServer httpsTestServer; // HTTPS/1.1 + Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI_fixed; + String httpURI_chunk; + String httpsURI_fixed; + String httpsURI_chunk; + String http2URI_fixed; + String http2URI_chunk; + String https2URI_fixed; + String https2URI_chunk; + + static final String SIMPLE_STRING = "Hello world. Goodbye world"; + static final int ITERATION_COUNT = 10; + // a shared executor helps reduce the amount of threads created by the test + static final Executor executor = Executors.newFixedThreadPool(ITERATION_COUNT * 2); + static final ExecutorService serverExecutor = Executors.newFixedThreadPool(ITERATION_COUNT * 4); + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][]{ + { httpURI_fixed, false }, + { httpURI_chunk, false }, + { httpsURI_fixed, false }, + { httpsURI_chunk, false }, + { http2URI_fixed, false }, + { http2URI_chunk, false }, + { https2URI_fixed, false,}, + { https2URI_chunk, false }, + + { httpURI_fixed, true }, + { httpURI_chunk, true }, + { httpsURI_fixed, true }, + { httpsURI_chunk, true }, + { http2URI_fixed, true }, + { http2URI_chunk, true }, + { https2URI_fixed, true,}, + { https2URI_chunk, true }, + }; + } + + HttpClient newHttpClient() { + return HttpClient.newBuilder() + .executor(executor) + .sslContext(sslContext) + .build(); + } + + @BeforeTest + public void setup() throws Exception { + printStamp(START, "setup"); + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + // HTTP/1.1 + HttpHandler h1_fixedLengthNoBodyHandler = new HTTP1_FixedLengthNoBodyHandler(); + HttpHandler h1_chunkNoBodyHandler = new HTTP1_ChunkedNoBodyHandler(); + InetSocketAddress sa = new InetSocketAddress("localhost", 0); + httpTestServer = HttpServer.create(sa, 0); + httpTestServer.setExecutor(serverExecutor); + httpTestServer.createContext("/http1/noBodyFixed", h1_fixedLengthNoBodyHandler); + httpTestServer.createContext("/http1/noBodyChunk", h1_chunkNoBodyHandler); + httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyFixed"; + httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyChunk"; + + httpsTestServer = HttpsServer.create(sa, 0); + httpsTestServer.setExecutor(serverExecutor); + httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer.createContext("/https1/noBodyFixed", h1_fixedLengthNoBodyHandler); + httpsTestServer.createContext("/https1/noBodyChunk", h1_chunkNoBodyHandler); + httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyFixed"; + httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyChunk"; + + // HTTP/2 + Http2Handler h2_fixedLengthNoBodyHandler = new HTTP2_FixedLengthNoBodyHandler(); + Http2Handler h2_chunkedNoBodyHandler = new HTTP2_ChunkedNoBodyHandler(); + + http2TestServer = new Http2TestServer("127.0.0.1", false, 0, serverExecutor, null); + http2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/http2/noBodyFixed"); + http2TestServer.addHandler(h2_chunkedNoBodyHandler, "/http2/noBodyChunk"); + int port = http2TestServer.getAddress().getPort(); + http2URI_fixed = "http://127.0.0.1:" + port + "/http2/noBodyFixed"; + http2URI_chunk = "http://127.0.0.1:" + port + "/http2/noBodyChunk"; + + https2TestServer = new Http2TestServer("127.0.0.1", true, 0, serverExecutor, sslContext); + https2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/https2/noBodyFixed"); + https2TestServer.addHandler(h2_chunkedNoBodyHandler, "/https2/noBodyChunk"); + port = https2TestServer.getAddress().getPort(); + https2URI_fixed = "https://127.0.0.1:" + port + "/https2/noBodyFixed"; + https2URI_chunk = "https://127.0.0.1:" + port + "/https2/noBodyChunk"; + + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + printStamp(END,"setup"); + } + + @AfterTest + public void teardown() throws Exception { + printStamp(START, "teardown"); + httpTestServer.stop(0); + httpsTestServer.stop(0); + http2TestServer.stop(); + https2TestServer.stop(); + printStamp(END, "teardown"); + } + + static final long start = System.nanoTime(); + static final String START = "start"; + static final String END = "end "; + static long elapsed() { return (System.nanoTime() - start)/1000_000;} + void printStamp(String what, String fmt, Object... args) { + long elapsed = elapsed(); + long sec = elapsed/1000; + long ms = elapsed % 1000; + String time = sec > 0 ? sec + "sec " : ""; + time = time + ms + "ms"; + System.out.printf("%s: %s \t [%s]\t %s%n", + getClass().getSimpleName(), what, time, String.format(fmt,args)); + } + + + static class HTTP1_FixedLengthNoBodyHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + //out.println("NoBodyHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, -1); // no body + } + } + + static class HTTP1_ChunkedNoBodyHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + //out.println("NoBodyHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, 0); // chunked + t.getResponseBody().close(); // write nothing + } + } + + static class HTTP2_FixedLengthNoBodyHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + //out.println("NoBodyHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, 0); + } + } + + static class HTTP2_ChunkedNoBodyHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + //out.println("NoBodyHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, -1); + t.getResponseBody().close(); // write nothing + } + } +} diff --git a/test/jdk/java/net/httpclient/BasicAuthTest.java b/test/jdk/java/net/httpclient/BasicAuthTest.java index d3acad31059..04ae3e8f39a 100644 --- a/test/jdk/java/net/httpclient/BasicAuthTest.java +++ b/test/jdk/java/net/httpclient/BasicAuthTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -45,7 +45,7 @@ import jdk.incubator.http.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.nio.charset.StandardCharsets.US_ASCII; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; public class BasicAuthTest { diff --git a/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java b/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java new file mode 100644 index 00000000000..d548c365f6a --- /dev/null +++ b/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpHeaders; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Flow; +import java.util.stream.Stream; +import static java.lang.System.err; + +/* + * @test + * @bug 8187503 + * @summary An example on how to read a response body with InputStream... + * @run main/othervm -Dtest.debug=true BodyProcessorInputStreamTest + * @author daniel fuchs + */ +public class BodyProcessorInputStreamTest { + + public static boolean DEBUG = Boolean.getBoolean("test.debug"); + + /** + * Examine the response headers to figure out the charset used to + * encode the body content. + * If the content type is not textual, returns an empty Optional. + * Otherwise, returns the body content's charset, defaulting to + * ISO-8859-1 if none is explicitly specified. + * @param headers The response headers. + * @return The charset to use for decoding the response body, if + * the response body content is text/... + */ + public static Optional<Charset> getCharset(HttpHeaders headers) { + Optional<String> contentType = headers.firstValue("Content-Type"); + Optional<Charset> charset = Optional.empty(); + if (contentType.isPresent()) { + final String[] values = contentType.get().split(";"); + if (values[0].startsWith("text/")) { + charset = Optional.of(Stream.of(values) + .map(x -> x.toLowerCase(Locale.ROOT)) + .map(String::trim) + .filter(x -> x.startsWith("charset=")) + .map(x -> x.substring("charset=".length())) + .findFirst() + .orElse("ISO-8859-1")) + .map(Charset::forName); + } + } + return charset; + } + + public static void main(String[] args) throws Exception { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest + .newBuilder(new URI("http://hg.openjdk.java.net/jdk9/sandbox/jdk/shortlog/http-client-branch/")) + .GET() + .build(); + + // This example shows how to return an InputStream that can be used to + // start reading the response body before the response is fully received. + // In comparison, the snipet below (which uses + // HttpResponse.BodyHandler.asString()) obviously will not return before the + // response body is fully read: + // + // System.out.println( + // client.sendAsync(request, HttpResponse.BodyHandler.asString()).get().body()); + + CompletableFuture<HttpResponse<InputStream>> handle = + client.sendAsync(request, HttpResponse.BodyHandler.asInputStream()); + if (DEBUG) err.println("Request sent"); + + HttpResponse<InputStream> pending = handle.get(); + + // At this point, the response headers have been received, but the + // response body may not have arrived yet. This comes from + // the implementation of HttpResponseInputStream::getBody above, + // which returns an already completed completion stage, without + // waiting for any data. + // We can therefore access the headers - and the body, which + // is our live InputStream, without waiting... + HttpHeaders responseHeaders = pending.headers(); + + // Get the charset declared in the response headers. + // The optional will be empty if the content type is not + // of type text/... + Optional<Charset> charset = getCharset(responseHeaders); + + try (InputStream is = pending.body(); + // We assume a textual content type. Construct an InputStream + // Reader with the appropriate Charset. + // charset.get() will throw NPE if the content is not textual. + Reader r = new InputStreamReader(is, charset.get())) { + + char[] buff = new char[32]; + int off=0, n=0; + if (DEBUG) err.println("Start receiving response body"); + if (DEBUG) err.println("Charset: " + charset.get()); + + // Start consuming the InputStream as the data arrives. + // Will block until there is something to read... + while ((n = r.read(buff, off, buff.length - off)) > 0) { + assert (buff.length - off) > 0; + assert n <= (buff.length - off); + if (n == (buff.length - off)) { + System.out.print(buff); + off = 0; + } else { + off += n; + } + assert off < buff.length; + } + + // last call to read may not have filled 'buff' completely. + // flush out the remaining characters. + assert off >= 0 && off < buff.length; + for (int i=0; i < off; i++) { + System.out.print(buff[i]); + } + + // We're done! + System.out.println("Done!"); + } + } + +} diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java new file mode 100644 index 00000000000..684e1f6ee2d --- /dev/null +++ b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.SubmissionPublisher; +import java.util.function.IntSupplier; +import java.util.stream.IntStream; +import jdk.incubator.http.HttpResponse.BodySubscriber; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; +import static java.lang.System.out; +import static java.nio.ByteBuffer.wrap; +import static java.util.concurrent.TimeUnit.SECONDS; +import static jdk.incubator.http.HttpResponse.BodySubscriber.buffering; +import static org.testng.Assert.*; + +/* + * @test + * @summary Direct test for HttpResponse.BodySubscriber.buffering() cancellation + * @run testng/othervm BufferingSubscriberCancelTest + */ + +public class BufferingSubscriberCancelTest { + + @DataProvider(name = "bufferSizes") + public Object[][] bufferSizes() { + return new Object[][]{ + // bufferSize should be irrelevant + {1}, {100}, {511}, {512}, {513}, {1024}, {2047}, {2048} + }; + } + + @Test(dataProvider = "bufferSizes") + public void cancelWithoutAnyItemsPublished(int bufferSize) throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 1); + + CountDownLatch gate = new CountDownLatch(1); // single onSubscribe + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize); + publisher.subscribe(subscriber); + gate.await(30, SECONDS); + assertEqualsWithRetry(publisher::getNumberOfSubscribers, 1); + exposingSubscriber.subscription.cancel(); + assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0); + + // further cancels/requests should be a no-op + Subscription s = exposingSubscriber.subscription; + s.cancel(); s.request(1); + s.cancel(); s.request(100); s.cancel(); + s.cancel(); s.request(MAX_VALUE); s.cancel(); s.cancel(); + s.cancel(); s.cancel(); s.cancel(); s.cancel(); + s.request(MAX_VALUE); s.request(MAX_VALUE); s.request(MAX_VALUE); + s.request(-1); s.request(-100); s.request(MIN_VALUE); + assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0); + executor.shutdown(); + } + + @DataProvider(name = "sizeAndItems") + public Object[][] sizeAndItems() { + return new Object[][] { + // bufferSize and item bytes must be equal to count onNext calls + // bufferSize items + { 1, List.of(wrap(new byte[] { 1 })) }, + { 2, List.of(wrap(new byte[] { 1, 2 })) }, + { 3, List.of(wrap(new byte[] { 1, 2, 3})) }, + { 4, List.of(wrap(new byte[] { 1, 2 , 3, 4})) }, + { 5, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5})) }, + { 6, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6})) }, + { 7, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7})) }, + { 8, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8})) }, + { 9, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8, 9})) }, + { 10, List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8, 9, 10})) }, + }; + } + + @Test(dataProvider = "sizeAndItems") + public void cancelWithItemsPublished(int bufferSize, List<ByteBuffer> items) + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 24); + + final int ITERATION_TIMES = 10; + // onSubscribe + onNext ITERATION_TIMES + CountDownLatch gate = new CountDownLatch(1 + ITERATION_TIMES); + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize); + publisher.subscribe(subscriber); + + assertEqualsWithRetry(publisher::getNumberOfSubscribers, 1); + IntStream.range(0, ITERATION_TIMES).forEach(x -> publisher.submit(items)); + gate.await(30, SECONDS); + exposingSubscriber.subscription.cancel(); + IntStream.range(0, ITERATION_TIMES+1).forEach(x -> publisher.submit(items)); + + assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0); + assertEquals(exposingSubscriber.onNextInvocations, ITERATION_TIMES); + executor.shutdown(); + } + + // same as above but with more racy conditions, do not wait on the gate + @Test(dataProvider = "sizeAndItems") + public void cancelWithItemsPublishedNoWait(int bufferSize, List<ByteBuffer> items) + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 24); + + final int ITERATION_TIMES = 10; + // any callback will so, since onSub is guaranteed to be before onNext + CountDownLatch gate = new CountDownLatch(1); + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize); + publisher.subscribe(subscriber); + + IntStream.range(0, ITERATION_TIMES).forEach(x -> publisher.submit(items)); + gate.await(30, SECONDS); + exposingSubscriber.subscription.cancel(); + IntStream.range(0, ITERATION_TIMES+1).forEach(x -> publisher.submit(items)); + + int onNextInvocations = exposingSubscriber.onNextInvocations; + assertTrue(onNextInvocations <= ITERATION_TIMES, + "Expected <= " + ITERATION_TIMES + ", got " + onNextInvocations); + executor.shutdown(); + } + + static class ExposingSubscriber implements BodySubscriber<Void> { + final CountDownLatch gate; + volatile Subscription subscription; + volatile int onNextInvocations; + + ExposingSubscriber(CountDownLatch gate) { + this.gate = gate; + } + + @Override + public void onSubscribe(Subscription subscription) { + //out.println("onSubscribe " + subscription); + this.subscription = subscription; + gate.countDown(); + subscription.request(MAX_VALUE); // forever + } + + @Override + public void onNext(List<ByteBuffer> item) { + //out.println("onNext " + item); + onNextInvocations++; + gate.countDown(); + } + + @Override + public void onError(Throwable throwable) { + out.println("onError " + throwable); + } + + @Override + public void onComplete() { + out.println("onComplete "); + } + + @Override + public CompletionStage<Void> getBody() { + throw new UnsupportedOperationException("getBody is unsupported"); + } + } + + // There is a race between cancellation and subscriber callbacks, the + // following mechanism retries a number of times to allow for this race. The + // only requirement is that the expected result is actually observed. + + static final int TEST_RECHECK_TIMES = 30; + + static void assertEqualsWithRetry(IntSupplier actualSupplier, int expected) + throws Exception + { + int actual = expected + 1; // anything other than expected + for (int i=0; i< TEST_RECHECK_TIMES; i++) { + actual = actualSupplier.getAsInt(); + if (actual == expected) + return; + Thread.sleep(100); + } + assertEquals(actual, expected); // will fail with the usual testng message + } +} diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java new file mode 100644 index 00000000000..ed875bc3dc6 --- /dev/null +++ b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.Phaser; +import java.util.concurrent.SubmissionPublisher; +import java.util.function.IntSupplier; +import java.util.stream.IntStream; +import jdk.incubator.http.HttpResponse.BodySubscriber; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; +import static java.lang.System.out; +import static java.nio.ByteBuffer.wrap; +import static java.util.concurrent.TimeUnit.SECONDS; +import static jdk.incubator.http.HttpResponse.BodySubscriber.buffering; +import static org.testng.Assert.*; + +/* + * @test + * @summary Test for HttpResponse.BodySubscriber.buffering() onError/onComplete + * @run testng/othervm BufferingSubscriberErrorCompleteTest + */ + +public class BufferingSubscriberErrorCompleteTest { + + @DataProvider(name = "illegalDemand") + public Object[][] illegalDemand() { + return new Object[][]{ + {0L}, {-1L}, {-5L}, {-100L}, {-101L}, {-100_001L}, {MIN_VALUE} + }; + } + + @Test(dataProvider = "illegalDemand") + public void illegalRequest(long demand) throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 1); + + Phaser gate = new Phaser(2); // single onSubscribe and onError + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, 1); + publisher.subscribe(subscriber); + gate.arriveAndAwaitAdvance(); + + Subscription s = exposingSubscriber.subscription; + int previous = exposingSubscriber.onErrorInvocations; + s.request(demand); + gate.arriveAndAwaitAdvance(); + + assertEquals(previous + 1, exposingSubscriber.onErrorInvocations); + assertTrue(exposingSubscriber.throwable instanceof IllegalArgumentException, + "Expected IAE, got:" + exposingSubscriber.throwable); + + furtherCancelsRequestsShouldBeNoOp(s); + assertEquals(exposingSubscriber.onErrorInvocations, 1); + executor.shutdown(); + } + + + @DataProvider(name = "bufferAndItemSizes") + public Object[][] bufferAndItemSizes() { + List<Object[]> values = new ArrayList<>(); + + for (int bufferSize : new int[] { 1, 5, 10, 100, 1000 }) + for (int items : new int[] { 0, 1, 2, 5, 9, 10, 11, 15, 29, 99 }) + values.add(new Object[] { bufferSize, items }); + + return values.stream().toArray(Object[][]::new); + } + + @Test(dataProvider = "bufferAndItemSizes") + public void onErrorFromPublisher(int bufferSize, + int numberOfItems) + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 1); + + // onSubscribe + onError + this thread + Phaser gate = new Phaser(3); + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize); + publisher.subscribe(subscriber); + + List<ByteBuffer> item = List.of(wrap(new byte[] { 1 })); + IntStream.range(0, numberOfItems).forEach(x -> publisher.submit(item)); + Throwable t = new Throwable("a message from me to me"); + publisher.closeExceptionally(t); + + gate.arriveAndAwaitAdvance(); + + Subscription s = exposingSubscriber.subscription; + + assertEquals(exposingSubscriber.onErrorInvocations, 1); + assertEquals(exposingSubscriber.onCompleteInvocations, 0); + assertEquals(exposingSubscriber.throwable, t); + assertEquals(exposingSubscriber.throwable.getMessage(), + "a message from me to me"); + + furtherCancelsRequestsShouldBeNoOp(s); + assertEquals(exposingSubscriber.onErrorInvocations, 1); + assertEquals(exposingSubscriber.onCompleteInvocations, 0); + executor.shutdown(); + } + + @Test(dataProvider = "bufferAndItemSizes") + public void onCompleteFromPublisher(int bufferSize, + int numberOfItems) + throws Exception + { + ExecutorService executor = Executors.newFixedThreadPool(1); + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, 1); + + // onSubscribe + onComplete + this thread + Phaser gate = new Phaser(3); + ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate); + BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize); + publisher.subscribe(subscriber); + + List<ByteBuffer> item = List.of(wrap(new byte[] { 1 })); + IntStream.range(0, numberOfItems).forEach(x -> publisher.submit(item)); + publisher.close(); + + gate.arriveAndAwaitAdvance(); + + Subscription s = exposingSubscriber.subscription; + + assertEquals(exposingSubscriber.onErrorInvocations, 0); + assertEquals(exposingSubscriber.onCompleteInvocations, 1); + assertEquals(exposingSubscriber.throwable, null); + + furtherCancelsRequestsShouldBeNoOp(s); + assertEquals(exposingSubscriber.onErrorInvocations, 0); + assertEquals(exposingSubscriber.onCompleteInvocations, 1); + assertEquals(exposingSubscriber.throwable, null); + executor.shutdown(); + } + + static class ExposingSubscriber implements BodySubscriber<Void> { + final Phaser gate; + volatile Subscription subscription; + volatile int onNextInvocations; + volatile int onErrorInvocations; + volatile int onCompleteInvocations; + volatile Throwable throwable; + + ExposingSubscriber(Phaser gate) { + this.gate = gate; + } + + @Override + public void onSubscribe(Subscription subscription) { + //out.println("onSubscribe " + subscription); + this.subscription = subscription; + subscription.request(MAX_VALUE); + gate.arrive(); + } + + @Override + public void onNext(List<ByteBuffer> item) { + //out.println("onNext " + item); + onNextInvocations++; + } + + @Override + public void onError(Throwable throwable) { + //out.println("onError " + throwable); + this.throwable = throwable; + onErrorInvocations++; + gate.arrive(); + } + + @Override + public void onComplete() { + //out.println("onComplete "); + onCompleteInvocations++; + gate.arrive(); + } + + @Override + public CompletionStage<Void> getBody() { + throw new UnsupportedOperationException("getBody is unsupported"); + } + } + + static void furtherCancelsRequestsShouldBeNoOp(Subscription s) { + s.cancel(); s.request(1); + s.cancel(); s.request(100); s.cancel(); + s.cancel(); s.request(MAX_VALUE); s.cancel(); s.cancel(); + s.cancel(); s.cancel(); s.cancel(); s.cancel(); + s.request(MAX_VALUE); s.request(MAX_VALUE); s.request(MAX_VALUE); + s.request(-1); s.request(-100); s.request(MIN_VALUE); + } +} diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java new file mode 100644 index 00000000000..1a246f83caa --- /dev/null +++ b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.SubmissionPublisher; +import java.util.function.BiConsumer; +import jdk.incubator.http.HttpResponse.BodyHandler; +import jdk.incubator.http.HttpResponse.BodySubscriber; +import jdk.test.lib.RandomFactory; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.min; +import static java.lang.System.out; +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.testng.Assert.*; + +/* + * @test + * @bug 8184285 + * @summary Direct test for HttpResponse.BodySubscriber.buffering() API + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run testng/othervm -Djdk.internal.httpclient.debug=true BufferingSubscriberTest + */ + +public class BufferingSubscriberTest { + + // If we compute that a test will take less that 10s + // we judge it acceptable + static final long LOWER_THRESHOLD = 10_000; // 10 sec. + // If we compute that a test will take more than 20 sec + // we judge it problematic: we will try to adjust the + // buffer sizes, and if we can't we will print a warning + static final long UPPER_THRESHOLD = 20_000; // 20 sec. + + static final Random random = RandomFactory.getRandom(); + static final long start = System.nanoTime(); + static final String START = "start"; + static final String END = "end "; + static long elapsed() { return (System.nanoTime() - start)/1000_000;} + static void printStamp(String what, String fmt, Object... args) { + long elapsed = elapsed(); + long sec = elapsed/1000; + long ms = elapsed % 1000; + String time = sec > 0 ? sec + "sec " : ""; + time = time + ms + "ms"; + out.println(what + "\t ["+time+"]\t "+ String.format(fmt,args)); + } + @DataProvider(name = "negatives") + public Object[][] negatives() { + return new Object[][] { { 0 }, { -1 }, { -1000 } }; + } + + @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class) + public void subscriberThrowsIAE(int bufferSize) { + printStamp(START, "subscriberThrowsIAE(%d)", bufferSize); + try { + BodySubscriber<?> bp = BodySubscriber.asByteArray(); + BodySubscriber.buffering(bp, bufferSize); + } finally { + printStamp(END, "subscriberThrowsIAE(%d)", bufferSize); + } + } + + @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class) + public void handlerThrowsIAE(int bufferSize) { + printStamp(START, "handlerThrowsIAE(%d)", bufferSize); + try { + BodyHandler<?> bp = BodyHandler.asByteArray(); + BodyHandler.buffering(bp, bufferSize); + } finally { + printStamp(END, "handlerThrowsIAE(%d)", bufferSize); + } + } + + // --- + + @DataProvider(name = "config") + public Object[][] config() { + return new Object[][] { + // iterations delayMillis numBuffers bufferSize maxBufferSize minBufferSize + { 1, 0, 1, 1, 2, 1 }, + { 1, 5, 1, 100, 2, 1 }, + { 1, 0, 1, 10, 1000, 1 }, + { 1, 10, 1, 10, 1000, 1 }, + { 1, 0, 1, 1000, 1000, 10 }, + { 1, 0, 10, 1000, 1000, 50 }, + { 1, 0, 1000, 10 , 1000, 50 }, + { 1, 100, 1, 1000 * 4, 1000, 50 }, + { 100, 0, 1000, 1, 2, 1 }, + { 3, 0, 4, 5006, 1000, 50 }, + { 20, 0, 100, 4888, 1000, 100 }, + { 16, 10, 1000, 50 , 1000, 100 }, + { 16, 10, 1000, 50 , 657, 657 }, + }; + } + + @Test(dataProvider = "config") + public void test(int iterations, + int delayMillis, + int numBuffers, + int bufferSize, + int maxBufferSize, + int minbufferSize) { + for (long perRequestAmount : new long[] { 1L, MAX_VALUE }) + test(iterations, + delayMillis, + numBuffers, + bufferSize, + maxBufferSize, + minbufferSize, + perRequestAmount); + } + + volatile boolean onNextThrew; + + BiConsumer<Flow.Subscriber<?>, ? super Throwable> onNextThrowHandler = + (sub, ex) -> { + onNextThrew = true; + System.out.println("onNext threw " + ex); + ex.printStackTrace(); + }; + + public void test(int iterations, + int delayMillis, + int numBuffers, + int bufferSize, + int maxBufferSize, + int minBufferSize, + long requestAmount) { + ExecutorService executor = Executors.newFixedThreadPool(1); + try { + out.printf("Iterations %d\n", iterations); + for (int i=0; i<iterations; i++ ) { + printStamp(START, "Iteration %d", i); + try { + SubmissionPublisher<List<ByteBuffer>> publisher = + new SubmissionPublisher<>(executor, + 1, // lock-step with the publisher, for now + onNextThrowHandler); + CompletableFuture<?> cf = sink(publisher, + delayMillis, + numBuffers * bufferSize, + requestAmount, + maxBufferSize, + minBufferSize); + source(publisher, numBuffers, bufferSize); + publisher.close(); + cf.join(); + } finally { + printStamp(END, "Iteration %d\n", i); + } + } + + assertFalse(onNextThrew, "Unexpected onNextThrew, check output"); + + out.println("OK"); + } finally { + executor.shutdown(); + } + } + + static long accumulatedDataSize(List<ByteBuffer> bufs) { + return bufs.stream().mapToLong(ByteBuffer::remaining).sum(); + } + + /** Returns a new BB with its contents set to monotonically increasing + * values, staring at the given start index and wrapping every 100. */ + static ByteBuffer allocateBuffer(int size, int startIdx) { + ByteBuffer b = ByteBuffer.allocate(size); + for (int i=0; i<size; i++) + b.put((byte)((startIdx + i) % 100)); + b.position(0); + return b; + } + + static class TestSubscriber implements BodySubscriber<Integer> { + final int delayMillis; + final int bufferSize; + final int expectedTotalSize; + final long requestAmount; + final CompletableFuture<Integer> completion; + final Executor delayedExecutor; + volatile Flow.Subscription subscription; + + TestSubscriber(int bufferSize, + int delayMillis, + int expectedTotalSize, + long requestAmount) { + this.bufferSize = bufferSize; + this.completion = new CompletableFuture<>(); + this.delayMillis = delayMillis; + this.delayedExecutor = delayedExecutor(delayMillis, MILLISECONDS); + this.expectedTotalSize = expectedTotalSize; + this.requestAmount = requestAmount; + } + + /** + * Example of a factory method which would decorate a buffering + * subscriber to create a new subscriber dependent on buffering capability. + * <p> + * The integer type parameter simulates the body just by counting the + * number of bytes in the body. + */ + static BodySubscriber<Integer> createSubscriber(int bufferSize, + int delay, + int expectedTotalSize, + long requestAmount) { + TestSubscriber s = new TestSubscriber(bufferSize, + delay, + expectedTotalSize, + requestAmount); + return BodySubscriber.buffering(s, bufferSize); + } + + private void requestMore() { + subscription.request(requestAmount); + } + + @Override + public void onSubscribe(Subscription subscription) { + assertNull(this.subscription); + this.subscription = subscription; + if (delayMillis > 0) + delayedExecutor.execute(this::requestMore); + else + requestMore(); + } + + volatile int wrongSizes; + volatile int totalBytesReceived; + volatile int onNextInvocations; + volatile long lastSeenSize = -1; + volatile boolean noMoreOnNext; // false + volatile int index; // 0 + volatile long count; + + @Override + public void onNext(List<ByteBuffer> items) { + try { + long sz = accumulatedDataSize(items); + boolean printStamp = delayMillis > 0 + && requestAmount < Long.MAX_VALUE + && count % 20 == 0; + if (printStamp) { + printStamp("stamp", "count=%d sz=%d accumulated=%d", + count, sz, (totalBytesReceived + sz)); + } + count++; + onNextInvocations++; + assertNotEquals(sz, 0L, "Unexpected empty buffers"); + items.stream().forEach(b -> assertEquals(b.position(), 0)); + assertFalse(noMoreOnNext); + + if (sz != bufferSize) { + String msg = sz + ", should be less than bufferSize, " + bufferSize; + assertTrue(sz < bufferSize, msg); + assertTrue(lastSeenSize == -1 || lastSeenSize == bufferSize); + noMoreOnNext = true; + wrongSizes++; + printStamp("onNext", + "Possibly received last buffer: sz=%d, accumulated=%d, total=%d", + sz, totalBytesReceived, totalBytesReceived + sz); + } else { + assertEquals(sz, bufferSize, "Expected to receive exactly bufferSize"); + } + lastSeenSize = sz; + + // Ensure expected contents + for (ByteBuffer b : items) { + while (b.hasRemaining()) { + assertEquals(b.get(), (byte) (index % 100)); + index++; + } + } + + totalBytesReceived += sz; + assertEquals(totalBytesReceived, index); + if (delayMillis > 0 && ((expectedTotalSize - totalBytesReceived) > bufferSize)) + delayedExecutor.execute(this::requestMore); + else + requestMore(); + } catch (Throwable t) { + completion.completeExceptionally(t); + } + } + + @Override + public void onError(Throwable throwable) { + completion.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + if (wrongSizes > 1) { // allow just the final item to be smaller + String msg = "Wrong sizes. Expected no more than 1. [" + this + "]"; + completion.completeExceptionally(new Throwable(msg)); + } + if (totalBytesReceived != expectedTotalSize) { + String msg = "Wrong number of bytes. [" + this + "]"; + completion.completeExceptionally(new Throwable(msg)); + } else { + completion.complete(totalBytesReceived); + } + } + + @Override + public CompletionStage<Integer> getBody() { + return completion; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(", bufferSize=").append(bufferSize); + sb.append(", onNextInvocations=").append(onNextInvocations); + sb.append(", totalBytesReceived=").append(totalBytesReceived); + sb.append(", expectedTotalSize=").append(expectedTotalSize); + sb.append(", requestAmount=").append(requestAmount); + sb.append(", lastSeenSize=").append(lastSeenSize); + sb.append(", wrongSizes=").append(wrongSizes); + sb.append(", index=").append(index); + return sb.toString(); + } + } + + /** + * Publishes data, through the given publisher, using the main thread. + * + * Note: The executor supplied when creating the SubmissionPublisher provides + * the threads for executing the Subscribers. + * + * @param publisher the publisher + * @param numBuffers the number of buffers to send ( before splitting in two ) + * @param bufferSize the total size of the data to send ( before splitting in two ) + */ + static void source(SubmissionPublisher<List<ByteBuffer>> publisher, + int numBuffers, + int bufferSize) { + printStamp("source","Publishing %d buffers of size %d each", numBuffers, bufferSize); + int index = 0; + for (int i=0; i<numBuffers; i++) { + int chunkSize = random.nextInt(bufferSize); + ByteBuffer buf1 = allocateBuffer(chunkSize, index); + index += chunkSize; + ByteBuffer buf2 = allocateBuffer(bufferSize - chunkSize, index); + index += bufferSize - chunkSize; + publisher.submit(List.of(buf1, buf2)); + } + printStamp("source", "complete"); + } + + /** + * Creates and subscribes Subscribers that receive data from the given + * publisher. + * + * @param publisher the publisher + * @param delayMillis time, in milliseconds, to delay the Subscription + * requesting more bytes ( for simulating slow consumption ) + * @param expectedTotalSize the total number of bytes expected to be received + * by the subscribers + * @return a CompletableFuture which completes when the subscription is complete + */ + static CompletableFuture<?> sink(SubmissionPublisher<List<ByteBuffer>> publisher, + int delayMillis, + int expectedTotalSize, + long requestAmount, + int maxBufferSize, + int minBufferSize) { + int bufferSize = chooseBufferSize(maxBufferSize, + minBufferSize, + delayMillis, + expectedTotalSize, + requestAmount); + assert bufferSize > 0; + assert bufferSize >= minBufferSize; + assert bufferSize <= maxBufferSize; + BodySubscriber<Integer> sub = TestSubscriber.createSubscriber(bufferSize, + delayMillis, + expectedTotalSize, + requestAmount); + publisher.subscribe(sub); + printStamp("sink","Subscriber reads data with buffer size: %d", bufferSize); + out.printf("Subscription delay is %d msec\n", delayMillis); + long delay = (((long)delayMillis * expectedTotalSize) / bufferSize) / requestAmount; + out.printf("Minimum total delay is %d sec %d ms\n", delay / 1000, delay % 1000); + out.printf("Request amount is %d items\n", requestAmount); + return sub.getBody().toCompletableFuture(); + } + + static int chooseBufferSize(int maxBufferSize, + int minBufferSize, + int delaysMillis, + int expectedTotalSize, + long requestAmount) { + assert minBufferSize > 0 && maxBufferSize > 0 && requestAmount > 0; + int bufferSize = maxBufferSize == minBufferSize ? maxBufferSize : + (random.nextInt(maxBufferSize - minBufferSize) + + minBufferSize); + if (requestAmount == Long.MAX_VALUE) return bufferSize; + long minDelay = (((long)delaysMillis * expectedTotalSize) / maxBufferSize) + / requestAmount; + long maxDelay = (((long)delaysMillis * expectedTotalSize) / minBufferSize) + / requestAmount; + // if the maximum delay is < 10s just take a random number between min and max. + if (maxDelay <= LOWER_THRESHOLD) { + return bufferSize; + } + // if minimum delay is greater than 20s then print a warning and use max buffer. + if (minDelay >= UPPER_THRESHOLD) { + System.out.println("Warning: minimum delay is " + + minDelay/1000 + "sec " + minDelay%1000 + "ms"); + System.err.println("Warning: minimum delay is " + + minDelay/1000 + "sec " + minDelay%1000 + "ms"); + return maxBufferSize; + } + // maxDelay could be anything, but minDelay is below the UPPER_THRESHOLD + // try to pick up a buffer size that keeps the delay below the + // UPPER_THRESHOLD + while (minBufferSize < maxBufferSize) { + bufferSize = random.nextInt(maxBufferSize - minBufferSize) + + minBufferSize; + long delay = (((long)delaysMillis * expectedTotalSize) / bufferSize) + / requestAmount; + if (delay < UPPER_THRESHOLD) return bufferSize; + minBufferSize++; + } + return minBufferSize; + } + + // --- + + /* Main entry point for standalone testing of the main functional test. */ + public static void main(String... args) { + BufferingSubscriberTest t = new BufferingSubscriberTest(); + for (Object[] objs : t.config()) + t.test((int)objs[0], (int)objs[1], (int)objs[2], (int)objs[3], (int)objs[4], (int)objs[5]); + } +} diff --git a/test/jdk/java/net/httpclient/CancelledResponse.java b/test/jdk/java/net/httpclient/CancelledResponse.java new file mode 100644 index 00000000000..04c28f6e979 --- /dev/null +++ b/test/jdk/java/net/httpclient/CancelledResponse.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2015, 2017, 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. + */ + +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpClient.Version; +import jdk.incubator.http.HttpHeaders; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.jshell.spi.ExecutionControl; +import jdk.testlibrary.SimpleSSLContext; + +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocketFactory; +import java.io.IOException; +import java.net.SocketException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import jdk.incubator.http.HttpResponse.BodyHandler; +import jdk.incubator.http.HttpResponse.BodySubscriber; + +import static java.lang.String.format; +import static java.lang.System.out; + +/** + * @test + * @bug 8087112 + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @build MockServer + * @run main/othervm CancelledResponse + * @run main/othervm CancelledResponse SSL + */ + +/** + * Similar test to SplitResponse except that the client will cancel the response + * before receiving it fully. + */ +public class CancelledResponse { + + static String response(String body, boolean serverKeepalive) { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP/1.1 200 OK\r\n"); + if (!serverKeepalive) + sb.append("Connection: Close\r\n"); + + sb.append("Content-length: ").append(body.length()).append("\r\n"); + sb.append("\r\n"); + sb.append(body); + return sb.toString(); + } + + static final String responses[] = { + "Lorem ipsum dolor sit amet consectetur adipiscing elit,", + "sed do eiusmod tempor quis nostrud exercitation ullamco laboris nisi ut", + "aliquip ex ea commodo consequat." + }; + + final ServerSocketFactory factory; + final SSLContext context; + final boolean useSSL; + CancelledResponse(boolean useSSL) throws IOException { + this.useSSL = useSSL; + context = new SimpleSSLContext().get(); + SSLContext.setDefault(context); + factory = useSSL ? SSLServerSocketFactory.getDefault() + : ServerSocketFactory.getDefault(); + } + + public HttpClient newHttpClient() { + HttpClient client; + if (useSSL) { + client = HttpClient.newBuilder() + .sslContext(context) + .build(); + } else { + client = HttpClient.newHttpClient(); + } + return client; + } + + public static void main(String[] args) throws Exception { + boolean useSSL = false; + if (args != null && args.length == 1) { + useSSL = "SSL".equals(args[0]); + } + CancelledResponse sp = new CancelledResponse(useSSL); + + for (Version version : Version.values()) { + for (boolean serverKeepalive : new boolean[]{ true, false }) { + // Note: the mock server doesn't support Keep-Alive, but + // pretending that it might exercises code paths in and out of + // the connection pool, and retry logic + for (boolean async : new boolean[]{ true, false }) { + sp.test(version, serverKeepalive, async); + } + } + } + } + + static class CancelException extends IOException { + } + + // @Test + void test(Version version, boolean serverKeepalive, boolean async) + throws Exception + { + out.println(format("*** version %s, serverKeepAlive: %s, async: %s ***", + version, serverKeepalive, async)); + MockServer server = new MockServer(0, factory); + URI uri = new URI(server.getURL()); + out.println("server is: " + uri); + server.start(); + + HttpClient client = newHttpClient(); + HttpRequest request = HttpRequest.newBuilder(uri).version(version).build(); + try { + for (int i = 0; i < responses.length; i++) { + HttpResponse<String> r = null; + CompletableFuture<HttpResponse<String>> cf1; + CancelException expected = null; + AtomicBoolean cancelled = new AtomicBoolean(); + + out.println("----- iteration " + i + " -----"); + String body = responses[i]; + Thread t = sendSplitResponse(response(body, serverKeepalive), server, cancelled); + + try { + if (async) { + out.println("send async: " + request); + cf1 = client.sendAsync(request, asString(body, cancelled)); + r = cf1.get(); + } else { // sync + out.println("send sync: " + request); + r = client.send(request, asString(body, cancelled)); + } + } catch (CancelException c1) { + System.out.println("Got expected exception: " + c1); + expected = c1; + } catch (IOException | ExecutionException | CompletionException c2) { + Throwable c = c2; + while (c != null && !(c instanceof CancelException)) { + c = c.getCause(); + } + if (c instanceof CancelException) { + System.out.println("Got expected exception: " + c); + expected = (CancelException)c; + } else throw c2; + } + if (r != null) { + if (r.statusCode() != 200) + throw new RuntimeException("Failed"); + + String rxbody = r.body(); + out.println("received " + rxbody); + if (!rxbody.equals(body)) + throw new RuntimeException(format("Expected:%s, got:%s", body, rxbody)); + } + t.join(); + conn.close(); + if (expected == null) { + throw new RuntimeException("Expected exception not raised for " + + i + " cancelled=" + cancelled.get()); + } + } + } finally { + server.close(); + } + System.out.println("OK"); + } + + static class CancellingSubscriber implements BodySubscriber<String> { + private final String expected; + private final CompletableFuture<String> result; + private Flow.Subscription subscription; + final AtomicInteger index = new AtomicInteger(); + final AtomicBoolean cancelled; + CancellingSubscriber(String expected, AtomicBoolean cancelled) { + this.cancelled = cancelled; + this.expected = expected; + result = new CompletableFuture<>(); + } + + @Override + public CompletionStage<String> getBody() { + return result; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + + @Override + public void onNext(List<ByteBuffer> item) { + //if (result.isDone()) + for (ByteBuffer b : item) { + while (b.hasRemaining() && !result.isDone()) { + int i = index.getAndIncrement(); + char at = expected.charAt(i); + byte[] data = new byte[b.remaining()]; + b.get(data); // we know that the server writes 1 char + String s = new String(data); + char c = s.charAt(0); + if (c != at) { + Throwable x = new IllegalStateException("char at " + + i + " is '" + c + "' expected '" + + at + "' for \"" + expected +"\""); + out.println("unexpected char received, cancelling"); + subscription.cancel(); + result.completeExceptionally(x); + return; + } + } + } + if (index.get() > 0 && !result.isDone()) { + // we should complete the result here, but let's + // see if we get something back... + out.println("Cancelling subscription after reading " + index.get()); + cancelled.set(true); + subscription.cancel(); + result.completeExceptionally(new CancelException()); + return; + } + if (!result.isDone()) { + out.println("requesting 1 more"); + subscription.request(1); + } + } + + @Override + public void onError(Throwable throwable) { + result.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + int len = index.get(); + if (len == expected.length()) { + result.complete(expected); + } else { + Throwable x = new IllegalStateException("received only " + + len + " chars, expected " + expected.length() + + " for \"" + expected +"\""); + result.completeExceptionally(x); + } + } + } + + static class CancellingHandler implements BodyHandler<String> { + final String expected; + final AtomicBoolean cancelled; + CancellingHandler(String expected, AtomicBoolean cancelled) { + this.expected = expected; + this.cancelled = cancelled; + } + @Override + public BodySubscriber<String> apply(int statusCode, HttpHeaders responseHeaders) { + assert !cancelled.get(); + return new CancellingSubscriber(expected, cancelled); + } + } + + BodyHandler<String> asString(String expected, AtomicBoolean cancelled) { + return new CancellingHandler(expected, cancelled); + } + + // required for cleanup + volatile MockServer.Connection conn; + + // Sends the response, mostly, one byte at a time with a small delay + // between bytes, to encourage that each byte is read in a separate read + Thread sendSplitResponse(String s, MockServer server, AtomicBoolean cancelled) { + System.out.println("Sending: "); + Thread t = new Thread(() -> { + System.out.println("Waiting for server to receive headers"); + conn = server.activity(); + System.out.println("Start sending response"); + int sent = 0; + try { + int len = s.length(); + out.println("sending " + s); + for (int i = 0; i < len; i++) { + String onechar = s.substring(i, i + 1); + conn.send(onechar); + sent++; + Thread.sleep(10); + } + out.println("sent " + s); + } catch (SSLException | SocketException x) { + // if SSL then we might get a "Broken Pipe", otherwise + // a "Socket closed". + boolean expected = cancelled.get(); + if (sent > 0 && expected) { + System.out.println("Connection closed by peer as expected: " + x); + return; + } else { + System.out.println("Unexpected exception (sent=" + + sent + ", cancelled=" + expected + "): " + x); + throw new RuntimeException(x); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + t.setDaemon(true); + t.start(); + return t; + } +} diff --git a/test/jdk/java/net/httpclient/CustomRequestPublisher.java b/test/jdk/java/net/httpclient/CustomRequestPublisher.java new file mode 100644 index 00000000000..399921fc8e9 --- /dev/null +++ b/test/jdk/java/net/httpclient/CustomRequestPublisher.java @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2017, 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 + * @summary Checks correct handling of Publishers that call onComplete without demand + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * java.logging + * jdk.httpserver + * @library /lib/testlibrary http2/server + * @build Http2TestServer + * @build jdk.testlibrary.SimpleSSLContext + * @run testng/othervm CustomRequestPublisher + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import javax.net.ssl.SSLContext; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.System.out; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static jdk.incubator.http.HttpResponse.BodyHandler.asString; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class CustomRequestPublisher { + + SSLContext sslContext; + HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpsServer httpsTestServer; // HTTPS/1.1 + Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI; + String httpsURI; + String http2URI; + String https2URI; + + @DataProvider(name = "variants") + public Object[][] variants() { + Supplier<BodyPublisher> fixedSupplier = () -> new FixedLengthBodyPublisher(); + Supplier<BodyPublisher> unknownSupplier = () -> new UnknownLengthBodyPublisher(); + + return new Object[][]{ + { httpURI, fixedSupplier, false }, + { httpURI, unknownSupplier, false }, + { httpsURI, fixedSupplier, false }, + { httpsURI, unknownSupplier, false }, + { http2URI, fixedSupplier, false }, + { http2URI, unknownSupplier, false }, + { https2URI, fixedSupplier, false,}, + { https2URI, unknownSupplier, false }, + + { httpURI, fixedSupplier, true }, + { httpURI, unknownSupplier, true }, + { httpsURI, fixedSupplier, true }, + { httpsURI, unknownSupplier, true }, + { http2URI, fixedSupplier, true }, + { http2URI, unknownSupplier, true }, + { https2URI, fixedSupplier, true,}, + { https2URI, unknownSupplier, true }, + }; + } + + static final int ITERATION_COUNT = 10; + + @Test(dataProvider = "variants") + void test(String uri, Supplier<BodyPublisher> bpSupplier, boolean sameClient) + throws Exception + { + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = HttpClient.newBuilder().sslContext(sslContext).build(); + + BodyPublisher bodyPublisher = bpSupplier.get(); + HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) + .POST(bodyPublisher) + .build(); + + HttpResponse<String> resp = client.send(request, asString()); + + out.println("Got response: " + resp); + out.println("Got body: " + resp.body()); + assertTrue(resp.statusCode() == 200, + "Expected 200, got:" + resp.statusCode()); + assertEquals(resp.body(), bodyPublisher.bodyAsString()); + } + } + + @Test(dataProvider = "variants") + void testAsync(String uri, Supplier<BodyPublisher> bpSupplier, boolean sameClient) + throws Exception + { + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = HttpClient.newBuilder().sslContext(sslContext).build(); + + BodyPublisher bodyPublisher = bpSupplier.get(); + HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) + .POST(bodyPublisher) + .build(); + + CompletableFuture<HttpResponse<String>> cf = client.sendAsync(request, asString()); + HttpResponse<String> resp = cf.get(); + + out.println("Got response: " + resp); + out.println("Got body: " + resp.body()); + assertTrue(resp.statusCode() == 200, + "Expected 200, got:" + resp.statusCode()); + assertEquals(resp.body(), bodyPublisher.bodyAsString()); + } + } + + /** A Publisher that returns an UNKNOWN content length. */ + static class UnknownLengthBodyPublisher extends BodyPublisher { + @Override + public long contentLength() { + return -1; // unknown + } + } + + /** A Publisher that returns a FIXED content length. */ + static class FixedLengthBodyPublisher extends BodyPublisher { + final int LENGTH = Arrays.stream(BODY) + .mapToInt(s-> s.getBytes(US_ASCII).length) + .sum(); + @Override + public long contentLength() { + return LENGTH; + } + } + + /** + * A Publisher that ( quite correctly ) invokes onComplete, after the last + * item has been published, even without any outstanding demand. + */ + static abstract class BodyPublisher implements HttpRequest.BodyPublisher { + + String[] BODY = new String[] + { "Say ", "Hello ", "To ", "My ", "Little ", "Friend" }; + + protected volatile Flow.Subscriber subscriber; + + @Override + public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) { + this.subscriber = subscriber; + subscriber.onSubscribe(new InternalSubscription()); + } + + @Override + public abstract long contentLength(); + + String bodyAsString() { + return Arrays.stream(BODY).collect(Collectors.joining()); + } + + class InternalSubscription implements Flow.Subscription { + + private final AtomicLong demand = new AtomicLong(); + private final AtomicBoolean cancelled = new AtomicBoolean(); + private volatile int position; + + private static final int IDLE = 1; + private static final int PUSHING = 2; + private static final int AGAIN = 4; + private final AtomicInteger state = new AtomicInteger(IDLE); + + @Override + public void request(long n) { + if (n <= 0L) { + subscriber.onError(new IllegalArgumentException( + "non-positive subscription request")); + return; + } + if (cancelled.get()) { + return; + } + + while (true) { + long prev = demand.get(), d; + if ((d = prev + n) < prev) // saturate + d = Long.MAX_VALUE; + if (demand.compareAndSet(prev, d)) + break; + } + + while (true) { + int s = state.get(); + if (s == IDLE) { + if (state.compareAndSet(IDLE, PUSHING)) { + while (true) { + push(); + if (state.compareAndSet(PUSHING, IDLE)) + return; + else if (state.compareAndSet(AGAIN, PUSHING)) + continue; + } + } + } else if (s == PUSHING) { + if (state.compareAndSet(PUSHING, AGAIN)) + return; + } else if (s == AGAIN){ + // do nothing, the pusher will already rerun + return; + } else { + throw new AssertionError("Unknown state:" + s); + } + } + } + + private void push() { + long prev; + while ((prev = demand.get()) > 0) { + if (!demand.compareAndSet(prev, prev -1)) + continue; + + int index = position; + if (index < BODY.length) { + position++; + subscriber.onNext(ByteBuffer.wrap(BODY[index].getBytes(US_ASCII))); + } + } + + if (position == BODY.length && !cancelled.get()) { + cancelled.set(true); + subscriber.onComplete(); // NOTE: onComplete without demand + } + } + + @Override + public void cancel() { + if (cancelled.compareAndExchange(false, true)) + return; // already cancelled + } + } + } + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + InetSocketAddress sa = new InetSocketAddress("localhost", 0); + httpTestServer = HttpServer.create(sa, 0); + httpTestServer.createContext("/http1/echo", new Http1EchoHandler()); + httpURI = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/echo"; + + httpsTestServer = HttpsServer.create(sa, 0); + httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer.createContext("/https1/echo", new Http1EchoHandler()); + httpsURI = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/echo"; + + http2TestServer = new Http2TestServer("127.0.0.1", false, 0); + http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo"); + int port = http2TestServer.getAddress().getPort(); + http2URI = "http://127.0.0.1:" + port + "/http2/echo"; + + https2TestServer = new Http2TestServer("127.0.0.1", true, 0); + https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo"); + port = https2TestServer.getAddress().getPort(); + https2URI = "https://127.0.0.1:" + port + "/https2/echo"; + + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + httpTestServer.stop(0); + httpsTestServer.stop(0); + http2TestServer.stop(); + https2TestServer.stop(); + } + + static class Http1EchoHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + try (InputStream is = t.getRequestBody(); + OutputStream os = t.getResponseBody()) { + byte[] bytes = is.readAllBytes(); + t.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + } + + static class Http2EchoHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + try (InputStream is = t.getRequestBody(); + OutputStream os = t.getResponseBody()) { + byte[] bytes = is.readAllBytes(); + t.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + } +} diff --git a/test/jdk/java/net/httpclient/CustomResponseSubscriber.java b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java new file mode 100644 index 00000000000..290b4de9262 --- /dev/null +++ b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2017, 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 + * @summary Tests response body subscribers's onComplete is not invoked before onSubscribe + * @library /lib/testlibrary http2/server + * @build jdk.testlibrary.SimpleSSLContext + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @run testng/othervm CustomResponseSubscriber + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpHeaders; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpResponse.BodyHandler; +import jdk.incubator.http.HttpResponse.BodySubscriber; +import javax.net.ssl.SSLContext; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.System.out; +import static java.nio.charset.StandardCharsets.UTF_8; +import static jdk.incubator.http.HttpResponse.BodySubscriber.asString; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class CustomResponseSubscriber { + + SSLContext sslContext; + HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpsServer httpsTestServer; // HTTPS/1.1 + Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + String httpURI_fixed; + String httpURI_chunk; + String httpsURI_fixed; + String httpsURI_chunk; + String http2URI_fixed; + String http2URI_chunk; + String https2URI_fixed; + String https2URI_chunk; + + static final int ITERATION_COUNT = 10; + // a shared executor helps reduce the amount of threads created by the test + static final Executor executor = Executors.newCachedThreadPool(); + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][]{ + { httpURI_fixed, false }, + { httpURI_chunk, false }, + { httpsURI_fixed, false }, + { httpsURI_chunk, false }, + { http2URI_fixed, false }, + { http2URI_chunk, false }, + { https2URI_fixed, false,}, + { https2URI_chunk, false }, + + { httpURI_fixed, true }, + { httpURI_chunk, true }, + { httpsURI_fixed, true }, + { httpsURI_chunk, true }, + { http2URI_fixed, true }, + { http2URI_chunk, true }, + { https2URI_fixed, true,}, + { https2URI_chunk, true }, + }; + } + + HttpClient newHttpClient() { + return HttpClient.newBuilder() + .executor(executor) + .sslContext(sslContext) + .build(); + } + + @Test(dataProvider = "variants") + public void testAsString(String uri, boolean sameClient) throws Exception { + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .build(); + BodyHandler<String> handler = new CRSBodyHandler(); + HttpResponse<String> response = client.send(req, handler); + String body = response.body(); + assertEquals(body, ""); + } + } + + static class CRSBodyHandler implements BodyHandler<String> { + @Override + public BodySubscriber<String> apply(int statusCode, HttpHeaders responseHeaders) { + assertEquals(statusCode, 200); + return new CRSBodySubscriber(); + } + } + + static class CRSBodySubscriber implements BodySubscriber<String> { + private final BodySubscriber<String> asString = asString(UTF_8); + volatile boolean onSubscribeCalled; + + @Override + public void onSubscribe(Flow.Subscription subscription) { + //out.println("onSubscribe "); + onSubscribeCalled = true; + asString.onSubscribe(subscription); + } + + @Override + public void onNext(List<ByteBuffer> item) { + // out.println("onNext " + item); + assertTrue(onSubscribeCalled); + asString.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + //out.println("onError"); + assertTrue(onSubscribeCalled); + asString.onError(throwable); + } + + @Override + public void onComplete() { + //out.println("onComplete"); + assertTrue(onSubscribeCalled, "onComplete called before onSubscribe"); + asString.onComplete(); + } + + @Override + public CompletionStage<String> getBody() { + return asString.getBody(); + } + } + + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + // HTTP/1.1 + HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler(); + HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler(); + InetSocketAddress sa = new InetSocketAddress("localhost", 0); + httpTestServer = HttpServer.create(sa, 0); + httpTestServer.createContext("/http1/fixed", h1_fixedLengthHandler); + httpTestServer.createContext("/http1/chunk", h1_chunkHandler); + httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/fixed"; + httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/chunk"; + + httpsTestServer = HttpsServer.create(sa, 0); + httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsTestServer.createContext("/https1/fixed", h1_fixedLengthHandler); + httpsTestServer.createContext("/https1/chunk", h1_chunkHandler); + httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/fixed"; + httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/chunk"; + + // HTTP/2 + Http2Handler h2_fixedLengthHandler = new HTTP2_FixedLengthHandler(); + Http2Handler h2_chunkedHandler = new HTTP2_VariableHandler(); + + http2TestServer = new Http2TestServer("127.0.0.1", false, 0); + http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed"); + http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk"); + int port = http2TestServer.getAddress().getPort(); + http2URI_fixed = "http://127.0.0.1:" + port + "/http2/fixed"; + http2URI_chunk = "http://127.0.0.1:" + port + "/http2/chunk"; + + https2TestServer = new Http2TestServer("127.0.0.1", true, 0); + https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed"); + https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk"); + port = https2TestServer.getAddress().getPort(); + https2URI_fixed = "https://127.0.0.1:" + port + "/https2/fixed"; + https2URI_chunk = "https://127.0.0.1:" + port + "/https2/chunk"; + + httpTestServer.start(); + httpsTestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + httpTestServer.stop(0); + httpsTestServer.stop(0); + http2TestServer.stop(); + https2TestServer.stop(); + } + + static class HTTP1_FixedLengthHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + out.println("HTTP1_FixedLengthHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, -1); //no body + } + } + + static class HTTP1_ChunkedHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + out.println("HTTP1_ChunkedHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, 0); // chunked + t.getResponseBody().close(); // no body + } + } + + static class HTTP2_FixedLengthHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + out.println("HTTP2_FixedLengthHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, 0); + t.getResponseBody().close(); + } + } + + static class HTTP2_VariableHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + out.println("HTTP2_VariableHandler received request to " + t.getRequestURI()); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + t.sendResponseHeaders(200, -1); // variable + t.getResponseBody().close(); //no body + } + } +} diff --git a/test/jdk/java/net/httpclient/EchoHandler.java b/test/jdk/java/net/httpclient/EchoHandler.java index 37c8614409f..2fb3a9a1acc 100644 --- a/test/jdk/java/net/httpclient/EchoHandler.java +++ b/test/jdk/java/net/httpclient/EchoHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/HandshakeFailureTest.java b/test/jdk/java/net/httpclient/HandshakeFailureTest.java new file mode 100644 index 00000000000..760e5a947c2 --- /dev/null +++ b/test/jdk/java/net/httpclient/HandshakeFailureTest.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2017, 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. + */ + +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLSocket; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpClient.Version; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpRequest; +import static java.lang.System.out; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; + +/** + * @test + * @run main/othervm HandshakeFailureTest + * @summary Verify SSLHandshakeException is received when the handshake fails, + * either because the server closes ( EOF ) the connection during handshaking + * or no cipher suite ( or similar ) can be negotiated. + */ +// To switch on debugging use: +// @run main/othervm -Djdk.internal.httpclient.debug=true HandshakeFailureTest +public class HandshakeFailureTest { + + // The number of iterations each testXXXClient performs. Can be increased + // when running standalone testing. + static final int TIMES = 10; + + public static void main(String[] args) throws Exception { + HandshakeFailureTest test = new HandshakeFailureTest(); + List<AbstractServer> servers = List.of( new PlainServer(), new SSLServer()); + + for (AbstractServer server : servers) { + try (server) { + out.format("%n%n------ Testing with server:%s ------%n", server); + URI uri = new URI("https://127.0.0.1:" + server.getPort() + "/"); + + test.testSyncSameClient(uri, Version.HTTP_1_1); + test.testSyncSameClient(uri, Version.HTTP_2); + test.testSyncDiffClient(uri, Version.HTTP_1_1); + test.testSyncDiffClient(uri, Version.HTTP_2); + + test.testAsyncSameClient(uri, Version.HTTP_1_1); + test.testAsyncSameClient(uri, Version.HTTP_2); + test.testAsyncDiffClient(uri, Version.HTTP_1_1); + test.testAsyncDiffClient(uri, Version.HTTP_2); + } + } + } + + void testSyncSameClient(URI uri, Version version) throws Exception { + out.printf("%n--- testSyncSameClient %s ---%n", version); + HttpClient client = HttpClient.newHttpClient(); + for (int i = 0; i < TIMES; i++) { + out.printf("iteration %d%n", i); + HttpRequest request = HttpRequest.newBuilder(uri) + .version(version) + .build(); + try { + HttpResponse<Void> response = client.send(request, discard(null)); + String msg = String.format("UNEXPECTED response=%s%n", response); + throw new RuntimeException(msg); + } catch (SSLHandshakeException expected) { + out.printf("Client: caught expected exception: %s%n", expected); + } + } + } + + void testSyncDiffClient(URI uri, Version version) throws Exception { + out.printf("%n--- testSyncDiffClient %s ---%n", version); + for (int i = 0; i < TIMES; i++) { + out.printf("iteration %d%n", i); + // a new client each time + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder(uri) + .version(version) + .build(); + try { + HttpResponse<Void> response = client.send(request, discard(null)); + String msg = String.format("UNEXPECTED response=%s%n", response); + throw new RuntimeException(msg); + } catch (SSLHandshakeException expected) { + out.printf("Client: caught expected exception: %s%n", expected); + } + } + } + + void testAsyncSameClient(URI uri, Version version) throws Exception { + out.printf("%n--- testAsyncSameClient %s ---%n", version); + HttpClient client = HttpClient.newHttpClient(); + for (int i = 0; i < TIMES; i++) { + out.printf("iteration %d%n", i); + HttpRequest request = HttpRequest.newBuilder(uri) + .version(version) + .build(); + CompletableFuture<HttpResponse<Void>> response = + client.sendAsync(request, discard(null)); + try { + response.join(); + String msg = String.format("UNEXPECTED response=%s%n", response); + throw new RuntimeException(msg); + } catch (CompletionException ce) { + if (ce.getCause() instanceof SSLHandshakeException) { + out.printf("Client: caught expected exception: %s%n", ce.getCause()); + } else { + out.printf("Client: caught UNEXPECTED exception: %s%n", ce.getCause()); + throw ce; + } + } + } + } + + void testAsyncDiffClient(URI uri, Version version) throws Exception { + out.printf("%n--- testAsyncDiffClient %s ---%n", version); + for (int i = 0; i < TIMES; i++) { + out.printf("iteration %d%n", i); + // a new client each time + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder(uri) + .version(version) + .build(); + CompletableFuture<HttpResponse<Void>> response = + client.sendAsync(request, discard(null)); + try { + response.join(); + String msg = String.format("UNEXPECTED response=%s%n", response); + throw new RuntimeException(msg); + } catch (CompletionException ce) { + if (ce.getCause() instanceof SSLHandshakeException) { + out.printf("Client: caught expected exception: %s%n", ce.getCause()); + } else { + out.printf("Client: caught UNEXPECTED exception: %s%n", ce.getCause()); + throw ce; + } + } + } + } + + /** Common supertype for PlainServer and SSLServer. */ + static abstract class AbstractServer extends Thread implements AutoCloseable { + protected final ServerSocket ss; + protected volatile boolean closed; + + AbstractServer(String name, ServerSocket ss) throws IOException { + super(name); + this.ss = ss; + this.start(); + } + + int getPort() { return ss.getLocalPort(); } + + @Override + public void close() { + if (closed) + return; + closed = true; + try { + ss.close(); + } catch (IOException e) { + throw new UncheckedIOException("Unexpected", e); + } + } + } + + /** Emulates a server-side, using plain cleartext Sockets, that just closes + * the connection, after a small variable delay. */ + static class PlainServer extends AbstractServer { + private volatile int count; + + PlainServer() throws IOException { + super("PlainServer", new ServerSocket(0)); + } + + @Override + public void run() { + while (!closed) { + try (Socket s = ss.accept()) { + count++; + + /* SSL record layer - contains the client hello + struct { + uint8 major, minor; + } ProtocolVersion; + + enum { + change_cipher_spec(20), alert(21), handshake(22), + application_data(23), (255) + } ContentType; + + struct { + ContentType type; + ProtocolVersion version; + uint16 length; + opaque fragment[SSLPlaintext.length]; + } SSLPlaintext; */ + DataInputStream din = new DataInputStream(s.getInputStream()); + int contentType = din.read(); + out.println("ContentType:" + contentType); + int majorVersion = din.read(); + out.println("Major:" + majorVersion); + int minorVersion = din.read(); + out.println("Minor:" + minorVersion); + int length = din.readShort(); + out.println("length:" + length); + byte[] ba = new byte[length]; + din.readFully(ba); + + // simulate various delays in response + Thread.sleep(10 * (count % 10)); + s.close(); // close without giving any reply + } catch (IOException e) { + if (!closed) + out.println("Unexpected" + e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + + /** Emulates a server-side, using SSL Sockets, that will fail during + * handshaking, as there are no cipher suites in common. */ + static class SSLServer extends AbstractServer { + static final SSLContext sslContext = createUntrustingContext(); + static final ServerSocketFactory factory = sslContext.getServerSocketFactory(); + + static SSLContext createUntrustingContext() { + try { + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(null, null, null); + return sslContext; + } catch (Throwable t) { + throw new AssertionError(t); + } + } + + SSLServer() throws IOException { + super("SSLServer", factory.createServerSocket(0)); + } + + @Override + public void run() { + while (!closed) { + try (SSLSocket s = (SSLSocket)ss.accept()) { + s.getInputStream().read(); // will throw SHE here + + throw new AssertionError("Should not reach here"); + } catch (SSLHandshakeException expected) { + // Expected: SSLHandshakeException: no cipher suites in common + out.printf("Server: caught expected exception: %s%n", expected); + } catch (IOException e) { + if (!closed) + out.printf("UNEXPECTED %s", e); + } + } + } + } +} diff --git a/test/jdk/java/net/httpclient/HeadersTest.java b/test/jdk/java/net/httpclient/HeadersTest.java index 086025c8388..d724873e447 100644 --- a/test/jdk/java/net/httpclient/HeadersTest.java +++ b/test/jdk/java/net/httpclient/HeadersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/HeadersTest1.java b/test/jdk/java/net/httpclient/HeadersTest1.java index 2777c347568..d03405ae234 100644 --- a/test/jdk/java/net/httpclient/HeadersTest1.java +++ b/test/jdk/java/net/httpclient/HeadersTest1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -95,16 +95,26 @@ public class HeadersTest1 { assertTrue(v1.isEmpty(), String.valueOf(v1)); TestKit.assertUnmodifiableList(v1); - List<String> v2 = hd.allValues("X-Foo-Response"); - assertNotNull(v2); - assertEquals(new HashSet<>(v2), Set.of("resp1", "resp2")); - TestKit.assertUnmodifiableList(v2); + // case insensitive + List<String> headernames = List.of("X-Foo-Response", + "x-foo-Response", + "x-fOo-REspoNse"); + for (String headerName : headernames) { + List<String> v2 = hd.allValues(headerName); + assertNotNull(v2); + assertEquals(new HashSet<>(v2), Set.of("resp1", "resp2")); + TestKit.assertUnmodifiableList(v2); + } Map<String, List<String>> map = hd.map(); TestKit.assertUnmodifiableMap(map); for (List<String> values : map.values()) { TestKit.assertUnmodifiableList(values); } + + // toString + hd.toString().toLowerCase().contains("content-length"); + hd.toString().toLowerCase().contains("x-foo-response"); } finally { server.stop(0); e.shutdownNow(); diff --git a/test/jdk/java/net/httpclient/HeadersTest2.java b/test/jdk/java/net/httpclient/HeadersTest2.java new file mode 100644 index 00000000000..54ebac13d68 --- /dev/null +++ b/test/jdk/java/net/httpclient/HeadersTest2.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015, 2017, 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 8087112 + * @summary Basic test for headers + */ + +import jdk.incubator.http.HttpHeaders; +import jdk.incubator.http.HttpRequest; +import java.net.URI; +import java.util.List; +import java.util.Iterator; + +public class HeadersTest2 { + static URI uri = URI.create("http://www.foo.com/"); + + static class CompareTest { + boolean succeed; + List<String> nameValues1; + List<String> nameValues2; + + + /** + * Each list contains header-name, header-value, header-name, header-value + * sequences. The test creates two requests with the two lists + * and compares the HttpHeaders objects returned from the requests + */ + CompareTest(boolean succeed, List<String> l1, List<String> l2) { + this.succeed = succeed; + this.nameValues1 = l1; + this.nameValues2 = l2; + } + + public void run() { + HttpRequest r1 = getRequest(nameValues1); + HttpRequest r2 = getRequest(nameValues2); + HttpHeaders h1 = r1.headers(); + HttpHeaders h2 = r2.headers(); + boolean equal = h1.equals(h2); + if (equal && !succeed) { + System.err.println("Expected failure"); + print(nameValues1); + print(nameValues2); + throw new RuntimeException(); + } else if (!equal && succeed) { + System.err.println("Expected success"); + print(nameValues1); + print(nameValues2); + throw new RuntimeException(); + } + + // Ensures that headers never equal a non-HttpHeaders type + if (h1.equals(new Object())) + throw new RuntimeException("Unexpected h1 equals Object"); + + if (h2.equals(r1)) + throw new RuntimeException("Unexpected h2 equals r1"); + } + + static void print(List<String> list) { + System.err.print("{"); + for (String s : list) { + System.err.print(s + " "); + } + System.err.println("}"); + } + + HttpRequest getRequest(List<String> headers) { + HttpRequest.Builder builder = HttpRequest.newBuilder(uri); + Iterator<String> iterator = headers.iterator(); + while (iterator.hasNext()) { + String name = iterator.next(); + String value = iterator.next(); + builder.header(name, value); + } + return builder.GET().build(); + } + } + + static CompareTest test(boolean s, List<String> l1, List<String> l2) { + return new CompareTest(s, l1, l2); + } + + static CompareTest[] compareTests = new CompareTest[] { + test(true, List.of("Dontent-length", "99"), List.of("dontent-length", "99")), + test(false, List.of("Dontent-length", "99"), List.of("dontent-length", "100")), + test(false, List.of("Name1", "val1", "Name1", "val2", "name1", "val3"), + List.of("Name1", "val1", "Name1", "val2")), + test(true, List.of("Name1", "val1", "Name1", "val2", "name1", "val3"), + List.of("NaMe1", "val1", "NAme1", "val2", "name1", "val3")) + }; + + public static void main(String[] args) { + for (CompareTest test : compareTests) { + test.run(); + } + } +} + diff --git a/test/jdk/java/net/httpclient/HttpClientBuilderTest.java b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java new file mode 100644 index 00000000000..b0aaefdd90a --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.lang.reflect.Method; +import java.net.Authenticator; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.InetSocketAddress; +import java.net.ProxySelector; +import java.util.List; +import java.util.concurrent.Executor; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpClient.Redirect; +import jdk.incubator.http.HttpClient.Version; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/* + * @test + * @summary HttpClient[.Builder] API and behaviour checks + * @library /lib/testlibrary/ + * @build jdk.testlibrary.SimpleSSLContext + * @run testng HttpClientBuilderTest + */ + +public class HttpClientBuilderTest { + + @Test + public void testDefaults() throws Exception { + List<HttpClient> clients = List.of(HttpClient.newHttpClient(), + HttpClient.newBuilder().build()); + + for (HttpClient client : clients) { + // Empty optionals and defaults + assertFalse(client.authenticator().isPresent()); + assertFalse(client.cookieHandler().isPresent()); + assertFalse(client.executor().isPresent()); + assertFalse(client.proxy().isPresent()); + assertTrue(client.sslParameters() != null); + assertTrue(client.followRedirects().equals(HttpClient.Redirect.NEVER)); + assertTrue(client.sslContext() == SSLContext.getDefault()); + assertTrue(client.version().equals(HttpClient.Version.HTTP_2)); + } + } + + @Test + public void testNull() throws Exception { + HttpClient.Builder builder = HttpClient.newBuilder(); + assertThrows(NullPointerException.class, () -> builder.authenticator(null)); + assertThrows(NullPointerException.class, () -> builder.cookieHandler(null)); + assertThrows(NullPointerException.class, () -> builder.executor(null)); + assertThrows(NullPointerException.class, () -> builder.proxy(null)); + assertThrows(NullPointerException.class, () -> builder.sslParameters(null)); + assertThrows(NullPointerException.class, () -> builder.followRedirects(null)); + assertThrows(NullPointerException.class, () -> builder.sslContext(null)); + assertThrows(NullPointerException.class, () -> builder.version(null)); + } + + static class TestAuthenticator extends Authenticator { } + + @Test + public void testAuthenticator() { + HttpClient.Builder builder = HttpClient.newBuilder(); + Authenticator a = new TestAuthenticator(); + builder.authenticator(a); + assertTrue(builder.build().authenticator().get() == a); + Authenticator b = new TestAuthenticator(); + builder.authenticator(b); + assertTrue(builder.build().authenticator().get() == b); + assertThrows(NullPointerException.class, () -> builder.authenticator(null)); + Authenticator c = new TestAuthenticator(); + builder.authenticator(c); + assertTrue(builder.build().authenticator().get() == c); + } + + @Test + public void testCookieHandler() { + HttpClient.Builder builder = HttpClient.newBuilder(); + CookieHandler a = new CookieManager(); + builder.cookieHandler(a); + assertTrue(builder.build().cookieHandler().get() == a); + CookieHandler b = new CookieManager(); + builder.cookieHandler(b); + assertTrue(builder.build().cookieHandler().get() == b); + assertThrows(NullPointerException.class, () -> builder.cookieHandler(null)); + CookieManager c = new CookieManager(); + builder.cookieHandler(c); + assertTrue(builder.build().cookieHandler().get() == c); + } + + static class TestExecutor implements Executor { + public void execute(Runnable r) { } + } + + @Test + public void testExecutor() { + HttpClient.Builder builder = HttpClient.newBuilder(); + TestExecutor a = new TestExecutor(); + builder.executor(a); + assertTrue(builder.build().executor().get() == a); + TestExecutor b = new TestExecutor(); + builder.executor(b); + assertTrue(builder.build().executor().get() == b); + assertThrows(NullPointerException.class, () -> builder.executor(null)); + TestExecutor c = new TestExecutor(); + builder.executor(c); + assertTrue(builder.build().executor().get() == c); + } + + @Test + public void testProxySelector() { + HttpClient.Builder builder = HttpClient.newBuilder(); + ProxySelector a = ProxySelector.of(null); + builder.proxy(a); + assertTrue(builder.build().proxy().get() == a); + ProxySelector b = ProxySelector.of(InetSocketAddress.createUnresolved("foo", 80)); + builder.proxy(b); + assertTrue(builder.build().proxy().get() == b); + assertThrows(NullPointerException.class, () -> builder.proxy(null)); + ProxySelector c = ProxySelector.of(InetSocketAddress.createUnresolved("bar", 80)); + builder.proxy(c); + assertTrue(builder.build().proxy().get() == c); + } + + @Test + public void testSSLParameters() { + HttpClient.Builder builder = HttpClient.newBuilder(); + SSLParameters a = new SSLParameters(); + a.setCipherSuites(new String[] { "A" }); + builder.sslParameters(a); + a.setCipherSuites(new String[] { "Z" }); + assertTrue(builder.build().sslParameters() != (a)); + assertTrue(builder.build().sslParameters().getCipherSuites()[0].equals("A")); + SSLParameters b = new SSLParameters(); + b.setEnableRetransmissions(true); + builder.sslParameters(b); + assertTrue(builder.build().sslParameters() != b); + assertTrue(builder.build().sslParameters().getEnableRetransmissions()); + assertThrows(NullPointerException.class, () -> builder.sslParameters(null)); + SSLParameters c = new SSLParameters(); + c.setProtocols(new String[] { "C" }); + builder.sslParameters(c); + c.setProtocols(new String[] { "D" }); + assertTrue(builder.build().sslParameters().getProtocols()[0].equals("C")); + } + + @Test + public void testSSLContext() throws Exception { + HttpClient.Builder builder = HttpClient.newBuilder(); + SSLContext a = (new SimpleSSLContext()).get(); + builder.sslContext(a); + assertTrue(builder.build().sslContext() == a); + SSLContext b = (new SimpleSSLContext()).get(); + builder.sslContext(b); + assertTrue(builder.build().sslContext() == b); + assertThrows(NullPointerException.class, () -> builder.sslContext(null)); + SSLContext c = (new SimpleSSLContext()).get(); + builder.sslContext(c); + assertTrue(builder.build().sslContext() == c); + } + + @Test + public void testFollowRedirects() { + HttpClient.Builder builder = HttpClient.newBuilder(); + builder.followRedirects(Redirect.ALWAYS); + assertTrue(builder.build().followRedirects() == Redirect.ALWAYS); + builder.followRedirects(Redirect.NEVER); + assertTrue(builder.build().followRedirects() == Redirect.NEVER); + assertThrows(NullPointerException.class, () -> builder.followRedirects(null)); + builder.followRedirects(Redirect.SAME_PROTOCOL); + assertTrue(builder.build().followRedirects() == Redirect.SAME_PROTOCOL); + builder.followRedirects(Redirect.SECURE); + assertTrue(builder.build().followRedirects() == Redirect.SECURE); + } + + @Test + public void testVersion() { + HttpClient.Builder builder = HttpClient.newBuilder(); + builder.version(Version.HTTP_2); + assertTrue(builder.build().version() == Version.HTTP_2); + builder.version(Version.HTTP_1_1); + assertTrue(builder.build().version() == Version.HTTP_1_1); + assertThrows(NullPointerException.class, () -> builder.version(null)); + builder.version(Version.HTTP_2); + assertTrue(builder.build().version() == Version.HTTP_2); + builder.version(Version.HTTP_1_1); + assertTrue(builder.build().version() == Version.HTTP_1_1); + } + + @Test + static void testPriority() throws Exception { + HttpClient.Builder builder = HttpClient.newBuilder(); + assertThrows(IllegalArgumentException.class, () -> builder.priority(-1)); + assertThrows(IllegalArgumentException.class, () -> builder.priority(0)); + assertThrows(IllegalArgumentException.class, () -> builder.priority(257)); + assertThrows(IllegalArgumentException.class, () -> builder.priority(500)); + + builder.priority(1); + builder.build(); + builder.priority(256); + builder.build(); + } + + + /* ---- standalone entry point ---- */ + + public static void main(String[] args) throws Exception { + HttpClientBuilderTest test = new HttpClientBuilderTest(); + for (Method m : HttpClientBuilderTest.class.getDeclaredMethods()) { + if (m.isAnnotationPresent(Test.class)) { + try { + m.invoke(test); + System.out.printf("test %s: success%n", m.getName()); + } catch (Throwable t ) { + System.out.printf("test %s: failed%n", m.getName()); + t.printStackTrace(); + } + } + } + } +} diff --git a/test/jdk/java/net/httpclient/HttpInputStreamTest.java b/test/jdk/java/net/httpclient/HttpInputStreamTest.java index 0851fed7db8..150cfabaf12 100644 --- a/test/jdk/java/net/httpclient/HttpInputStreamTest.java +++ b/test/jdk/java/net/httpclient/HttpInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -32,6 +32,8 @@ import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; @@ -40,11 +42,12 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Flow; import java.util.stream.Stream; +import static java.lang.System.err; /* * @test * @summary An example on how to read a response body with InputStream... - * @run main/othervm HttpInputStreamTest + * @run main/othervm -Dtest.debug=true HttpInputStreamTest * @author daniel fuchs */ public class HttpInputStreamTest { @@ -61,7 +64,7 @@ public class HttpInputStreamTest { public static class HttpInputStreamHandler implements HttpResponse.BodyHandler<InputStream> { - public static final int MAX_BUFFERS_IN_QUEUE = 1; + public static final int MAX_BUFFERS_IN_QUEUE = 1; // lock-step with the producer private final int maxBuffers; @@ -74,7 +77,7 @@ public class HttpInputStreamTest { } @Override - public synchronized HttpResponse.BodyProcessor<InputStream> + public HttpResponse.BodySubscriber<InputStream> apply(int i, HttpHeaders hh) { return new HttpResponseInputStream(maxBuffers); } @@ -83,17 +86,19 @@ public class HttpInputStreamTest { * An InputStream built on top of the Flow API. */ private static class HttpResponseInputStream extends InputStream - implements HttpResponse.BodyProcessor<InputStream> { + implements HttpResponse.BodySubscriber<InputStream> { // An immutable ByteBuffer sentinel to mark that the last byte was received. - private static final ByteBuffer LAST = ByteBuffer.wrap(new byte[0]); + private static final ByteBuffer LAST_BUFFER = ByteBuffer.wrap(new byte[0]); + private static final List<ByteBuffer> LAST_LIST = List.of(LAST_BUFFER); // A queue of yet unprocessed ByteBuffers received from the flow API. - private final BlockingQueue<ByteBuffer> buffers; + private final BlockingQueue<List<ByteBuffer>> buffers; private volatile Flow.Subscription subscription; private volatile boolean closed; private volatile Throwable failed; - private volatile ByteBuffer current; + private volatile Iterator<ByteBuffer> currentListItr; + private volatile ByteBuffer currentBuffer; HttpResponseInputStream() { this(MAX_BUFFERS_IN_QUEUE); @@ -101,7 +106,8 @@ public class HttpInputStreamTest { HttpResponseInputStream(int maxBuffers) { int capacity = maxBuffers <= 0 ? MAX_BUFFERS_IN_QUEUE : maxBuffers; - this.buffers = new ArrayBlockingQueue<>(capacity); + // 1 additional slot for LAST_LIST added by onComplete + this.buffers = new ArrayBlockingQueue<>(capacity + 1); } @Override @@ -119,40 +125,49 @@ public class HttpInputStreamTest { // a new buffer is made available through the Flow API, or the // end of the flow is reached. private ByteBuffer current() throws IOException { - while (current == null || !current.hasRemaining()) { - // Check whether the stream is claused or exhausted + while (currentBuffer == null || !currentBuffer.hasRemaining()) { + // Check whether the stream is closed or exhausted if (closed || failed != null) { throw new IOException("closed", failed); } - if (current == LAST) break; + if (currentBuffer == LAST_BUFFER) break; try { - // Take a new buffer from the queue, blocking - // if none is available yet... - if (DEBUG) System.err.println("Taking Buffer"); - current = buffers.take(); - if (DEBUG) System.err.println("Buffer Taken"); + if (currentListItr == null || !currentListItr.hasNext()) { + // Take a new list of buffers from the queue, blocking + // if none is available yet... - // Check whether some exception was encountered - // upstream - if (closed || failed != null) { - throw new IOException("closed", failed); + if (DEBUG) err.println("Taking list of Buffers"); + List<ByteBuffer> lb = buffers.take(); + currentListItr = lb.iterator(); + if (DEBUG) err.println("List of Buffers Taken"); + + // Check whether an exception was encountered upstream + if (closed || failed != null) + throw new IOException("closed", failed); + + // Check whether we're done. + if (lb == LAST_LIST) { + currentListItr = null; + currentBuffer = LAST_BUFFER; + break; + } + + // Request another upstream item ( list of buffers ) + Flow.Subscription s = subscription; + if (s != null) + s.request(1); } - - // Check whether we're done. - if (current == LAST) break; - - // Inform the producer that it can start sending - // us a new buffer - Flow.Subscription s = subscription; - if (s != null) s.request(1); - + assert currentListItr != null; + assert currentListItr.hasNext(); + if (DEBUG) err.println("Next Buffer"); + currentBuffer = currentListItr.next(); } catch (InterruptedException ex) { // continue } } - assert current == LAST || current.hasRemaining(); - return current; + assert currentBuffer == LAST_BUFFER || currentBuffer.hasRemaining(); + return currentBuffer; } @Override @@ -160,7 +175,7 @@ public class HttpInputStreamTest { // get the buffer to read from, possibly blocking if // none is available ByteBuffer buffer; - if ((buffer = current()) == LAST) return -1; + if ((buffer = current()) == LAST_BUFFER) return -1; // don't attempt to read more than what is available // in the current buffer. @@ -175,22 +190,31 @@ public class HttpInputStreamTest { @Override public int read() throws IOException { ByteBuffer buffer; - if ((buffer = current()) == LAST) return -1; + if ((buffer = current()) == LAST_BUFFER) return -1; return buffer.get() & 0xFF; } @Override public void onSubscribe(Flow.Subscription s) { + if (this.subscription != null) { + s.cancel(); + return; + } this.subscription = s; - s.request(Math.max(2, buffers.remainingCapacity() + 1)); + assert buffers.remainingCapacity() > 1; // should at least be 2 + if (DEBUG) err.println("onSubscribe: requesting " + + Math.max(1, buffers.remainingCapacity() - 1)); + s.request(Math.max(1, buffers.remainingCapacity() - 1)); } @Override - public synchronized void onNext(ByteBuffer t) { + public void onNext(List<ByteBuffer> t) { try { - if (DEBUG) System.err.println("next buffer received"); - buffers.put(t); - if (DEBUG) System.err.println("buffered offered"); + if (DEBUG) err.println("next item received"); + if (!buffers.offer(t)) { + throw new IllegalStateException("queue is full"); + } + if (DEBUG) err.println("item offered"); } catch (Exception ex) { failed = ex; try { @@ -203,24 +227,26 @@ public class HttpInputStreamTest { @Override public void onError(Throwable thrwbl) { + subscription = null; failed = thrwbl; } @Override - public synchronized void onComplete() { + public void onComplete() { subscription = null; - onNext(LAST); + onNext(LAST_LIST); } @Override public void close() throws IOException { synchronized (this) { + if (closed) return; closed = true; - Flow.Subscription s = subscription; - if (s != null) { - s.cancel(); - } - subscription = null; + } + Flow.Subscription s = subscription; + subscription = null; + if (s != null) { + s.cancel(); } super.close(); } @@ -274,8 +300,8 @@ public class HttpInputStreamTest { // client.sendAsync(request, HttpResponse.BodyHandler.asString()).get().body()); CompletableFuture<HttpResponse<InputStream>> handle = - client.sendAsync(request, new HttpInputStreamHandler()); - if (DEBUG) System.err.println("Request sent"); + client.sendAsync(request, new HttpInputStreamHandler(3)); + if (DEBUG) err.println("Request sent"); HttpResponse<InputStream> pending = handle.get(); @@ -301,8 +327,8 @@ public class HttpInputStreamTest { char[] buff = new char[32]; int off=0, n=0; - if (DEBUG) System.err.println("Start receiving response body"); - if (DEBUG) System.err.println("Charset: " + charset.get()); + if (DEBUG) err.println("Start receiving response body"); + if (DEBUG) err.println("Charset: " + charset.get()); // Start consuming the InputStream as the data arrives. // Will block until there is something to read... diff --git a/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java b/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java index 6df6e5a67b4..b2023783d63 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -21,21 +21,23 @@ * questions. */ -import jdk.incubator.http.HttpRequest; import java.net.URI; import jdk.incubator.http.HttpClient; import java.time.Duration; +import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.incubator.http.HttpRequest; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.noBody; /** * @test * @bug 8170064 - * @summary HttpRequest API documentation says:" Unless otherwise stated, - * {@code null} parameter values will cause methods - * of this class to throw {@code NullPointerException}". + * @summary HttpRequest[.Builder] API and behaviour checks */ public class HttpRequestBuilderTest { @@ -44,70 +46,166 @@ public class HttpRequestBuilderTest { public static void main(String[] args) throws Exception { + test0("newBuilder().build()", + () -> HttpRequest.newBuilder().build(), + IllegalStateException.class); + + test0("newBuilder(null)", + () -> HttpRequest.newBuilder(null), + NullPointerException.class); + + test0("newBuilder(URI.create(\"badScheme://www.foo.com/\")", + () -> HttpRequest.newBuilder(URI.create("badScheme://www.foo.com/")), + IllegalArgumentException.class); + + test0("newBuilder(URI.create(\"http://www.foo.com:-1/\")", + () -> HttpRequest.newBuilder(URI.create("http://www.foo.com:-1/")), + IllegalArgumentException.class); + + test0("newBuilder(URI.create(\"https://www.foo.com:-1/\")", + () -> HttpRequest.newBuilder(URI.create("https://www.foo.com:-1/")), + IllegalArgumentException.class); + + test0("newBuilder(" + TEST_URI + ").uri(null)", + () -> HttpRequest.newBuilder(TEST_URI).uri(null), + NullPointerException.class); + + test0("newBuilder(uri).build()", + () -> HttpRequest.newBuilder(TEST_URI).build() + /* no expected exceptions */ ); + HttpRequest.Builder builder = HttpRequest.newBuilder(); + builder = test1("uri", builder, builder::uri, (URI)null, NullPointerException.class); + + builder = test1("uri", builder, builder::uri, URI.create("http://www.foo.com:-1/"), + IllegalArgumentException.class); + + builder = test1("uri", builder, builder::uri, URI.create("https://www.foo.com:-1/"), + IllegalArgumentException.class); + builder = test2("header", builder, builder::header, (String) null, "bar", NullPointerException.class); + builder = test2("header", builder, builder::header, "foo", (String) null, NullPointerException.class); + builder = test2("header", builder, builder::header, (String)null, (String) null, NullPointerException.class); + + builder = test2("header", builder, builder::header, "", "bar", + IllegalArgumentException.class); + + builder = test2("header", builder, builder::header, "foo", "\r", + IllegalArgumentException.class); + builder = test1("headers", builder, builder::headers, (String[]) null, NullPointerException.class); + + builder = test1("headers", builder, builder::headers, new String[0], + IllegalArgumentException.class); + builder = test1("headers", builder, builder::headers, (String[]) new String[] {null, "bar"}, NullPointerException.class); + builder = test1("headers", builder, builder::headers, (String[]) new String[] {"foo", null}, NullPointerException.class); + builder = test1("headers", builder, builder::headers, (String[]) new String[] {null, null}, NullPointerException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo", "bar", null}, - NullPointerException.class, - IllegalArgumentException.class); + (String[]) new String[] {"foo", "bar", null}, + NullPointerException.class, + IllegalArgumentException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo", "bar", null, null}, - NullPointerException.class); + (String[]) new String[] {"foo", "bar", null, null}, + NullPointerException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo", "bar", "baz", null}, - NullPointerException.class); + (String[]) new String[] {"foo", "bar", "baz", null}, + NullPointerException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo", "bar", null, "baz"}, - NullPointerException.class); + (String[]) new String[] {"foo", "bar", "\r", "baz"}, + IllegalArgumentException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo", "bar", "baz"}, - IllegalArgumentException.class); + (String[]) new String[] {"foo", "bar", "baz", "\n"}, + IllegalArgumentException.class); + builder = test1("headers", builder, builder::headers, - (String[]) new String[] {"foo"}, - IllegalArgumentException.class); + (String[]) new String[] {"foo", "bar", "", "baz"}, + IllegalArgumentException.class); + + builder = test1("headers", builder, builder::headers, + (String[]) new String[] {"foo", "bar", null, "baz"}, + NullPointerException.class); + + builder = test1("headers", builder, builder::headers, + (String[]) new String[] {"foo", "bar", "baz"}, + IllegalArgumentException.class); + + builder = test1("headers", builder, builder::headers, + (String[]) new String[] {"foo"}, + IllegalArgumentException.class); + builder = test1("DELETE", builder, builder::DELETE, - (HttpRequest.BodyProcessor)null, null); + noBody(), null); + builder = test1("POST", builder, builder::POST, - (HttpRequest.BodyProcessor)null, null); + noBody(), null); + builder = test1("PUT", builder, builder::PUT, - (HttpRequest.BodyProcessor)null, null); + noBody(), null); + builder = test2("method", builder, builder::method, "GET", - (HttpRequest.BodyProcessor) null, null); + noBody(), null); + + builder = test1("DELETE", builder, builder::DELETE, + (HttpRequest.BodyPublisher)null, + NullPointerException.class); + + builder = test1("POST", builder, builder::POST, + (HttpRequest.BodyPublisher)null, + NullPointerException.class); + + builder = test1("PUT", builder, builder::PUT, + (HttpRequest.BodyPublisher)null, + NullPointerException.class); + + builder = test2("method", builder, builder::method, "GET", + (HttpRequest.BodyPublisher) null, + NullPointerException.class); + builder = test2("setHeader", builder, builder::setHeader, (String) null, "bar", NullPointerException.class); + builder = test2("setHeader", builder, builder::setHeader, "foo", (String) null, NullPointerException.class); + builder = test2("setHeader", builder, builder::setHeader, (String)null, (String) null, NullPointerException.class); + builder = test1("timeout", builder, builder::timeout, - (Duration)null, NullPointerException.class); + (Duration)null, + NullPointerException.class); + builder = test1("version", builder, builder::version, (HttpClient.Version)null, NullPointerException.class); + builder = test2("method", builder, builder::method, null, - HttpRequest.BodyProcessor.fromString("foo"), - NullPointerException.class); + fromString("foo"), + NullPointerException.class); // see JDK-8170093 // // builder = test2("method", builder, builder::method, "foo", @@ -116,6 +214,53 @@ public class HttpRequestBuilderTest { // // builder.build(); + + method("newBuilder(TEST_URI).build().method() == GET", + () -> HttpRequest.newBuilder(TEST_URI), + "GET"); + + method("newBuilder(TEST_URI).GET().build().method() == GET", + () -> HttpRequest.newBuilder(TEST_URI).GET(), + "GET"); + + method("newBuilder(TEST_URI).POST(fromString(\"\")).GET().build().method() == GET", + () -> HttpRequest.newBuilder(TEST_URI).POST(fromString("")).GET(), + "GET"); + + method("newBuilder(TEST_URI).PUT(fromString(\"\")).GET().build().method() == GET", + () -> HttpRequest.newBuilder(TEST_URI).PUT(fromString("")).GET(), + "GET"); + + method("newBuilder(TEST_URI).DELETE(fromString(\"\")).GET().build().method() == GET", + () -> HttpRequest.newBuilder(TEST_URI).DELETE(fromString("")).GET(), + "GET"); + + method("newBuilder(TEST_URI).POST(fromString(\"\")).build().method() == POST", + () -> HttpRequest.newBuilder(TEST_URI).POST(fromString("")), + "POST"); + + method("newBuilder(TEST_URI).PUT(fromString(\"\")).build().method() == PUT", + () -> HttpRequest.newBuilder(TEST_URI).PUT(fromString("")), + "PUT"); + + method("newBuilder(TEST_URI).DELETE(fromString(\"\")).build().method() == DELETE", + () -> HttpRequest.newBuilder(TEST_URI).DELETE(fromString("")), + "DELETE"); + + method("newBuilder(TEST_URI).GET().POST(fromString(\"\")).build().method() == POST", + () -> HttpRequest.newBuilder(TEST_URI).GET().POST(fromString("")), + "POST"); + + method("newBuilder(TEST_URI).GET().PUT(fromString(\"\")).build().method() == PUT", + () -> HttpRequest.newBuilder(TEST_URI).GET().PUT(fromString("")), + "PUT"); + + method("newBuilder(TEST_URI).GET().DELETE(fromString(\"\")).build().method() == DELETE", + () -> HttpRequest.newBuilder(TEST_URI).GET().DELETE(fromString("")), + "DELETE"); + + + } private static boolean shouldFail(Class<? extends Exception> ...exceptions) { @@ -133,22 +278,66 @@ public class HttpRequestBuilderTest { .findAny().isPresent(); } - public static <R,P> R test1(String name, R receiver, Function<P, R> m, P arg, - Class<? extends Exception> ...ex) { + static void method(String name, + Supplier<HttpRequest.Builder> supplier, + String expectedMethod) { + HttpRequest request = supplier.get().build(); + String method = request.method(); + if (request.method().equals("GET") && request.bodyPublisher().isPresent()) + throw new AssertionError("failed: " + name + + ". Unexpected body processor for GET: " + + request.bodyPublisher().get()); + + if (expectedMethod.equals(method)) { + System.out.println("success: " + name); + } else { + throw new AssertionError("failed: " + name + + ". Expected " + expectedMethod + ", got " + method); + } + } + + static void test0(String name, + Runnable r, + Class<? extends Exception> ...ex) { try { - R result = m.apply(arg); + r.run(); if (!shouldFail(ex)) { - System.out.println("success: " + name + "(" + arg + ")"); - return result; + System.out.println("success: " + name); + return; } else { throw new AssertionError("Expected " + expectedNames(ex) - + " not raised for " + name + "(" + arg + ")"); + + " not raised for " + name); } } catch (Exception x) { if (!isExpected(x, ex)) { throw x; } else { - System.out.println("success: " + name + "(" + arg + ")" + + System.out.println("success: " + name + + " - Got expected exception: " + x); + } + } + } + + public static <R,P> R test1(String name, R receiver, Function<P, R> m, P arg, + Class<? extends Exception> ...ex) { + String argMessage = arg == null ? "null" : arg.toString(); + if (arg instanceof String[]) { + argMessage = Arrays.asList((String[])arg).toString(); + } + try { + R result = m.apply(arg); + if (!shouldFail(ex)) { + System.out.println("success: " + name + "(" + argMessage + ")"); + return result; + } else { + throw new AssertionError("Expected " + expectedNames(ex) + + " not raised for " + name + "(" + argMessage + ")"); + } + } catch (Exception x) { + if (!isExpected(x, ex)) { + throw x; + } else { + System.out.println("success: " + name + "(" + argMessage + ")" + " - Got expected exception: " + x); return receiver; } diff --git a/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java new file mode 100644 index 00000000000..3480fabeffe --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2017, 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. + */ + +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpResponse.BodySubscriber; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import org.testng.annotations.Test; + +/* + * @test + * @summary Simple smoke test for BodySubscriber.asInputStream(); + * @run testng/othervm HttpResponseInputStreamTest + * @author daniel fuchs + */ +public class HttpResponseInputStreamTest { + + static class TestException extends IOException {} + + public static void main(String[] args) throws InterruptedException, ExecutionException { + testOnError(); + } + + /** + * Tests that a client will be unblocked and will throw an IOException + * if an error occurs while the client is waiting for more data. + * @throws InterruptedException + * @throws ExecutionException + */ + @Test + public static void testOnError() throws InterruptedException, ExecutionException { + CountDownLatch latch = new CountDownLatch(1); + BodySubscriber<InputStream> isb = BodySubscriber.asInputStream(); + ErrorTestSubscription s = new ErrorTestSubscription(isb); + CompletionStage<Throwable> cs = + isb.getBody().thenApplyAsync((is) -> s.accept(latch, is)); + latch.await(); + isb.onSubscribe(s); + s.t.join(); + Throwable result = cs.toCompletableFuture().get(); + Throwable t = result; + if (!(t instanceof IOException)) { + throw new RuntimeException("Failed to receive excpected IOException", result); + } else { + System.out.println("Got expected exception: " + t); + } + while (t != null) { + if (t instanceof TestException) break; + t = t.getCause(); + } + if (t instanceof TestException) { + System.out.println("Got expected cause: " + t); + } else { + throw new RuntimeException("Failed to receive excpected TestException", result); + } + } + + static class ErrorTestSubscription implements Flow.Subscription { + final BodySubscriber<InputStream> isb; + final Thread t = new Thread() { + @Override + public void run() { + try { + // Give time to + System.out.println("waiting..."); + Thread.sleep(1000); + } catch (InterruptedException e) { + + } + System.out.println("Calling onError..."); + isb.onError(new TestException()); + } + }; + + ErrorTestSubscription(BodySubscriber<InputStream> isb) { + this.isb = isb; + } + + int requested = 0; + + @Override + public void request(long n) { + System.out.println("Got request: " + n); + if (requested == 0 && n > 0) { + //isb.onNext(List.of(java.nio.ByteBuffer.wrap(new byte[] {0x01}))); + requested += n; + t.start(); + } + } + + @Override + public void cancel() { + } + + public Throwable accept(CountDownLatch latch, InputStream is) { + System.out.println("got " + is); + try { + latch.countDown(); + System.out.println("reading all bytes"); + is.readAllBytes(); + System.out.println("all bytes read"); + } catch (IOException e) { + return e; + } finally { + try { + is.close(); + } catch (IOException e) { + return e; + } + } + return is == null ? new NullPointerException() : null; + } + } + + static InputStream close(InputStream is) { + try { + is.close(); + } catch (IOException io) { + throw new CompletionException(io); + } + return is; + } + + @Test + public static void testCloseAndSubscribe() + throws InterruptedException, ExecutionException + { + BodySubscriber<InputStream> isb = BodySubscriber.asInputStream(); + TestCancelOnCloseSubscription s = new TestCancelOnCloseSubscription(); + InputStream is = isb.getBody() + .thenApply(HttpResponseInputStreamTest::close) + .toCompletableFuture() + .get(); + isb.onSubscribe(s); + System.out.println(s); + if (!s.cancelled.get()) { + throw new RuntimeException("subscription not cancelled"); + } + if (s.request.get() > 0) { + throw new RuntimeException("subscription has demand"); + } + } + + static byte[] readAllBytes(InputStream is) { + try { + return is.readAllBytes(); + } catch (IOException io) { + io.printStackTrace(); + throw new CompletionException(io); + } + } + + @Test + public static void testSubscribeAndClose() + throws InterruptedException, ExecutionException + { + BodySubscriber<InputStream> isb = BodySubscriber.asInputStream(); + TestCancelOnCloseSubscription s = new TestCancelOnCloseSubscription(); + InputStream is = isb.getBody().toCompletableFuture().get(); + isb.onSubscribe(s); + if (s.cancelled.get()) { + throw new RuntimeException("subscription cancelled"); + } + CompletableFuture<String> cf = CompletableFuture.supplyAsync( + () -> HttpResponseInputStreamTest.readAllBytes(is)) + .thenApply(String::new); + while (s.request.get() == 0) { + Thread.sleep(100); + } + isb.onNext(List.of(ByteBuffer.wrap("coucou".getBytes()))); + close(is); + System.out.println(s); + if (!s.cancelled.get()) { + throw new RuntimeException("subscription not cancelled"); + } + if (s.request.get() == 0) { + throw new RuntimeException("subscription has no demand"); + } + try { + System.out.println("read " + cf.get() + "!"); + throw new RuntimeException("expected IOException not raised"); + } catch (ExecutionException | CompletionException x) { + if (x.getCause() instanceof IOException) { + System.out.println("Got expected IOException: " + x.getCause()); + } else { + throw x; + } + } + } + + static class TestCancelOnCloseSubscription implements Flow.Subscription { + final AtomicLong request = new AtomicLong(); + final AtomicBoolean cancelled = new AtomicBoolean(); + + @Override + public void request(long n) { + request.addAndGet(n); + } + + @Override + public void cancel() { + cancelled.set(true); + } + + @Override + public String toString() { + return String.format("%s(request=%d, cancelled=%s)", + this.getClass().getSimpleName(), + request.get(), + cancelled.get()); + } + } +} diff --git a/test/jdk/java/net/httpclient/ImmutableHeaders.java b/test/jdk/java/net/httpclient/ImmutableHeaders.java index bd6a2f7e733..48304ad1afc 100644 --- a/test/jdk/java/net/httpclient/ImmutableHeaders.java +++ b/test/jdk/java/net/httpclient/ImmutableHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/InterruptedBlockingSend.java b/test/jdk/java/net/httpclient/InterruptedBlockingSend.java new file mode 100644 index 00000000000..b3514eac0b5 --- /dev/null +++ b/test/jdk/java/net/httpclient/InterruptedBlockingSend.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.net.ServerSocket; +import java.net.URI; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import static java.lang.System.out; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; + +/** + * @test + * @summary Basic test for interrupted blocking send + */ + +public class InterruptedBlockingSend { + + static volatile Throwable throwable; + + public static void main(String[] args) throws Exception { + HttpClient client = HttpClient.newHttpClient(); + try (ServerSocket ss = new ServerSocket(0, 20)) { + int port = ss.getLocalPort(); + URI uri = new URI("http://127.0.0.1:" + port + "/"); + + HttpRequest request = HttpRequest.newBuilder(uri).build(); + + Thread t = new Thread(() -> { + try { + client.send(request, discard(null)); + } catch (InterruptedException e) { + throwable = e; + } catch (Throwable th) { + throwable = th; + } + }); + t.start(); + Thread.sleep(5000); + t.interrupt(); + t.join(); + + if (!(throwable instanceof InterruptedException)) { + throw new RuntimeException("Expected InterruptedException, got " + throwable); + } else { + out.println("Caught expected InterruptedException: " + throwable); + } + } + } +} diff --git a/test/jdk/java/net/httpclient/LightWeightHttpServer.java b/test/jdk/java/net/httpclient/LightWeightHttpServer.java index d80a382d669..38242dd5221 100644 --- a/test/jdk/java/net/httpclient/LightWeightHttpServer.java +++ b/test/jdk/java/net/httpclient/LightWeightHttpServer.java @@ -80,7 +80,7 @@ public class LightWeightHttpServer { ch.setLevel(Level.ALL); logger.addHandler(ch); - String root = System.getProperty("test.src") + "/docs"; + String root = System.getProperty("test.src", ".") + "/docs"; InetSocketAddress addr = new InetSocketAddress(0); httpServer = HttpServer.create(addr, 0); if (httpServer instanceof HttpsServer) { @@ -301,11 +301,12 @@ public class LightWeightHttpServer { @Override public synchronized void handle(HttpExchange he) throws IOException { - byte[] buf = Util.readAll(he.getRequestBody()); - try { + try(InputStream is = he.getRequestBody()) { + is.readAllBytes(); bar1.await(); bar2.await(); } catch (InterruptedException | BrokenBarrierException e) { + throw new IOException(e); } he.sendResponseHeaders(200, -1); // will probably fail he.close(); diff --git a/test/jdk/java/net/httpclient/ManyRequests.java b/test/jdk/java/net/httpclient/ManyRequests.java index 8803b397528..dfcd2acd0bc 100644 --- a/test/jdk/java/net/httpclient/ManyRequests.java +++ b/test/jdk/java/net/httpclient/ManyRequests.java @@ -56,13 +56,12 @@ import java.util.Formatter; import java.util.HashMap; import java.util.LinkedList; import java.util.Random; -import java.util.concurrent.ExecutorService; import java.util.logging.Logger; import java.util.logging.Level; import java.util.concurrent.CompletableFuture; import javax.net.ssl.SSLContext; import jdk.testlibrary.SimpleSSLContext; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromByteArray; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray; import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray; public class ManyRequests { @@ -91,7 +90,6 @@ public class ManyRequests { System.out.println("OK"); } finally { server.stop(0); - ((ExecutorService)client.executor()).shutdownNow(); } } @@ -109,17 +107,23 @@ public class ManyRequests { System.out.println("Server: received " + e.getRequestURI()); super.handle(e); } - protected void close(OutputStream os) throws IOException { + @Override + protected void close(HttpExchange t, OutputStream os) throws IOException { if (INSERT_DELAY) { - try { Thread.sleep(rand.nextInt(200)); } catch (InterruptedException e) {} + try { Thread.sleep(rand.nextInt(200)); } + catch (InterruptedException e) {} } - super.close(os); + System.out.println("Server: close outbound: " + t.getRequestURI()); + super.close(t, os); } - protected void close(InputStream is) throws IOException { + @Override + protected void close(HttpExchange t, InputStream is) throws IOException { if (INSERT_DELAY) { - try { Thread.sleep(rand.nextInt(200)); } catch (InterruptedException e) {} + try { Thread.sleep(rand.nextInt(200)); } + catch (InterruptedException e) {} } - super.close(is); + System.out.println("Server: close inbound: " + t.getRequestURI()); + super.close(t, is); } } diff --git a/test/jdk/java/net/httpclient/ManyRequests2.java b/test/jdk/java/net/httpclient/ManyRequests2.java index 26c281fca61..af0189147b3 100644 --- a/test/jdk/java/net/httpclient/ManyRequests2.java +++ b/test/jdk/java/net/httpclient/ManyRequests2.java @@ -36,7 +36,7 @@ * @run main/othervm/timeout=40 -Dtest.XFixed=true ManyRequests2 * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.insertDelay=true ManyRequests2 * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.chunkSize=64 ManyRequests2 - * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequests2 + * @run main/othervm/timeout=40 -Djdk.internal.httpclient.debug=true -Dtest.XFixed=true -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequests2 * @summary Send a large number of requests asynchronously. The server echoes back using known content length. */ // * @run main/othervm/timeout=40 -Djdk.httpclient.HttpClient.log=ssl ManyRequests diff --git a/test/jdk/java/net/httpclient/ManyRequestsLegacy.java b/test/jdk/java/net/httpclient/ManyRequestsLegacy.java new file mode 100644 index 00000000000..16707741095 --- /dev/null +++ b/test/jdk/java/net/httpclient/ManyRequestsLegacy.java @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2015, 2017, 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 + * @modules jdk.incubator.httpclient + * java.logging + * jdk.httpserver + * @library /lib/testlibrary/ / + * @build jdk.testlibrary.SimpleSSLContext + * @compile ../../../com/sun/net/httpserver/LogFilter.java + * @compile ../../../com/sun/net/httpserver/EchoHandler.java + * @compile ../../../com/sun/net/httpserver/FileServerHandler.java + * @run main/othervm/timeout=40 ManyRequestsLegacy + * @run main/othervm/timeout=40 -Dtest.insertDelay=true ManyRequestsLegacy + * @run main/othervm/timeout=40 -Dtest.chunkSize=64 ManyRequestsLegacy + * @run main/othervm/timeout=40 -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequestsLegacy + * @summary Send a large number of requests asynchronously using the legacy URL.openConnection(), to help sanitize results of the test ManyRequest.java. + */ + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.HostnameVerifier; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsParameters; +import com.sun.net.httpserver.HttpsServer; +import com.sun.net.httpserver.HttpExchange; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URLConnection; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSession; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpClient.Version; +import jdk.incubator.http.HttpHeaders; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Formatter; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.logging.Logger; +import java.util.logging.Level; +import jdk.testlibrary.SimpleSSLContext; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray; +import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray; + +public class ManyRequestsLegacy { + + volatile static int counter = 0; + + public static void main(String[] args) throws Exception { + Logger logger = Logger.getLogger("com.sun.net.httpserver"); + logger.setLevel(Level.ALL); + logger.info("TEST"); + System.out.println("Sending " + REQUESTS + + " requests; delay=" + INSERT_DELAY + + ", chunks=" + CHUNK_SIZE + + ", XFixed=" + XFIXED); + SSLContext ctx = new SimpleSSLContext().get(); + SSLContext.setDefault(ctx); + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + InetSocketAddress addr = new InetSocketAddress(0); + HttpsServer server = HttpsServer.create(addr, 0); + server.setHttpsConfigurator(new Configurator(ctx)); + + LegacyHttpClient client = new LegacyHttpClient(); + + try { + test(server, client); + System.out.println("OK"); + } finally { + server.stop(0); + } + } + + //static final int REQUESTS = 1000; + static final int REQUESTS = 20; + static final boolean INSERT_DELAY = Boolean.getBoolean("test.insertDelay"); + static final int CHUNK_SIZE = Math.max(0, + Integer.parseInt(System.getProperty("test.chunkSize", "0"))); + static final boolean XFIXED = Boolean.getBoolean("test.XFixed"); + + static class LegacyHttpClient { + static final class LegacyHttpResponse extends HttpResponse<byte[]> { + final HttpRequest request; + final byte[] response; + final int statusCode; + public LegacyHttpResponse(HttpRequest request, int statusCode, byte[] response) { + this.request = request; + this.statusCode = statusCode; + this.response = response; + } + private <T> T error() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override + public int statusCode() { return statusCode;} + @Override + public HttpRequest request() {return request;} + @Override + public Optional<HttpResponse<byte[]>> previousResponse() {return Optional.empty();} + @Override + public HttpHeaders headers() { return error(); } + @Override + public byte[] body() {return response;} + @Override + public SSLParameters sslParameters() { + try { + return SSLContext.getDefault().getDefaultSSLParameters(); + } catch (NoSuchAlgorithmException ex) { + throw new UnsupportedOperationException(ex); + } + } + @Override + public URI uri() { return request.uri();} + @Override + public HttpClient.Version version() { return Version.HTTP_1_1;} + } + + private void debugCompleted(String tag, long startNanos, HttpRequest req) { + System.err.println(tag + " elapsed " + + (System.nanoTime() - startNanos)/1000_000L + + " millis for " + req.method() + + " to " + req.uri()); + } + + CompletableFuture<? extends HttpResponse<byte[]>> sendAsync(HttpRequest r, byte[] buf) { + long start = System.nanoTime(); + try { + CompletableFuture<LegacyHttpResponse> cf = new CompletableFuture<>(); + URLConnection urlc = r.uri().toURL().openConnection(); + HttpURLConnection httpc = (HttpURLConnection)urlc; + httpc.setRequestMethod(r.method()); + for (String s : r.headers().map().keySet()) { + httpc.setRequestProperty(s, r.headers().allValues(s) + .stream().collect(Collectors.joining(","))); + } + httpc.setDoInput(true); + if (buf != null) httpc.setDoOutput(true); + Thread t = new Thread(() -> { + try { + if (buf != null) { + try (OutputStream os = httpc.getOutputStream()) { + os.write(buf); + os.flush(); + } + } + LegacyHttpResponse response = new LegacyHttpResponse(r, + httpc.getResponseCode(),httpc.getInputStream().readAllBytes()); + cf.complete(response); + } catch(Throwable x) { + cf.completeExceptionally(x); + } + }); + t.start(); + return cf.whenComplete((b,x) -> debugCompleted("ClientImpl (async)", start, r)); + } catch(Throwable t) { + debugCompleted("ClientImpl (async)", start, r); + return CompletableFuture.failedFuture(t); + } + } + } + + static class TestEchoHandler extends EchoHandler { + final Random rand = new Random(); + @Override + public void handle(HttpExchange e) throws IOException { + System.out.println("Server: received " + e.getRequestURI()); + super.handle(e); + } + @Override + protected void close(HttpExchange t, OutputStream os) throws IOException { + if (INSERT_DELAY) { + try { Thread.sleep(rand.nextInt(200)); } + catch (InterruptedException e) {} + } + System.out.println("Server: close outbound: " + t.getRequestURI()); + os.close(); + } + @Override + protected void close(HttpExchange t, InputStream is) throws IOException { + if (INSERT_DELAY) { + try { Thread.sleep(rand.nextInt(200)); } + catch (InterruptedException e) {} + } + System.out.println("Server: close inbound: " + t.getRequestURI()); + is.close(); + } + } + + static void test(HttpsServer server, LegacyHttpClient client) throws Exception { + int port = server.getAddress().getPort(); + URI baseURI = new URI("https://127.0.0.1:" + port + "/foo/x"); + server.createContext("/foo", new TestEchoHandler()); + server.start(); + + RequestLimiter limiter = new RequestLimiter(40); + Random rand = new Random(); + CompletableFuture<?>[] results = new CompletableFuture<?>[REQUESTS]; + HashMap<HttpRequest,byte[]> bodies = new HashMap<>(); + + for (int i=0; i<REQUESTS; i++) { + byte[] buf = new byte[(i+1)*CHUNK_SIZE+i+1]; // different size bodies + rand.nextBytes(buf); + URI uri = new URI(baseURI.toString() + String.valueOf(i+1)); + HttpRequest r = HttpRequest.newBuilder(uri) + .header("XFixed", "true") + .POST(fromByteArray(buf)) + .build(); + bodies.put(r, buf); + + results[i] = + limiter.whenOkToSend() + .thenCompose((v) -> { + System.out.println("Client: sendAsync: " + r.uri()); + return client.sendAsync(r, buf); + }) + .thenCompose((resp) -> { + limiter.requestComplete(); + if (resp.statusCode() != 200) { + String s = "Expected 200, got: " + resp.statusCode(); + System.out.println(s + " from " + + resp.request().uri().getPath()); + return completedWithIOException(s); + } else { + counter++; + System.out.println("Result (" + counter + ") from " + + resp.request().uri().getPath()); + } + return CompletableFuture.completedStage(resp.body()) + .thenApply((b) -> new Pair<>(resp, b)); + }) + .thenAccept((pair) -> { + HttpRequest request = pair.t.request(); + byte[] requestBody = bodies.get(request); + check(Arrays.equals(requestBody, pair.u), + "bodies not equal:[" + bytesToHexString(requestBody) + + "] [" + bytesToHexString(pair.u) + "]"); + + }); + } + + // wait for them all to complete and throw exception in case of error + CompletableFuture.allOf(results).join(); + } + + static <T> CompletableFuture<T> completedWithIOException(String message) { + return CompletableFuture.failedFuture(new IOException(message)); + } + + static String bytesToHexString(byte[] bytes) { + if (bytes == null) + return "null"; + + StringBuilder sb = new StringBuilder(bytes.length * 2); + + Formatter formatter = new Formatter(sb); + for (byte b : bytes) { + formatter.format("%02x", b); + } + + return sb.toString(); + } + + static final class Pair<T,U> { + Pair(T t, U u) { + this.t = t; this.u = u; + } + T t; + U u; + } + + /** + * A simple limiter for controlling the number of requests to be run in + * parallel whenOkToSend() is called which returns a CF<Void> that allows + * each individual request to proceed, or block temporarily (blocking occurs + * on the waiters list here. As each request actually completes + * requestComplete() is called to notify this object, and allow some + * requests to continue. + */ + static class RequestLimiter { + + static final CompletableFuture<Void> COMPLETED_FUTURE = + CompletableFuture.completedFuture(null); + + final int maxnumber; + final LinkedList<CompletableFuture<Void>> waiters; + int number; + boolean blocked; + + RequestLimiter(int maximum) { + waiters = new LinkedList<>(); + maxnumber = maximum; + } + + synchronized void requestComplete() { + number--; + // don't unblock until number of requests has halved. + if ((blocked && number <= maxnumber / 2) || + (!blocked && waiters.size() > 0)) { + int toRelease = Math.min(maxnumber - number, waiters.size()); + for (int i=0; i<toRelease; i++) { + CompletableFuture<Void> f = waiters.remove(); + number ++; + f.complete(null); + } + blocked = number >= maxnumber; + } + } + + synchronized CompletableFuture<Void> whenOkToSend() { + if (blocked || number + 1 >= maxnumber) { + blocked = true; + CompletableFuture<Void> r = new CompletableFuture<>(); + waiters.add(r); + return r; + } else { + number++; + return COMPLETED_FUTURE; + } + } + } + + static void check(boolean cond, Object... msg) { + if (cond) + return; + StringBuilder sb = new StringBuilder(); + for (Object o : msg) + sb.append(o); + throw new RuntimeException(sb.toString()); + } + + static class Configurator extends HttpsConfigurator { + public Configurator(SSLContext ctx) { + super(ctx); + } + + public void configure(HttpsParameters params) { + params.setSSLParameters(getSSLContext().getSupportedSSLParameters()); + } + } +} diff --git a/test/jdk/java/net/httpclient/MessageHeadersTest.java b/test/jdk/java/net/httpclient/MessageHeadersTest.java index eedff118b4a..a11511cb817 100644 --- a/test/jdk/java/net/httpclient/MessageHeadersTest.java +++ b/test/jdk/java/net/httpclient/MessageHeadersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/Server.java b/test/jdk/java/net/httpclient/MockServer.java similarity index 64% rename from test/jdk/java/net/httpclient/Server.java rename to test/jdk/java/net/httpclient/MockServer.java index c8f9864cb59..f7c578f36c2 100644 --- a/test/jdk/java/net/httpclient/Server.java +++ b/test/jdk/java/net/httpclient/MockServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLServerSocket; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; @@ -41,13 +43,22 @@ import java.util.concurrent.atomic.AtomicInteger; * * use interrupt() to halt */ -public class Server extends Thread implements Closeable { +public class MockServer extends Thread implements Closeable { - ServerSocket ss; + final ServerSocket ss; private final List<Connection> sockets; private final List<Connection> removals; private final List<Connection> additions; AtomicInteger counter = new AtomicInteger(0); + // if specified (not null), only requests which + // contain this value in their status line + // will be taken into account and returned by activity(). + // Other requests will get summarily closed. + // When specified, this can prevent answering to rogue + // (external) clients that might be lurking + // on the test machine instead of answering + // to the test client. + final String root; // waits up to 20 seconds for something to happen // dont use this unless certain activity coming. @@ -56,6 +67,19 @@ public class Server extends Thread implements Closeable { doRemovalsAndAdditions(); for (Connection c : sockets) { if (c.poll()) { + if (root != null) { + // if a root was specified in MockServer + // constructor, rejects (by closing) all + // requests whose statusLine does not contain + // root. + if (!c.statusLine.contains(root)) { + System.out.println("Bad statusLine: " + + c.statusLine + + " closing connection"); + c.close(); + continue; + } + } return c; } } @@ -69,17 +93,25 @@ public class Server extends Thread implements Closeable { } private void doRemovalsAndAdditions() { - if (removals.isEmpty() && additions.isEmpty()) - return; - Iterator<Connection> i = removals.iterator(); - while (i.hasNext()) - sockets.remove(i.next()); - removals.clear(); + synchronized (removals) { + Iterator<Connection> i = removals.iterator(); + while (i.hasNext()) { + Connection c = i.next(); + System.out.println("socket removed: " + c); + sockets.remove(c); + } + removals.clear(); + } - i = additions.iterator(); - while (i.hasNext()) - sockets.add(i.next()); - additions.clear(); + synchronized (additions) { + Iterator<Connection> i = additions.iterator(); + while (i.hasNext()) { + Connection c = i.next(); + System.out.println("socket added: " + c); + sockets.add(c); + } + additions.clear(); + } } // clears all current connections on Server. @@ -108,12 +140,14 @@ public class Server extends Thread implements Closeable { final InputStream is; final OutputStream os; final ArrayBlockingQueue<String> incoming; + volatile String statusLine; final static String CRLF = "\r\n"; // sentinel indicating connection closed final static String CLOSED = "C.L.O.S.E.D"; volatile boolean closed = false; + volatile boolean released = false; @Override public void run() { @@ -131,6 +165,8 @@ public class Server extends Thread implements Closeable { int i; while ((i=s.indexOf(CRLF)) != -1) { String s1 = s.substring(0, i+2); + System.out.println("Server got: " + s1.substring(0,i)); + if (statusLine == null) statusLine = s1.substring(0,i); incoming.put(s1); if (i+2 == s.length()) { s = ""; @@ -224,23 +260,40 @@ public class Server extends Thread implements Closeable { } private void cleanup() { + if (released) return; + synchronized(this) { + if (released) return; + released = true; + } try { socket.close(); } catch (IOException e) {} - removals.add(this); + synchronized (removals) { + removals.add(this); + } } } - Server(int port) throws IOException { - ss = new ServerSocket(port); + MockServer(int port, ServerSocketFactory factory, String root) throws IOException { + ss = factory.createServerSocket(port); + this.root = root; // if specified, any request which don't have this value + // in their statusLine will be rejected. sockets = Collections.synchronizedList(new LinkedList<>()); - removals = Collections.synchronizedList(new LinkedList<>()); - additions = Collections.synchronizedList(new LinkedList<>()); + removals = new LinkedList<>(); + additions = new LinkedList<>(); setName("Test-Server"); setDaemon(true); } - Server() throws IOException { + MockServer(int port, ServerSocketFactory factory) throws IOException { + this(port, factory, "/foo/"); + } + + MockServer(int port) throws IOException { + this(port, ServerSocketFactory.getDefault()); + } + + MockServer() throws IOException { this(0); } @@ -249,7 +302,11 @@ public class Server extends Thread implements Closeable { } public String getURL() { - return "http://127.0.0.1:" + port() + "/foo/"; + if (ss instanceof SSLServerSocket) { + return "https://127.0.0.1:" + port() + "/foo/"; + } else { + return "http://127.0.0.1:" + port() + "/foo/"; + } } private volatile boolean closed; @@ -269,16 +326,32 @@ public class Server extends Thread implements Closeable { @Override public void run() { - while (!closed) { - try { - Socket s = ss.accept(); - Connection c = new Connection(s); - c.start(); - additions.add(c); - } catch (IOException e) { - if (closed) - return; - e.printStackTrace(); + try { + while (!closed) { + try { + System.out.println("Server waiting for connection"); + Socket s = ss.accept(); + Connection c = new Connection(s); + c.start(); + System.out.println("Server got new connection: " + c); + synchronized (additions) { + additions.add(c); + } + } catch (IOException e) { + if (closed) + return; + e.printStackTrace(System.out); + } + } + } catch (Throwable t) { + System.out.println("Unexpected exception in accept loop: " + t); + t.printStackTrace(System.out); + } finally { + if (closed) { + System.out.println("Server closed: exiting accept loop"); + } else { + System.out.println("Server not closed: exiting accept loop and closing"); + close(); } } } diff --git a/test/jdk/java/net/httpclient/MultiAuthTest.java b/test/jdk/java/net/httpclient/MultiAuthTest.java index 1ef86e668a2..6d049041981 100644 --- a/test/jdk/java/net/httpclient/MultiAuthTest.java +++ b/test/jdk/java/net/httpclient/MultiAuthTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -46,7 +46,7 @@ import jdk.incubator.http.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.nio.charset.StandardCharsets.US_ASCII; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; diff --git a/test/jdk/java/net/httpclient/NoBodyPartOne.java b/test/jdk/java/net/httpclient/NoBodyPartOne.java new file mode 100644 index 00000000000..d2a979394db --- /dev/null +++ b/test/jdk/java/net/httpclient/NoBodyPartOne.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, 2017, 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 8161157 + * @summary Test response body handlers/subscribers when there is no body + * @library /lib/testlibrary http2/server + * @build jdk.testlibrary.SimpleSSLContext + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @run testng/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all NoBodyPartOne + */ + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpResponse.BodyHandler; +import org.testng.annotations.Test; +import static java.nio.charset.StandardCharsets.UTF_8; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray; +import static jdk.incubator.http.HttpResponse.BodyHandler.asFile; +import static jdk.incubator.http.HttpResponse.BodyHandler.asString; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class NoBodyPartOne extends AbstractNoBody { + + @Test(dataProvider = "variants") + public void testAsString(String uri, boolean sameClient) throws Exception { + printStamp(START, "testAsString(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + BodyHandler<String> handler = i % 2 == 0 ? asString() : asString(UTF_8); + HttpResponse<String> response = client.send(req, handler); + String body = response.body(); + assertEquals(body, ""); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } + + @Test(dataProvider = "variants") + public void testAsFile(String uri, boolean sameClient) throws Exception { + printStamp(START, "testAsFile(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + Path p = Paths.get("NoBody_testAsFile.txt"); + HttpResponse<Path> response = client.send(req, asFile(p)); + Path bodyPath = response.body(); + assertTrue(Files.exists(bodyPath)); + assertEquals(Files.size(bodyPath), 0); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } + + @Test(dataProvider = "variants") + public void testAsByteArray(String uri, boolean sameClient) throws Exception { + printStamp(START, "testAsByteArray(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + HttpResponse<byte[]> response = client.send(req, asByteArray()); + byte[] body = response.body(); + assertEquals(body.length, 0); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } +} diff --git a/test/jdk/java/net/httpclient/NoBodyPartTwo.java b/test/jdk/java/net/httpclient/NoBodyPartTwo.java new file mode 100644 index 00000000000..a9fbb95b937 --- /dev/null +++ b/test/jdk/java/net/httpclient/NoBodyPartTwo.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, 2017, 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 8161157 + * @summary Test response body handlers/subscribers when there is no body + * @library /lib/testlibrary http2/server + * @build jdk.testlibrary.SimpleSSLContext + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @run testng/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all NoBodyPartTwo + */ + +import java.io.InputStream; +import java.net.URI; +import java.util.Optional; +import java.util.function.Consumer; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.testng.annotations.Test; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray; +import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArrayConsumer; +import static jdk.incubator.http.HttpResponse.BodyHandler.asInputStream; +import static jdk.incubator.http.HttpResponse.BodyHandler.buffering; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +public class NoBodyPartTwo extends AbstractNoBody { + + volatile boolean consumerHasBeenCalled; + @Test(dataProvider = "variants") + public void testAsByteArrayConsumer(String uri, boolean sameClient) throws Exception { + printStamp(START, "testAsByteArrayConsumer(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + Consumer<Optional<byte[]>> consumer = oba -> { + consumerHasBeenCalled = true; + oba.ifPresent(ba -> fail("Unexpected non-empty optional:" + ba)); + }; + consumerHasBeenCalled = false; + client.send(req, asByteArrayConsumer(consumer)); + assertTrue(consumerHasBeenCalled); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } + + @Test(dataProvider = "variants") + public void testAsInputStream(String uri, boolean sameClient) throws Exception { + printStamp(START, "testAsInputStream(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + HttpResponse<InputStream> response = client.send(req, asInputStream()); + byte[] body = response.body().readAllBytes(); + assertEquals(body.length, 0); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } + + @Test(dataProvider = "variants") + public void testBuffering(String uri, boolean sameClient) throws Exception { + printStamp(START, "testBuffering(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + HttpResponse<byte[]> response = client.send(req, buffering(asByteArray(), 1024)); + byte[] body = response.body(); + assertEquals(body.length, 0); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } + + @Test(dataProvider = "variants") + public void testDiscard(String uri, boolean sameClient) throws Exception { + printStamp(START, "testDiscard(\"%s\", %s)", uri, sameClient); + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .PUT(fromString(SIMPLE_STRING)) + .build(); + Object obj = new Object(); + HttpResponse<Object> response = client.send(req, discard(obj)); + assertEquals(response.body(), obj); + } + // We have created many clients here. Try to speed up their release. + if (!sameClient) System.gc(); + } +} diff --git a/test/jdk/java/net/httpclient/ProxyAuthTest.java b/test/jdk/java/net/httpclient/ProxyAuthTest.java index 43ba69032e0..d6e310ddd22 100644 --- a/test/jdk/java/net/httpclient/ProxyAuthTest.java +++ b/test/jdk/java/net/httpclient/ProxyAuthTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -29,7 +29,6 @@ * @modules java.base/sun.net.www * jdk.incubator.httpclient * @summary Verify that Proxy-Authenticate header is correctly handled - * * @run main/othervm ProxyAuthTest */ @@ -42,14 +41,17 @@ import java.io.PrintWriter; import java.net.Authenticator; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; +import java.net.Proxy; import java.net.ProxySelector; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketAddress; import java.net.URI; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import java.util.Base64; +import java.util.List; import sun.net.www.MessageHeader; import static jdk.incubator.http.HttpResponse.BodyHandler.discard; @@ -68,8 +70,9 @@ public class ProxyAuthTest { InetSocketAddress paddr = new InetSocketAddress("localhost", port); URI uri = new URI("http://www.google.ie/"); + CountingProxySelector ps = CountingProxySelector.of(paddr); HttpClient client = HttpClient.newBuilder() - .proxy(ProxySelector.of(paddr)) + .proxy(ps) .authenticator(auth) .build(); HttpRequest req = HttpRequest.newBuilder(uri).GET().build(); @@ -87,6 +90,9 @@ public class ProxyAuthTest { if (!proxy.matched) { throw new RuntimeException("Proxy authentication failed"); } + if (ps.count() > 1) { + throw new RuntimeException("CountingProxySelector. Expected 1, got " + ps.count()); + } } } @@ -102,6 +108,37 @@ public class ProxyAuthTest { } } + /** + * A Proxy Selector that wraps a ProxySelector.of(), and counts the number + * of times its select method has been invoked. This can be used to ensure + * that the Proxy Selector is invoked only once per HttpClient.sendXXX + * invocation. + */ + static class CountingProxySelector extends ProxySelector { + private final ProxySelector proxySelector; + private volatile int count; // 0 + private CountingProxySelector(InetSocketAddress proxyAddress) { + proxySelector = ProxySelector.of(proxyAddress); + } + + public static CountingProxySelector of(InetSocketAddress proxyAddress) { + return new CountingProxySelector(proxyAddress); + } + + int count() { return count; } + + @Override + public List<Proxy> select(URI uri) { + count++; + return proxySelector.select(uri); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + proxySelector.connectFailed(uri, sa, ioe); + } + } + static class MyProxy implements Runnable { final ServerSocket ss; private volatile boolean matched; diff --git a/test/jdk/java/net/httpclient/ProxyServer.java b/test/jdk/java/net/httpclient/ProxyServer.java index 4da9b5e1c55..c8d3dd64ca3 100644 --- a/test/jdk/java/net/httpclient/ProxyServer.java +++ b/test/jdk/java/net/httpclient/ProxyServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -207,7 +207,7 @@ public class ProxyServer extends Thread implements Closeable { } else { doProxy(params[1], buf, p, cmd); } - } catch (IOException e) { + } catch (Throwable e) { if (debug) { System.out.println (e); } diff --git a/test/jdk/java/net/httpclient/ProxyTest.java b/test/jdk/java/net/httpclient/ProxyTest.java index f6590d89f17..0a992b98a7d 100644 --- a/test/jdk/java/net/httpclient/ProxyTest.java +++ b/test/jdk/java/net/httpclient/ProxyTest.java @@ -41,10 +41,12 @@ import java.net.Proxy; import java.net.ProxySelector; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; +import java.util.List; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; @@ -121,6 +123,37 @@ public class ProxyTest { } } + /** + * A Proxy Selector that wraps a ProxySelector.of(), and counts the number + * of times its select method has been invoked. This can be used to ensure + * that the Proxy Selector is invoked only once per HttpClient.sendXXX + * invocation. + */ + static class CountingProxySelector extends ProxySelector { + private final ProxySelector proxySelector; + private volatile int count; // 0 + private CountingProxySelector(InetSocketAddress proxyAddress) { + proxySelector = ProxySelector.of(proxyAddress); + } + + public static CountingProxySelector of(InetSocketAddress proxyAddress) { + return new CountingProxySelector(proxyAddress); + } + + int count() { return count; } + + @Override + public List<Proxy> select(URI uri) { + count++; + return proxySelector.select(uri); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + proxySelector.connectFailed(uri, sa, ioe); + } + } + public static void test(HttpServer server, HttpClient.Version version) throws IOException, URISyntaxException, @@ -158,7 +191,7 @@ public class ProxyTest { System.out.println("\nReal test begins here."); System.out.println("Setting up request with HttpClient for version: " + version.name()); - ProxySelector ps = ProxySelector.of( + CountingProxySelector ps = CountingProxySelector.of( InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort())); HttpClient client = HttpClient.newBuilder() .version(version) @@ -178,6 +211,9 @@ public class ProxyTest { if (!RESPONSE.equals(resp)) { throw new AssertionError("Unexpected response"); } + if (ps.count() > 1) { + throw new AssertionError("CountingProxySelector. Expected 1, got " + ps.count()); + } } finally { System.out.println("Stopping proxy"); proxy.stop(); diff --git a/test/jdk/java/net/httpclient/RequestBodyTest.java b/test/jdk/java/net/httpclient/RequestBodyTest.java index 637cbc31aff..b3bf1495248 100644 --- a/test/jdk/java/net/httpclient/RequestBodyTest.java +++ b/test/jdk/java/net/httpclient/RequestBodyTest.java @@ -22,7 +22,8 @@ */ /* - * @test @bug 8087112 + * @test + * @bug 8087112 * @modules jdk.incubator.httpclient * java.logging * jdk.httpserver @@ -42,6 +43,7 @@ import java.net.URI; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpResponse.BodyHandler; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -51,14 +53,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; import java.util.function.Supplier; import javax.net.ssl.SSLContext; import jdk.test.lib.util.FileUtils; +import static java.lang.System.out; import static java.nio.charset.StandardCharsets.*; import static java.nio.file.StandardOpenOption.*; -import static jdk.incubator.http.HttpRequest.BodyProcessor.*; +import static jdk.incubator.http.HttpRequest.BodyPublisher.*; import static jdk.incubator.http.HttpResponse.BodyHandler.*; import org.testng.annotations.AfterTest; @@ -69,12 +72,12 @@ import static org.testng.Assert.*; public class RequestBodyTest { - static final String fileroot = System.getProperty("test.src") + "/docs"; + static final String fileroot = System.getProperty("test.src", ".") + "/docs"; static final String midSizedFilename = "/files/notsobigfile.txt"; static final String smallFilename = "/files/smallfile.txt"; + final ConcurrentHashMap<String,Throwable> failures = new ConcurrentHashMap<>(); HttpClient client; - ExecutorService exec = Executors.newCachedThreadPool(); String httpURI; String httpsURI; @@ -109,14 +112,25 @@ public class RequestBodyTest { .sslContext(ctx) .version(HttpClient.Version.HTTP_1_1) .followRedirects(HttpClient.Redirect.ALWAYS) - .executor(exec) .build(); } @AfterTest public void teardown() throws Exception { - exec.shutdownNow(); - LightWeightHttpServer.stop(); + try { + LightWeightHttpServer.stop(); + } finally { + System.out.println("RequestBodyTest: " + failures.size() + " failures"); + int i = 0; + for (String key: failures.keySet()) { + System.out.println("test" + key + " failed: " + failures.get(key)); + failures.get(key).printStackTrace(System.out); + if (i++ > 3) { + System.out.println("..... other failures not printed ...."); + break; + } + } + } } @DataProvider @@ -128,8 +142,9 @@ public class RequestBodyTest { for (String file : new String[] { smallFilename, midSizedFilename }) for (RequestBody requestBodyType : RequestBody.values()) for (ResponseBody responseBodyType : ResponseBody.values()) - values.add(new Object[] - {uri, requestBodyType, responseBodyType, file, async}); + for (boolean bufferResponseBody : new boolean[] { false, true }) + values.add(new Object[] + {uri, requestBodyType, responseBodyType, file, async, bufferResponseBody}); return values.stream().toArray(Object[][]::new); } @@ -139,19 +154,29 @@ public class RequestBodyTest { RequestBody requestBodyType, ResponseBody responseBodyType, String file, - boolean async) + boolean async, + boolean bufferResponseBody) throws Exception { - Path filePath = Paths.get(fileroot + file); - URI uri = new URI(target); + try { + Path filePath = Paths.get(fileroot + file); + URI uri = new URI(target); - HttpRequest request = createRequest(uri, requestBodyType, filePath); + HttpRequest request = createRequest(uri, requestBodyType, filePath); - checkResponse(client, request, requestBodyType, responseBodyType, filePath, async); + checkResponse(client, request, requestBodyType, responseBodyType, filePath, async, bufferResponseBody); + } catch (Exception | Error x) { + Object[] params = new Object[] { + target, requestBodyType, responseBodyType, + file, "async=" + async, "buffer=" + bufferResponseBody + }; + failures.put(java.util.Arrays.toString(params), x); + throw x; + } } static final int DEFAULT_OFFSET = 10; - static final int DEFAULT_LENGTH = 1000; + static final int DEFAULT_LENGTH_FACTOR = 4/5; HttpRequest createRequest(URI uri, RequestBody requestBodyType, @@ -169,7 +194,9 @@ public class RequestBodyTest { rb.POST(fromByteArray(fileAsBytes)); break; case BYTE_ARRAY_OFFSET: - rb.POST(fromByteArray(fileAsBytes, DEFAULT_OFFSET, DEFAULT_LENGTH)); + rb.POST(fromByteArray(fileAsBytes, + DEFAULT_OFFSET, + fileAsBytes.length * DEFAULT_LENGTH_FACTOR)); break; case BYTE_ARRAYS: Iterable<byte[]> iterable = Arrays.asList(fileAsBytes); @@ -198,16 +225,16 @@ public class RequestBodyTest { RequestBody requestBodyType, ResponseBody responseBodyType, Path file, - boolean async) + boolean async, + boolean bufferResponseBody) throws InterruptedException, IOException { String filename = file.toFile().getAbsolutePath(); byte[] fileAsBytes = getFileBytes(filename); if (requestBodyType == RequestBody.BYTE_ARRAY_OFFSET) { // Truncate the expected response body, if only a portion was sent - fileAsBytes = Arrays.copyOfRange(fileAsBytes, - DEFAULT_OFFSET, - DEFAULT_OFFSET + DEFAULT_LENGTH); + int length = DEFAULT_OFFSET + (fileAsBytes.length * DEFAULT_LENGTH_FACTOR); + fileAsBytes = Arrays.copyOfRange(fileAsBytes, DEFAULT_OFFSET, length); } String fileAsString = new String(fileAsBytes, UTF_8); Path tempFile = Paths.get("RequestBodyTest.tmp"); @@ -215,43 +242,57 @@ public class RequestBodyTest { switch (responseBodyType) { case BYTE_ARRAY: - HttpResponse<byte[]> bar = getResponse(client, request, asByteArray(), async); + BodyHandler<byte[]> bh = asByteArray(); + if (bufferResponseBody) bh = buffering(bh, 50); + HttpResponse<byte[]> bar = getResponse(client, request, bh, async); assertEquals(bar.statusCode(), 200); assertEquals(bar.body(), fileAsBytes); break; case BYTE_ARRAY_CONSUMER: ByteArrayOutputStream baos = new ByteArrayOutputStream(); - HttpResponse<Void> v = getResponse(client, request, - asByteArrayConsumer(o -> consumerBytes(o, baos) ), async); + Consumer<Optional<byte[]>> consumer = o -> consumerBytes(o, baos); + BodyHandler<Void> bh1 = asByteArrayConsumer(consumer); + if (bufferResponseBody) bh1 = buffering(bh1, 49); + HttpResponse<Void> v = getResponse(client, request, bh1, async); byte[] ba = baos.toByteArray(); assertEquals(v.statusCode(), 200); assertEquals(ba, fileAsBytes); break; case DISCARD: Object o = new Object(); - HttpResponse<Object> or = getResponse(client, request, discard(o), async); + BodyHandler<Object> bh2 = discard(o); + if (bufferResponseBody) bh2 = buffering(bh2, 51); + HttpResponse<Object> or = getResponse(client, request, bh2, async); assertEquals(or.statusCode(), 200); assertSame(or.body(), o); break; case FILE: - HttpResponse<Path> fr = getResponse(client, request, asFile(tempFile), async); + BodyHandler<Path> bh3 = asFile(tempFile); + if (bufferResponseBody) bh3 = buffering(bh3, 48); + HttpResponse<Path> fr = getResponse(client, request, bh3, async); assertEquals(fr.statusCode(), 200); assertEquals(Files.size(tempFile), fileAsString.length()); assertEquals(Files.readAllBytes(tempFile), fileAsBytes); break; case FILE_WITH_OPTION: - fr = getResponse(client, request, asFile(tempFile, CREATE_NEW, WRITE), async); + BodyHandler<Path> bh4 = asFile(tempFile, CREATE_NEW, WRITE); + if (bufferResponseBody) bh4 = buffering(bh4, 52); + fr = getResponse(client, request, bh4, async); assertEquals(fr.statusCode(), 200); assertEquals(Files.size(tempFile), fileAsString.length()); assertEquals(Files.readAllBytes(tempFile), fileAsBytes); break; case STRING: - HttpResponse<String> sr = getResponse(client, request, asString(), async); + BodyHandler<String> bh5 = asString(); + if(bufferResponseBody) bh5 = buffering(bh5, 47); + HttpResponse<String> sr = getResponse(client, request, bh5, async); assertEquals(sr.statusCode(), 200); assertEquals(sr.body(), fileAsString); break; case STRING_WITH_CHARSET: - HttpResponse<String> r = getResponse(client, request, asString(StandardCharsets.UTF_8), async); + BodyHandler<String> bh6 = asString(StandardCharsets.UTF_8); + if (bufferResponseBody) bh6 = buffering(bh6, 53); + HttpResponse<String> r = getResponse(client, request, bh6, async); assertEquals(r.statusCode(), 200); assertEquals(r.body(), fileAsString); break; @@ -303,4 +344,28 @@ public class RequestBodyTest { throw new UncheckedIOException(x); } } + + // --- + + /* Main entry point for standalone testing of the main functional test. */ + public static void main(String... args) throws Exception { + RequestBodyTest t = new RequestBodyTest(); + t.setup(); + int count = 0; + try { + for (Object[] objs : t.exchanges()) { + count++; + out.printf("********* iteration: %d %s %s %s %s %s %s *********%n", + count, objs[0], objs[1], objs[2], objs[3], objs[4], objs[5]); + t.exchange((String) objs[0], + (RequestBody) objs[1], + (ResponseBody) objs[2], + (String) objs[3], + (boolean) objs[4], + (boolean) objs[5]); + } + } finally { + t.teardown(); + } + } } diff --git a/test/jdk/java/net/httpclient/RequestBuilderTest.java b/test/jdk/java/net/httpclient/RequestBuilderTest.java new file mode 100644 index 00000000000..49876499222 --- /dev/null +++ b/test/jdk/java/net/httpclient/RequestBuilderTest.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2017, 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 + * @summary HttpRequest[.Builder] API and behaviour checks + * @run testng RequestBuilderTest + */ + +import java.net.URI; +import java.util.List; +import jdk.incubator.http.HttpRequest; +import static java.time.Duration.ofNanos; +import static java.time.Duration.ofMinutes; +import static java.time.Duration.ofSeconds; +import static java.time.Duration.ZERO; +import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; +import static jdk.incubator.http.HttpClient.Version.HTTP_2; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.noBody; +import static jdk.incubator.http.HttpRequest.newBuilder; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class RequestBuilderTest { + + static final URI uri = URI.create("http://foo.com/"); + static final Class<NullPointerException> NPE = NullPointerException.class; + static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class; + static final Class<IllegalStateException> ISE = IllegalStateException.class; + static final Class<NumberFormatException> NFE = NumberFormatException.class; + static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class; + + @Test + public void testDefaults() { + List<HttpRequest.Builder> builders = List.of(newBuilder().uri(uri), + newBuilder(uri)); + for (HttpRequest.Builder builder : builders) { + assertFalse(builder.build().expectContinue()); + assertEquals(builder.build().method(), "GET"); + assertFalse(builder.build().bodyPublisher().isPresent()); + assertFalse(builder.build().version().isPresent()); + assertFalse(builder.build().timeout().isPresent()); + assertTrue(builder.build().headers() != null); + assertEquals(builder.build().headers().map().size(), 0); + } + } + + @Test + public void testNull() { + HttpRequest.Builder builder = newBuilder(); + + assertThrows(NPE, () -> newBuilder(null).build()); + assertThrows(NPE, () -> newBuilder(uri).uri(null).build()); + assertThrows(NPE, () -> builder.uri(null)); + assertThrows(NPE, () -> builder.version(null)); + assertThrows(NPE, () -> builder.header(null, null)); + assertThrows(NPE, () -> builder.header("name", null)); + assertThrows(NPE, () -> builder.header(null, "value")); + assertThrows(NPE, () -> builder.headers(null)); + assertThrows(NPE, () -> builder.headers(new String[] { null, null })); + assertThrows(NPE, () -> builder.headers(new String[] { "name", null })); + assertThrows(NPE, () -> builder.headers(new String[] { null, "value" })); + assertThrows(NPE, () -> builder.method(null, null)); + assertThrows(NPE, () -> builder.method("GET", null)); + assertThrows(NPE, () -> builder.method("POST", null)); + assertThrows(NPE, () -> builder.method("PUT", null)); + assertThrows(NPE, () -> builder.method("DELETE", null)); + assertThrows(NPE, () -> builder.setHeader(null, null)); + assertThrows(NPE, () -> builder.setHeader("name", null)); + assertThrows(NPE, () -> builder.setHeader(null, "value")); + assertThrows(NPE, () -> builder.timeout(null)); + assertThrows(NPE, () -> builder.DELETE(null)); + assertThrows(NPE, () -> builder.POST(null)); + assertThrows(NPE, () -> builder.PUT(null)); + } + + @Test + public void testURI() { + assertThrows(ISE, () -> newBuilder().build()); + List<URI> uris = List.of( + URI.create("ws://foo.com"), + URI.create("wss://foo.com"), + URI.create("ftp://foo.com"), + URI.create("gopher://foo.com"), + URI.create("mailto:a@b.com"), + URI.create("scheme:example.com"), + URI.create("scheme:example.com"), + URI.create("scheme:example.com/path"), + URI.create("path"), + URI.create("/path") + ); + for (URI u : uris) { + assertThrows(IAE, () -> newBuilder(u)); + assertThrows(IAE, () -> newBuilder().uri(u)); + } + + assertEquals(newBuilder(uri).build().uri(), uri); + assertEquals(newBuilder().uri(uri).build().uri(), uri); + URI https = URI.create("https://foo.com"); + assertEquals(newBuilder(https).build().uri(), https); + assertEquals(newBuilder().uri(https).build().uri(), https); + } + + @Test + public void testMethod() { + HttpRequest request = newBuilder(uri).build(); + assertEquals(request.method(), "GET"); + assertTrue(!request.bodyPublisher().isPresent()); + + request = newBuilder(uri).GET().build(); + assertEquals(request.method(), "GET"); + assertTrue(!request.bodyPublisher().isPresent()); + + request = newBuilder(uri).POST(fromString("")).GET().build(); + assertEquals(request.method(), "GET"); + assertTrue(!request.bodyPublisher().isPresent()); + + request = newBuilder(uri).PUT(fromString("")).GET().build(); + assertEquals(request.method(), "GET"); + assertTrue(!request.bodyPublisher().isPresent()); + + request = newBuilder(uri).DELETE(fromString("")).GET().build(); + assertEquals(request.method(), "GET"); + assertTrue(!request.bodyPublisher().isPresent()); + + request = newBuilder(uri).POST(fromString("")).build(); + assertEquals(request.method(), "POST"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).PUT(fromString("")).build(); + assertEquals(request.method(), "PUT"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).DELETE(fromString("")).build(); + assertEquals(request.method(), "DELETE"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).GET().POST(fromString("")).build(); + assertEquals(request.method(), "POST"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).GET().PUT(fromString("")).build(); + assertEquals(request.method(), "PUT"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).GET().DELETE(fromString("")).build(); + assertEquals(request.method(), "DELETE"); + assertTrue(request.bodyPublisher().isPresent()); + + // CONNECT is disallowed in the implementation, since it is used for + // tunneling, and is handled separately for security checks. + assertThrows(IAE, () -> newBuilder(uri).method("CONNECT", noBody()).build()); + + request = newBuilder(uri).method("GET", noBody()).build(); + assertEquals(request.method(), "GET"); + assertTrue(request.bodyPublisher().isPresent()); + + request = newBuilder(uri).method("POST", fromString("")).build(); + assertEquals(request.method(), "POST"); + assertTrue(request.bodyPublisher().isPresent()); + } + + @Test + public void testHeaders() { + HttpRequest.Builder builder = newBuilder(uri); + + String[] empty = new String[0]; + assertThrows(IAE, () -> builder.headers(empty).build()); + assertThrows(IAE, () -> builder.headers("1").build()); + assertThrows(IAE, () -> builder.headers("1", "2", "3").build()); + assertThrows(IAE, () -> builder.headers("1", "2", "3", "4", "5").build()); + assertEquals(builder.build().headers().map().size(),0); + + List<HttpRequest> requests = List.of( + // same header built from different combinations of the API + newBuilder(uri).header("A", "B").build(), + newBuilder(uri).headers("A", "B").build(), + newBuilder(uri).setHeader("A", "B").build(), + newBuilder(uri).header("A", "F").setHeader("A", "B").build(), + newBuilder(uri).headers("A", "F").setHeader("A", "B").build() + ); + + for (HttpRequest r : requests) { + assertEquals(r.headers().map().size(), 1); + assertTrue(r.headers().firstValue("A").isPresent()); + assertEquals(r.headers().firstValue("A").get(), "B"); + assertEquals(r.headers().allValues("A"), List.of("B")); + assertEquals(r.headers().allValues("C").size(), 0); + assertEquals(r.headers().map().get("A"), List.of("B")); + assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); + assertFalse(r.headers().firstValue("C").isPresent()); + // a non-exhaustive list of mutators + assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); + assertThrows(UOE, () -> r.headers().map().remove("A")); + assertThrows(UOE, () -> r.headers().map().remove("A", "B")); + assertThrows(UOE, () -> r.headers().map().clear()); + assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); + assertThrows(UOE, () -> r.headers().allValues("A").remove(1)); + assertThrows(UOE, () -> r.headers().allValues("A").clear()); + assertThrows(UOE, () -> r.headers().allValues("A").add("Z")); + assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z"))); + assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z")); + } + + requests = List.of( + // same headers built from different combinations of the API + newBuilder(uri).header("A", "B") + .header("C", "D").build(), + newBuilder(uri).header("A", "B") + .headers("C", "D").build(), + newBuilder(uri).header("A", "B") + .setHeader("C", "D").build(), + newBuilder(uri).headers("A", "B") + .headers("C", "D").build(), + newBuilder(uri).headers("A", "B") + .header("C", "D").build(), + newBuilder(uri).headers("A", "B") + .setHeader("C", "D").build(), + newBuilder(uri).setHeader("A", "B") + .setHeader("C", "D").build(), + newBuilder(uri).setHeader("A", "B") + .header("C", "D").build(), + newBuilder(uri).setHeader("A", "B") + .headers("C", "D").build(), + newBuilder(uri).headers("A", "B", "C", "D").build() + ); + + for (HttpRequest r : requests) { + assertEquals(r.headers().map().size(), 2); + assertTrue(r.headers().firstValue("A").isPresent()); + assertEquals(r.headers().firstValue("A").get(), "B"); + assertEquals(r.headers().allValues("A"), List.of("B")); + assertTrue(r.headers().firstValue("C").isPresent()); + assertEquals(r.headers().firstValue("C").get(), "D"); + assertEquals(r.headers().allValues("C"), List.of("D")); + assertEquals(r.headers().map().get("C"), List.of("D")); + assertThrows(NFE, () -> r.headers().firstValueAsLong("C")); + assertFalse(r.headers().firstValue("E").isPresent()); + // a smaller non-exhaustive list of mutators + assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); + assertThrows(UOE, () -> r.headers().map().remove("C")); + assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); + assertThrows(UOE, () -> r.headers().allValues("A").clear()); + assertThrows(UOE, () -> r.headers().allValues("C").add("Z")); + } + + requests = List.of( + // same multi-value headers built from different combinations of the API + newBuilder(uri).header("A", "B") + .header("A", "C").build(), + newBuilder(uri).header("A", "B") + .headers("A", "C").build(), + newBuilder(uri).headers("A", "B") + .headers("A", "C").build(), + newBuilder(uri).headers("A", "B") + .header("A", "C").build(), + newBuilder(uri).setHeader("A", "B") + .header("A", "C").build(), + newBuilder(uri).setHeader("A", "B") + .headers("A", "C").build(), + newBuilder(uri).header("A", "D") + .setHeader("A", "B") + .headers("A", "C").build(), + newBuilder(uri).headers("A", "B", "A", "C").build() + ); + + for (HttpRequest r : requests) { + assertEquals(r.headers().map().size(), 1); + assertTrue(r.headers().firstValue("A").isPresent()); + assertTrue(r.headers().allValues("A").containsAll(List.of("B", "C"))); + assertEquals(r.headers().allValues("C").size(), 0); + assertEquals(r.headers().map().get("A"), List.of("B", "C")); + assertThrows(NFE, () -> r.headers().firstValueAsLong("A")); + assertFalse(r.headers().firstValue("C").isPresent()); + // a non-exhaustive list of mutators + assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z"))); + assertThrows(UOE, () -> r.headers().map().remove("A")); + assertThrows(UOE, () -> r.headers().map().remove("A", "B")); + assertThrows(UOE, () -> r.headers().map().clear()); + assertThrows(UOE, () -> r.headers().allValues("A").remove("B")); + assertThrows(UOE, () -> r.headers().allValues("A").remove(1)); + assertThrows(UOE, () -> r.headers().allValues("A").clear()); + assertThrows(UOE, () -> r.headers().allValues("A").add("Z")); + assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z"))); + assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z")); + } + } + + @Test + public void testCopy() { + HttpRequest.Builder builder = newBuilder(uri).expectContinue(true) + .header("A", "B") + .POST(fromString("")) + .timeout(ofSeconds(30)) + .version(HTTP_1_1); + HttpRequest.Builder copy = builder.copy(); + assertTrue(builder != copy); + + // modify the original builder before building from the copy + builder.GET().timeout(ofSeconds(5)).version(HTTP_2).setHeader("A", "C"); + + HttpRequest copyRequest = copy.build(); + assertEquals(copyRequest.uri(), uri); + assertEquals(copyRequest.expectContinue(), true); + assertEquals(copyRequest.headers().map().get("A"), List.of("B")); + assertEquals(copyRequest.method(), "POST"); + assertEquals(copyRequest.bodyPublisher().isPresent(), true); + assertEquals(copyRequest.timeout().get(), ofSeconds(30)); + assertTrue(copyRequest.version().isPresent()); + assertEquals(copyRequest.version().get(), HTTP_1_1); + } + + @Test + public void testTimeout() { + HttpRequest.Builder builder = newBuilder(uri); + assertThrows(IAE, () -> builder.timeout(ZERO)); + assertThrows(IAE, () -> builder.timeout(ofSeconds(0))); + assertThrows(IAE, () -> builder.timeout(ofSeconds(-1))); + assertThrows(IAE, () -> builder.timeout(ofNanos(-100))); + assertEquals(builder.timeout(ofNanos(15)).build().timeout().get(), ofNanos(15)); + assertEquals(builder.timeout(ofSeconds(50)).build().timeout().get(), ofSeconds(50)); + assertEquals(builder.timeout(ofMinutes(30)).build().timeout().get(), ofMinutes(30)); + } + + @Test + public void testExpect() { + HttpRequest.Builder builder = newBuilder(uri); + assertEquals(builder.build().expectContinue(), false); + assertEquals(builder.expectContinue(true).build().expectContinue(), true); + assertEquals(builder.expectContinue(false).build().expectContinue(), false); + assertEquals(builder.expectContinue(true).build().expectContinue(), true); + } + + @Test + public void testEquals() { + assertNotEquals(newBuilder(URI.create("http://foo.com")), + newBuilder(URI.create("http://bar.com"))); + + HttpRequest.Builder builder = newBuilder(uri); + assertEquals(builder.build(), builder.build()); + assertEquals(builder.build(), newBuilder(uri).build()); + + builder.POST(noBody()); + assertEquals(builder.build(), builder.build()); + assertEquals(builder.build(), newBuilder(uri).POST(noBody()).build()); + assertEquals(builder.build(), newBuilder(uri).POST(fromString("")).build()); + assertNotEquals(builder.build(), newBuilder(uri).build()); + assertNotEquals(builder.build(), newBuilder(uri).GET().build()); + assertNotEquals(builder.build(), newBuilder(uri).PUT(noBody()).build()); + + builder = newBuilder(uri).header("x", "y"); + assertEquals(builder.build(), builder.build()); + assertEquals(builder.build(), newBuilder(uri).header("x", "y").build()); + assertNotEquals(builder.build(), newBuilder(uri).header("x", "Z").build()); + assertNotEquals(builder.build(), newBuilder(uri).header("z", "y").build()); + } +} diff --git a/test/jdk/java/net/httpclient/RequestProcessorExceptions.java b/test/jdk/java/net/httpclient/RequestProcessorExceptions.java new file mode 100644 index 00000000000..c84d5b059c1 --- /dev/null +++ b/test/jdk/java/net/httpclient/RequestProcessorExceptions.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, 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 + * @run testng RequestProcessorExceptions + */ + +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile; + +public class RequestProcessorExceptions { + + @DataProvider(name = "byteArrayOOBs") + public Object[][] byteArrayOOBs() { + return new Object[][] { + { new byte[100], 1, 100 }, + { new byte[100], -1, 10 }, + { new byte[100], 99, 2 }, + { new byte[1], -100, 1 } }; + } + + @Test(dataProvider = "byteArrayOOBs", expectedExceptions = IndexOutOfBoundsException.class) + public void fromByteArrayCheck(byte[] buf, int offset, int length) { + fromByteArray(buf, offset, length); + } + + @DataProvider(name = "nonExistentFiles") + public Object[][] nonExistentFiles() { + List<Path> paths = List.of(Paths.get("doesNotExist"), + Paths.get("tsixEtoNseod"), + Paths.get("doesNotExist2")); + paths.forEach(p -> { + if (Files.exists(p)) + throw new AssertionError("Unexpected " + p); + }); + + return paths.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); + } + + @Test(dataProvider = "nonExistentFiles", expectedExceptions = FileNotFoundException.class) + public void fromFileCheck(Path path) throws Exception { + fromFile(path); + } + + // --- + + /* Main entry point for standalone testing of the main functional test. */ + public static void main(String... args) throws Exception { + RequestProcessorExceptions t = new RequestProcessorExceptions(); + for (Object[] objs : t.byteArrayOOBs()) { + try { + t.fromByteArrayCheck((byte[]) objs[0], (int) objs[1], (int) objs[2]); + throw new RuntimeException("fromByteArrayCheck failed"); + } catch (IndexOutOfBoundsException expected) { /* Ok */ } + } + for (Object[] objs : t.nonExistentFiles()) { + try { + t.fromFileCheck((Path) objs[0]); + throw new RuntimeException("fromFileCheck failed"); + } catch (FileNotFoundException expected) { /* Ok */ } + } + } +} diff --git a/test/jdk/java/net/httpclient/ShortRequestBody.java b/test/jdk/java/net/httpclient/ShortRequestBody.java index 9740bb30dcd..9fd3187e25a 100644 --- a/test/jdk/java/net/httpclient/ShortRequestBody.java +++ b/test/jdk/java/net/httpclient/ShortRequestBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -21,10 +21,10 @@ * questions. */ -import java.io.*; -import jdk.incubator.http.HttpClient; -import jdk.incubator.http.HttpResponse; -import jdk.incubator.http.HttpRequest; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; @@ -32,14 +32,20 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutionException; import java.util.concurrent.Flow; import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeUnit; -import static java.lang.System.out; +import java.util.function.Supplier; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpTimeoutException; + +import static java.lang.System.err; import static java.nio.charset.StandardCharsets.US_ASCII; import static jdk.incubator.http.HttpResponse.BodyHandler.discard; import static java.nio.charset.StandardCharsets.UTF_8; @@ -48,23 +54,13 @@ import static java.nio.charset.StandardCharsets.UTF_8; * @test * @bug 8151441 * @summary Request body of incorrect (larger or smaller) sizes than that - * reported by the body processor + * reported by the body publisher * @run main/othervm ShortRequestBody */ public class ShortRequestBody { static final Path testSrc = Paths.get(System.getProperty("test.src", ".")); - static volatile HttpClient staticDefaultClient; - - static HttpClient defaultClient() { - if (staticDefaultClient == null) { - synchronized (ShortRequestBody.class) { - staticDefaultClient = HttpClient.newHttpClient(); - } - } - return staticDefaultClient; - } // Some body types ( sources ) for testing. static final String STRING_BODY = "Hello world"; @@ -79,14 +75,14 @@ public class ShortRequestBody { fileSize(FILE_BODY) }; static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 }; - // A delegating body processor. Subtypes will have a concrete body type. + // A delegating Body Publisher. Subtypes will have a concrete body type. static abstract class AbstractDelegateRequestBody - implements HttpRequest.BodyProcessor { + implements HttpRequest.BodyPublisher { - final HttpRequest.BodyProcessor delegate; + final HttpRequest.BodyPublisher delegate; final long contentLength; - AbstractDelegateRequestBody(HttpRequest.BodyProcessor delegate, + AbstractDelegateRequestBody(HttpRequest.BodyPublisher delegate, long contentLength) { this.delegate = delegate; this.contentLength = contentLength; @@ -101,26 +97,26 @@ public class ShortRequestBody { public long contentLength() { return contentLength; /* may be wrong! */ } } - // Request body processors that may generate a different number of actual + // Request body Publishers that may generate a different number of actual // bytes to that of what is reported through their {@code contentLength}. static class StringRequestBody extends AbstractDelegateRequestBody { StringRequestBody(String body, int additionalLength) { - super(HttpRequest.BodyProcessor.fromString(body), + super(HttpRequest.BodyPublisher.fromString(body), body.getBytes(UTF_8).length + additionalLength); } } static class ByteArrayRequestBody extends AbstractDelegateRequestBody { ByteArrayRequestBody(byte[] body, int additionalLength) { - super(HttpRequest.BodyProcessor.fromByteArray(body), + super(HttpRequest.BodyPublisher.fromByteArray(body), body.length + additionalLength); } } static class FileRequestBody extends AbstractDelegateRequestBody { FileRequestBody(Path path, int additionalLength) throws IOException { - super(HttpRequest.BodyProcessor.fromFile(path), + super(HttpRequest.BodyPublisher.fromFile(path), Files.size(path) + additionalLength); } } @@ -128,53 +124,63 @@ public class ShortRequestBody { // --- public static void main(String[] args) throws Exception { + HttpClient sharedClient = HttpClient.newHttpClient(); + List<Supplier<HttpClient>> clientSuppliers = new ArrayList<>(); + clientSuppliers.add(() -> HttpClient.newHttpClient()); + clientSuppliers.add(() -> sharedClient); + try (Server server = new Server()) { - URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/"); + for (Supplier<HttpClient> cs : clientSuppliers) { + err.println("\n---- next supplier ----\n"); + URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/"); - // sanity - success(uri, new StringRequestBody(STRING_BODY, 0)); - success(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0)); - success(uri, new FileRequestBody(FILE_BODY, 0)); + // sanity ( 6 requests to keep client and server offsets easy to workout ) + success(cs, uri, new StringRequestBody(STRING_BODY, 0)); + success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0)); + success(cs, uri, new FileRequestBody(FILE_BODY, 0)); + success(cs, uri, new StringRequestBody(STRING_BODY, 0)); + success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0)); + success(cs, uri, new FileRequestBody(FILE_BODY, 0)); - for (int i=1; i< BODY_OFFSETS.length; i++) { - failureBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i])); - failureBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i])); - failureBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i])); + for (int i = 1; i < BODY_OFFSETS.length; i++) { + failureBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i])); + failureBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i])); + failureBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i])); - failureNonBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i])); - failureNonBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i])); - failureNonBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i])); - } - } finally { - Executor def = defaultClient().executor(); - if (def instanceof ExecutorService) { - ((ExecutorService)def).shutdownNow(); + failureNonBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i])); + failureNonBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i])); + failureNonBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i])); + } } } } - static void success(URI uri, HttpRequest.BodyProcessor processor) + static void success(Supplier<HttpClient> clientSupplier, + URI uri, + HttpRequest.BodyPublisher publisher) throws Exception { CompletableFuture<HttpResponse<Void>> cf; HttpRequest request = HttpRequest.newBuilder(uri) - .POST(processor) + .POST(publisher) .build(); - cf = defaultClient().sendAsync(request, discard(null)); + cf = clientSupplier.get().sendAsync(request, discard(null)); HttpResponse<Void> resp = cf.get(30, TimeUnit.SECONDS); - out.println("Response code: " + resp.statusCode()); + err.println("Response code: " + resp.statusCode()); check(resp.statusCode() == 200, "Expected 200, got ", resp.statusCode()); } - static void failureNonBlocking(URI uri, HttpRequest.BodyProcessor processor) + static void failureNonBlocking(Supplier<HttpClient> clientSupplier, + URI uri, + HttpRequest.BodyPublisher publisher) throws Exception { CompletableFuture<HttpResponse<Void>> cf; HttpRequest request = HttpRequest.newBuilder(uri) - .POST(processor) + .POST(publisher) .build(); - cf = defaultClient().sendAsync(request, discard(null)); + cf = clientSupplier.get().sendAsync(request, discard(null)); try { HttpResponse<Void> r = cf.get(30, TimeUnit.SECONDS); @@ -182,23 +188,34 @@ public class ShortRequestBody { } catch (TimeoutException x) { throw new RuntimeException("Unexpected timeout", x); } catch (ExecutionException expected) { - out.println("Caught expected: " + expected); - check(expected.getCause() instanceof IOException, + err.println("Caught expected: " + expected); + Throwable t = expected.getCause(); + check(t instanceof IOException, "Expected cause IOException, but got: ", expected.getCause()); + String msg = t.getMessage(); + check(msg.contains("Too many") || msg.contains("Too few"), + "Expected Too many|Too few, got: ", t); } } - static void failureBlocking(URI uri, HttpRequest.BodyProcessor processor) + static void failureBlocking(Supplier<HttpClient> clientSupplier, + URI uri, + HttpRequest.BodyPublisher publisher) throws Exception { HttpRequest request = HttpRequest.newBuilder(uri) - .POST(processor) + .POST(publisher) .build(); try { - HttpResponse<Void> r = defaultClient().send(request, discard(null)); + HttpResponse<Void> r = clientSupplier.get().send(request, discard(null)); throw new RuntimeException("Unexpected response: " + r.statusCode()); + } catch (HttpTimeoutException x) { + throw new RuntimeException("Unexpected timeout", x); } catch (IOException expected) { - out.println("Caught expected: " + expected); + err.println("Caught expected: " + expected); + String msg = expected.getMessage(); + check(msg.contains("Too many") || msg.contains("Too few"), + "Expected Too many|Too few, got: ", expected); } } @@ -225,20 +242,40 @@ public class ShortRequestBody { while (!closed) { try (Socket s = ss.accept()) { + err.println("Server: got connection"); InputStream is = s.getInputStream(); readRequestHeaders(is); byte[] ba = new byte[1024]; int length = BODY_LENGTHS[count % 3]; length += BODY_OFFSETS[offset]; + err.println("Server: count=" + count + ", offset=" + offset); + err.println("Server: expecting " +length+ " bytes"); + int read = is.readNBytes(ba, 0, length); + err.println("Server: actually read " + read + " bytes"); - is.readNBytes(ba, 0, length); - - OutputStream os = s.getOutputStream(); - os.write(RESPONSE.getBytes(US_ASCII)); + // Update the counts before replying, to prevent the + // client-side racing reset with this thread. count++; if (count % 6 == 0) // 6 is the number of failure requests per offset offset++; + if (count % 42 == 0) { + count = 0; // reset, for second iteration + offset = 0; + } + + if (read < length) { + // no need to reply, client has already closed + // ensure closed + if (is.read() != -1) + new AssertionError("Unexpected read"); + } else { + OutputStream os = s.getOutputStream(); + err.println("Server: writing " + + RESPONSE.getBytes(US_ASCII).length + " bytes"); + os.write(RESPONSE.getBytes(US_ASCII)); + } + } catch (IOException e) { if (!closed) System.out.println("Unexpected" + e); diff --git a/test/jdk/java/net/httpclient/SmallTimeout.java b/test/jdk/java/net/httpclient/SmallTimeout.java index c06994c417b..28e6958d4ff 100644 --- a/test/jdk/java/net/httpclient/SmallTimeout.java +++ b/test/jdk/java/net/httpclient/SmallTimeout.java @@ -40,7 +40,7 @@ import static jdk.incubator.http.HttpResponse.BodyHandler.discard; * @test * @bug 8178147 * @summary Ensures that small timeouts do not cause hangs due to race conditions - * @run main/othervm SmallTimeout + * @run main/othervm -Djdk.incubator.http.internal.common.DEBUG=true SmallTimeout */ // To enable logging use. Not enabled by default as it changes the dynamics @@ -52,7 +52,25 @@ public class SmallTimeout { static int[] TIMEOUTS = {2, 1, 3, 2, 100, 1}; // A queue for placing timed out requests so that their order can be checked. - static LinkedBlockingQueue<HttpRequest> queue = new LinkedBlockingQueue<>(); + static LinkedBlockingQueue<HttpResult> queue = new LinkedBlockingQueue<>(); + + static final class HttpResult { + final HttpRequest request; + final Throwable failed; + HttpResult(HttpRequest request, Throwable failed) { + this.request = request; + this.failed = failed; + } + + static HttpResult of(HttpRequest request) { + return new HttpResult(request, null); + } + + static HttpResult of(HttpRequest request, Throwable t) { + return new HttpResult(request, t); + } + + } static volatile boolean error; @@ -76,8 +94,10 @@ public class SmallTimeout { CompletableFuture<HttpResponse<Object>> response = client .sendAsync(req, discard(null)) .whenComplete((HttpResponse<Object> r, Throwable t) -> { + Throwable cause = null; if (r != null) { out.println("Unexpected response: " + r); + cause = new RuntimeException("Unexpected response"); error = true; } if (t != null) { @@ -85,6 +105,7 @@ public class SmallTimeout { out.println("Wrong exception type:" + t.toString()); Throwable c = t.getCause() == null ? t : t.getCause(); c.printStackTrace(); + cause = c; error = true; } else { out.println("Caught expected timeout: " + t.getCause()); @@ -92,9 +113,10 @@ public class SmallTimeout { } if (t == null && r == null) { out.println("Both response and throwable are null!"); + cause = new RuntimeException("Both response and throwable are null!"); error = true; } - queue.add(req); + queue.add(HttpResult.of(req,cause)); }); } System.out.println("All requests submitted. Waiting ..."); @@ -118,15 +140,18 @@ public class SmallTimeout { final HttpRequest req = requests[i]; executor.execute(() -> { + Throwable cause = null; try { client.send(req, discard(null)); } catch (HttpTimeoutException e) { out.println("Caught expected timeout: " + e); - queue.offer(req); - } catch (IOException | InterruptedException ee) { + } catch (Throwable ee) { Throwable c = ee.getCause() == null ? ee : ee.getCause(); c.printStackTrace(); + cause = c; error = true; + } finally { + queue.offer(HttpResult.of(req, cause)); } }); } @@ -139,18 +164,20 @@ public class SmallTimeout { if (error) throw new RuntimeException("Failed. Check output"); - } finally { - ((ExecutorService) client.executor()).shutdownNow(); } } static void checkReturn(HttpRequest[] requests) throws InterruptedException { // wait for exceptions and check order + boolean ok = true; for (int j = 0; j < TIMEOUTS.length; j++) { - HttpRequest req = queue.take(); - out.println("Got request from queue " + req + ", order: " + getRequest(req, requests)); + HttpResult res = queue.take(); + HttpRequest req = res.request; + out.println("Got request from queue " + req + ", order: " + getRequest(req, requests) + + (res.failed == null ? "" : " failed: " + res.failed)); + ok = ok && res.failed == null; } - out.println("Return ok"); + out.println("Return " + (ok ? "ok" : "nok")); } /** Returns the index of the request in the array. */ diff --git a/test/jdk/java/net/httpclient/SmokeTest.java b/test/jdk/java/net/httpclient/SmokeTest.java index 7d18ae0015b..55f9d4e1aa7 100644 --- a/test/jdk/java/net/httpclient/SmokeTest.java +++ b/test/jdk/java/net/httpclient/SmokeTest.java @@ -32,7 +32,7 @@ * @compile ../../../com/sun/net/httpserver/LogFilter.java * @compile ../../../com/sun/net/httpserver/EchoHandler.java * @compile ../../../com/sun/net/httpserver/FileServerHandler.java - * @run main/othervm -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest + * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest */ import com.sun.net.httpserver.Headers; @@ -43,12 +43,12 @@ import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; +import java.net.Proxy; +import java.net.SocketAddress; import java.util.concurrent.atomic.AtomicInteger; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.ProxySelector; -import java.net.ServerSocket; -import java.net.Socket; import java.net.URI; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; @@ -81,9 +81,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; import jdk.testlibrary.SimpleSSLContext; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromInputStream; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromInputStream; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.*; import static jdk.incubator.http.HttpResponse.BodyHandler.asFile; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; @@ -172,7 +172,6 @@ public class SmokeTest { .build(); try { - test1(httproot + "files/foo.txt", true); test1(httproot + "files/foo.txt", false); test1(httpsroot + "files/foo.txt", true); @@ -255,7 +254,10 @@ public class SmokeTest { String body = response.body(); if (!body.equals("This is foo.txt\r\n")) { - throw new RuntimeException(); + throw new RuntimeException("Did not get expected body: " + + "\n\t expected \"This is foo.txt\\r\\n\"" + + "\n\t received \"" + + body.replace("\r", "\\r").replace("\n","\\n") + "\""); } // repeat async @@ -296,14 +298,13 @@ public class SmokeTest { static void test2a(String s) throws Exception { System.out.print("test2a: " + s); URI uri = new URI(s); - Path p = Util.getTempFile(128 * 1024); - //Path p = Util.getTempFile(1 * 1024); + Path p = getTempFile(128 * 1024); HttpRequest request = HttpRequest.newBuilder(uri) .POST(fromFile(p)) .build(); - Path resp = Util.getTempFile(1); // will be overwritten + Path resp = getTempFile(1); // will be overwritten HttpResponse<Path> response = client.send(request, @@ -315,6 +316,11 @@ public class SmokeTest { throw new RuntimeException( "Expected 200, got [ " + response.statusCode() + " ]"); } + // no redirection, etc, should be no previous response + if (response.previousResponse().isPresent()) { + throw new RuntimeException( + "Unexpected previous response: " + response.previousResponse().get()); + } Path reply = response.body(); //System.out.println("Reply stored in " + reply.toString()); cmpFileContent(reply, p); @@ -347,7 +353,7 @@ public class SmokeTest { if (Files.size(downloaded) != Files.size(midSizedFile)) { throw new RuntimeException("Size mismatch"); } - + checkPreviousRedirectResponses(request, response); System.out.printf(" (count: %d) ", handler.count()); // repeat with async api @@ -368,10 +374,77 @@ public class SmokeTest { if (Files.size(downloaded) != Files.size(midSizedFile)) { throw new RuntimeException("Size mismatch 2"); } + + checkPreviousRedirectResponses(request, response); System.out.printf(" (count: %d) ", handler.count()); System.out.println(" OK"); } + static void checkPreviousRedirectResponses(HttpRequest initialRequest, + HttpResponse<?> finalResponse) { + // there must be at least one previous response + finalResponse.previousResponse() + .orElseThrow(() -> new RuntimeException("no previous response")); + + HttpResponse<?> response = finalResponse; + do { + URI uri = response.uri(); + response = response.previousResponse().get(); + check(300 <= response.statusCode() && response.statusCode() <= 309, + "Expected 300 <= code <= 309, got:" + response.statusCode()); + check(response.body() == null, "Unexpected body: " + response.body()); + String locationHeader = response.headers().firstValue("Location") + .orElseThrow(() -> new RuntimeException("no previous Location")); + check(uri.toString().endsWith(locationHeader), + "URI: " + uri + ", Location: " + locationHeader); + } while (response.previousResponse().isPresent()); + + // initial + check(initialRequest.equals(response.request()), + "Expected initial request [%s] to equal last prev req [%s]", + initialRequest, response.request()); + } + + static void check(boolean cond, Object... msg) { + if (cond) + return; + StringBuilder sb = new StringBuilder(); + for (Object o : msg) + sb.append(o); + throw new RuntimeException(sb.toString()); + } + + /** + * A Proxy Selector that wraps a ProxySelector.of(), and counts the number + * of times its select method has been invoked. This can be used to ensure + * that the Proxy Selector is invoked only once per HttpClient.sendXXX + * invocation. + */ + static class CountingProxySelector extends ProxySelector { + private final ProxySelector proxySelector; + private volatile int count; // 0 + private CountingProxySelector(InetSocketAddress proxyAddress) { + proxySelector = ProxySelector.of(proxyAddress); + } + + public static CountingProxySelector of(InetSocketAddress proxyAddress) { + return new CountingProxySelector(proxyAddress); + } + + int count() { return count; } + + @Override + public List<Proxy> select(URI uri) { + count++; + return proxySelector.select(uri); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + proxySelector.connectFailed(uri, sa, ioe); + } + } + // Proxies static void test4(String s) throws Exception { System.out.print("test4: " + s); @@ -381,16 +454,17 @@ public class SmokeTest { ExecutorService e = Executors.newCachedThreadPool(); + CountingProxySelector ps = CountingProxySelector.of(proxyAddr); HttpClient cl = HttpClient.newBuilder() .executor(e) - .proxy(ProxySelector.of(proxyAddr)) + .proxy(ps) .sslContext(ctx) .sslParameters(sslparams) .build(); HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); - CompletableFuture<String> fut = client.sendAsync(request, asString()) + CompletableFuture<String> fut = cl.sendAsync(request, asString()) .thenApply((response) -> response.body()); String body = fut.get(5, TimeUnit.HOURS); @@ -401,6 +475,9 @@ public class SmokeTest { throw new RuntimeException( "Body mismatch: expected [" + body + "], got [" + fc + "]"); } + if (ps.count() != 1) { + throw new RuntimeException("CountingProxySelector. Expected 1, got " + ps.count()); + } e.shutdownNow(); System.out.println(" OK"); } @@ -465,7 +542,7 @@ public class SmokeTest { @SuppressWarnings("rawtypes") static void test7(String target) throws Exception { System.out.print("test7: " + target); - Path requestBody = Util.getTempFile(128 * 1024); + Path requestBody = getTempFile(128 * 1024); // First test URI uri = new URI(target); HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build(); @@ -644,7 +721,7 @@ public class SmokeTest { ch.setLevel(Level.SEVERE); logger.addHandler(ch); - String root = System.getProperty ("test.src")+ "/docs"; + String root = System.getProperty ("test.src", ".")+ "/docs"; InetSocketAddress addr = new InetSocketAddress (0); s1 = HttpServer.create (addr, 0); if (s1 instanceof HttpsServer) { @@ -690,167 +767,112 @@ public class SmokeTest { proxyPort = proxy.getPort(); System.out.println("Proxy port = " + proxyPort); } -} -class Configurator extends HttpsConfigurator { - public Configurator(SSLContext ctx) { - super(ctx); - } + static class RedirectHandler implements HttpHandler { + private final String root; + private volatile int count = 0; - public void configure (HttpsParameters params) { - params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); - } -} + RedirectHandler(String root) { + this.root = root; + } -class UploadServer extends Thread { - int statusCode; - ServerSocket ss; - int port; - int size; - Object lock; - boolean failed = false; + @Override + public synchronized void handle(HttpExchange t) throws IOException { + byte[] buf = new byte[2048]; + try (InputStream is = t.getRequestBody()) { + while (is.read(buf) != -1) ; + } - UploadServer(int size) throws IOException { - this.statusCode = statusCode; - this.size = size; - ss = new ServerSocket(0); - port = ss.getLocalPort(); - lock = new Object(); - } + Headers responseHeaders = t.getResponseHeaders(); - int port() { - return port; - } + if (count++ < 1) { + responseHeaders.add("Location", root + "/foo/" + count); + } else { + responseHeaders.add("Location", SmokeTest.midSizedFilename); + } + t.sendResponseHeaders(301, 64 * 1024); + byte[] bb = new byte[1024]; + OutputStream os = t.getResponseBody(); + for (int i=0; i<64; i++) { + os.write(bb); + } + os.close(); + t.close(); + } - int size() { - return size; - } + int count() { + return count; + } - // wait a sec before calling this - boolean failed() { - synchronized(lock) { - return failed; + void reset() { + count = 0; } } - @Override - public void run () { - int nbytes = 0; - Socket s = null; + static class RedirectErrorHandler implements HttpHandler { + private final String root; + private volatile int count = 1; - synchronized(lock) { + RedirectErrorHandler(String root) { + this.root = root; + } + + synchronized int count() { + return count; + } + + synchronized void increment() { + count++; + } + + @Override + public synchronized void handle(HttpExchange t) throws IOException { + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); + } + + Headers map = t.getResponseHeaders(); + String redirect = root + "/foo/" + Integer.toString(count); + increment(); + map.add("Location", redirect); + t.sendResponseHeaders(301, -1); + t.close(); + } + } + + static class DelayHandler implements HttpHandler { + + CyclicBarrier bar1 = new CyclicBarrier(2); + CyclicBarrier bar2 = new CyclicBarrier(2); + CyclicBarrier bar3 = new CyclicBarrier(2); + + CyclicBarrier barrier1() { + return bar1; + } + + CyclicBarrier barrier2() { + return bar2; + } + + @Override + public synchronized void handle(HttpExchange he) throws IOException { + he.getRequestBody().readAllBytes(); try { - s = ss.accept(); - - InputStream is = s.getInputStream(); - OutputStream os = s.getOutputStream(); - os.write("HTTP/1.1 201 OK\r\nContent-length: 0\r\n\r\n".getBytes()); - int n; - byte[] buf = new byte[8000]; - while ((n=is.read(buf)) != -1) { - nbytes += n; - } - } catch (IOException e) { - System.out.println ("read " + nbytes); - System.out.println ("size " + size); - failed = nbytes >= size; - } finally { - try { - ss.close(); - if (s != null) - s.close(); - } catch (IOException e) {} - } + bar1.await(); + bar2.await(); + } catch (Exception e) { } + he.sendResponseHeaders(200, -1); // will probably fail + he.close(); } } -} -class RedirectHandler implements HttpHandler { - String root; - volatile int count = 0; - - RedirectHandler(String root) { - this.root = root; - } - - @Override - public synchronized void handle(HttpExchange t) - throws IOException - { - byte[] buf = new byte[2048]; - try (InputStream is = t.getRequestBody()) { - while (is.read(buf) != -1) ; + static class Configurator extends HttpsConfigurator { + public Configurator(SSLContext ctx) { + super(ctx); } - Headers responseHeaders = t.getResponseHeaders(); - - if (count++ < 1) { - responseHeaders.add("Location", root + "/foo/" + count); - } else { - responseHeaders.add("Location", SmokeTest.midSizedFilename); - } - t.sendResponseHeaders(301, -1); - t.close(); - } - - int count() { - return count; - } - - void reset() { - count = 0; - } -} - -class RedirectErrorHandler implements HttpHandler { - String root; - volatile int count = 1; - - RedirectErrorHandler(String root) { - this.root = root; - } - - synchronized int count() { - return count; - } - - synchronized void increment() { - count++; - } - - @Override - public synchronized void handle (HttpExchange t) - throws IOException - { - byte[] buf = new byte[2048]; - try (InputStream is = t.getRequestBody()) { - while (is.read(buf) != -1) ; - } - - Headers map = t.getResponseHeaders(); - String redirect = root + "/foo/" + Integer.toString(count); - increment(); - map.add("Location", redirect); - t.sendResponseHeaders(301, -1); - t.close(); - } -} - -class Util { - static byte[] readAll(InputStream is) throws IOException { - byte[] buf = new byte[1024]; - byte[] result = new byte[0]; - - while (true) { - int n = is.read(buf); - if (n > 0) { - byte[] b1 = new byte[result.length + n]; - System.arraycopy(result, 0, b1, 0, result.length); - System.arraycopy(buf, 0, b1, result.length, n); - result = b1; - } else if (n == -1) { - return result; - } + public void configure (HttpsParameters params) { + params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); } } @@ -858,8 +880,8 @@ class Util { File f = File.createTempFile("test", "txt"); f.deleteOnExit(); byte[] buf = new byte[2048]; - for (int i=0; i<buf.length; i++) - buf[i] = (byte)i; + for (int i = 0; i < buf.length; i++) + buf[i] = (byte) i; FileOutputStream fos = new FileOutputStream(f); while (size > 0) { @@ -872,33 +894,6 @@ class Util { } } -class DelayHandler implements HttpHandler { - - CyclicBarrier bar1 = new CyclicBarrier(2); - CyclicBarrier bar2 = new CyclicBarrier(2); - CyclicBarrier bar3 = new CyclicBarrier(2); - - CyclicBarrier barrier1() { - return bar1; - } - - CyclicBarrier barrier2() { - return bar2; - } - - @Override - public synchronized void handle(HttpExchange he) throws IOException { - byte[] buf = Util.readAll(he.getRequestBody()); - try { - bar1.await(); - bar2.await(); - } catch (Exception e) {} - he.sendResponseHeaders(200, -1); // will probably fail - he.close(); - } - -} - // check for simple hardcoded sequence and use remote address // to check. // First 4 requests executed in sequence (should use same connection/address) diff --git a/test/jdk/java/net/httpclient/SplitResponse.java b/test/jdk/java/net/httpclient/SplitResponse.java index 645e862699e..c2434f57e61 100644 --- a/test/jdk/java/net/httpclient/SplitResponse.java +++ b/test/jdk/java/net/httpclient/SplitResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -21,38 +21,49 @@ * questions. */ - import java.io.IOException; -import jdk.incubator.http.HttpClient; -import jdk.incubator.http.HttpRequest; -import jdk.incubator.http.HttpResponse; import java.net.URI; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; +import javax.net.ssl.SSLContext; +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLServerSocketFactory; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpClient.Version; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.testlibrary.SimpleSSLContext; +import static java.lang.System.out; +import static java.lang.String.format; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; /** * @test * @bug 8087112 - * @key intermittent - * @build Server - * @run main/othervm -Djava.net.HttpClient.log=all SplitResponse + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @build MockServer + * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all SplitResponse */ /** * Similar test to QuickResponses except that each byte of the response * is sent in a separate packet, which tests the stability of the implementation - * for receiving unusual packet sizes. + * for receiving unusual packet sizes. Additionally, tests scenarios there + * connections that are retrieved from the connection pool may reach EOF before + * being reused. */ public class SplitResponse { - static Server server; + static String response(String body, boolean serverKeepalive) { + StringBuilder sb = new StringBuilder(); + sb.append("HTTP/1.1 200 OK\r\n"); + if (!serverKeepalive) + sb.append("Connection: Close\r\n"); - static String response(String body) { - return "HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-length: " - + Integer.toString(body.length()) - + "\r\n\r\n" + body; + sb.append("Content-length: ").append(body.length()).append("\r\n"); + sb.append("\r\n"); + sb.append(body); + return sb.toString(); } static final String responses[] = { @@ -68,59 +79,151 @@ public class SplitResponse { "Excepteur sint occaecat cupidatat non proident." }; + final ServerSocketFactory factory; + final SSLContext context; + final boolean useSSL; + SplitResponse(boolean useSSL) throws IOException { + this.useSSL = useSSL; + context = new SimpleSSLContext().get(); + SSLContext.setDefault(context); + factory = useSSL ? SSLServerSocketFactory.getDefault() + : ServerSocketFactory.getDefault(); + } + + public HttpClient newHttpClient() { + HttpClient client; + if (useSSL) { + client = HttpClient.newBuilder() + .sslContext(context) + .build(); + } else { + client = HttpClient.newHttpClient(); + } + return client; + } + public static void main(String[] args) throws Exception { - server = new Server(0); + boolean useSSL = false; + if (args != null && args.length == 1) { + useSSL = "SSL".equals(args[0]); + } + SplitResponse sp = new SplitResponse(useSSL); + + for (Version version : Version.values()) { + for (boolean serverKeepalive : new boolean[]{ true, false }) { + // Note: the mock server doesn't support Keep-Alive, but + // pretending that it might exercises code paths in and out of + // the connection pool, and retry logic + for (boolean async : new boolean[]{ true, false }) { + sp.test(version, serverKeepalive, async); + } + } + } + } + + // @Test + void test(Version version, boolean serverKeepalive, boolean async) + throws Exception + { + out.println(format("*** version %s, serverKeepAlive: %s, async: %s ***", + version, serverKeepalive, async)); + MockServer server = new MockServer(0, factory); URI uri = new URI(server.getURL()); + out.println("server is: " + uri); server.start(); - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder(uri).build(); + + // The following code can be uncommented to verify that the + // MockServer will reject rogue requests whose URI does not + // contain "/foo/". + // + // Thread rogue = new Thread() { + // public void run() { + // try { + // HttpClient client = newHttpClient(); + // URI uri2 = URI.create(uri.toString().replace("/foo/","/")); + // HttpRequest request = HttpRequest + // .newBuilder(uri2).version(version).build(); + // while (true) { + // try { + // client.send(request, HttpResponse.BodyHandler.asString()); + // } catch (IOException ex) { + // System.out.println("Client rejected " + request); + // } + // sleep(250); + // } + // } catch ( Throwable x) { + // } + // } + // }; + // rogue.setDaemon(true); + // rogue.start(); + + + HttpClient client = newHttpClient(); + HttpRequest request = HttpRequest.newBuilder(uri).version(version).build(); HttpResponse<String> r; CompletableFuture<HttpResponse<String>> cf1; try { for (int i=0; i<responses.length; i++) { - cf1 = client.sendAsync(request, asString()); + out.println("----- iteration " + i + " -----"); String body = responses[i]; + Thread t = sendSplitResponse(response(body, serverKeepalive), server); - Server.Connection c = server.activity(); - sendSplitResponse(response(body), c); - r = cf1.get(); - if (r.statusCode()!= 200) + if (async) { + out.println("send async: " + request); + cf1 = client.sendAsync(request, asString()); + r = cf1.get(); + } else { // sync + out.println("send sync: " + request); + r = client.send(request, asString()); + } + + if (r.statusCode() != 200) throw new RuntimeException("Failed"); String rxbody = r.body(); - System.out.println("received " + rxbody); + out.println("received " + rxbody); if (!rxbody.equals(body)) - throw new RuntimeException("Failed"); - c.close(); + throw new RuntimeException(format("Expected:%s, got:%s", body, rxbody)); + + t.join(); + conn.close(); } } finally { - Executor def = client.executor(); - if (def instanceof ExecutorService) { - ((ExecutorService)def).shutdownNow(); - } + server.close(); } System.out.println("OK"); } - // send the response one byte at a time with a small delay between bytes - // to ensure that each byte is read in a separate read - static void sendSplitResponse(String s, Server.Connection conn) { + // required for cleanup + volatile MockServer.Connection conn; + + // Sends the response, mostly, one byte at a time with a small delay + // between bytes, to encourage that each byte is read in a separate read + Thread sendSplitResponse(String s, MockServer server) { System.out.println("Sending: "); Thread t = new Thread(() -> { + System.out.println("Waiting for server to receive headers"); + conn = server.activity(); + System.out.println("Start sending response"); + try { int len = s.length(); + out.println("sending " + s); for (int i = 0; i < len; i++) { String onechar = s.substring(i, i + 1); conn.send(onechar); - Thread.sleep(30); + Thread.sleep(10); } - System.out.println("sent"); + out.println("sent " + s); } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); } }); t.setDaemon(true); t.start(); + return t; } } diff --git a/test/jdk/java/net/httpclient/websocket/LoggingHelper.java b/test/jdk/java/net/httpclient/SplitResponseSSL.java similarity index 69% rename from test/jdk/java/net/httpclient/websocket/LoggingHelper.java rename to test/jdk/java/net/httpclient/SplitResponseSSL.java index 75d445c1a9d..3b0ebc151f8 100644 --- a/test/jdk/java/net/httpclient/websocket/LoggingHelper.java +++ b/test/jdk/java/net/httpclient/SplitResponseSSL.java @@ -21,18 +21,16 @@ * questions. */ -import java.io.File; - -public final class LoggingHelper { - - /* - * I wish we had a support for java.util.logging in jtreg similar to what we - * have for security policy files: - * - * @run main/othervm/jul=logging.properties ClassUnderTest - */ - public static void setupLogging() { - String path = System.getProperty("test.src", ".") + File.separator + "logging.properties"; - System.setProperty("java.util.logging.config.file", path); +/** + * @test + * @bug 8087112 + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @build MockServer SplitResponse + * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all SplitResponseSSL SSL + */ +public class SplitResponseSSL { + public static void main(String[] args) throws Exception { + SplitResponse.main(args); } } diff --git a/test/jdk/java/net/httpclient/TestKit.java b/test/jdk/java/net/httpclient/TestKit.java index 930e5e47395..c68d4bcdc36 100644 --- a/test/jdk/java/net/httpclient/TestKit.java +++ b/test/jdk/java/net/httpclient/TestKit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/TestKitTest.java b/test/jdk/java/net/httpclient/TestKitTest.java index 82293c4676b..2fd084cd3a3 100644 --- a/test/jdk/java/net/httpclient/TestKitTest.java +++ b/test/jdk/java/net/httpclient/TestKitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/TimeoutBasic.java b/test/jdk/java/net/httpclient/TimeoutBasic.java index b5dd58240ca..85e88b4a009 100644 --- a/test/jdk/java/net/httpclient/TimeoutBasic.java +++ b/test/jdk/java/net/httpclient/TimeoutBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -28,66 +28,157 @@ import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import jdk.incubator.http.HttpTimeoutException; +import jdk.testlibrary.SimpleSSLContext; + +import javax.net.ServerSocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocketFactory; import java.time.Duration; +import java.util.Arrays; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.CompletionException; +import java.util.function.Function; import static java.lang.System.out; import static jdk.incubator.http.HttpResponse.BodyHandler.discard; /** * @test + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext * @summary Basic tests for response timeouts * @run main/othervm TimeoutBasic - * @ignore */ public class TimeoutBasic { - static List<Duration> TIMEOUTS = List.of(/*Duration.ofSeconds(1), - Duration.ofMillis(100),*/ - Duration.ofNanos(99) - /* Duration.ofNanos(1)*/); + static List<Duration> TIMEOUTS = List.of(Duration.ofSeconds(1), + Duration.ofMillis(100), + Duration.ofNanos(99), + Duration.ofNanos(1)); + + static final List<Function<HttpRequest.Builder, HttpRequest.Builder>> METHODS = + Arrays.asList(HttpRequest.Builder::GET, + TimeoutBasic::DELETE, + TimeoutBasic::PUT, + TimeoutBasic::POST, + null); + + static final List<HttpClient.Version> VERSIONS = + Arrays.asList(HttpClient.Version.HTTP_2, HttpClient.Version.HTTP_1_1, null); + + static final List<String> SCHEMES = List.of("https", "http"); + + static { + try { + SSLContext.setDefault(new SimpleSSLContext().get()); + } catch (IOException x) { + throw new ExceptionInInitializerError(x); + } + } public static void main(String[] args) throws Exception { - HttpClient client = HttpClient.newHttpClient(); - try (ServerSocket ss = new ServerSocket(0, 20)) { + for (Function<HttpRequest.Builder, HttpRequest.Builder> m : METHODS) { + for (HttpClient.Version version : List.of(HttpClient.Version.HTTP_1_1)) { + for (HttpClient.Version reqVersion : VERSIONS) { + for (String scheme : SCHEMES) { + ServerSocketFactory ssf; + if (scheme.equalsIgnoreCase("https")) { + ssf = SSLServerSocketFactory.getDefault(); + } else { + ssf = ServerSocketFactory.getDefault(); + } + test(version, reqVersion, scheme, m, ssf); + } + } + } + } + } + + static HttpRequest.Builder DELETE(HttpRequest.Builder builder) { + HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody(); + return builder.DELETE(noBody); + } + + static HttpRequest.Builder PUT(HttpRequest.Builder builder) { + HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody(); + return builder.PUT(noBody); + } + + static HttpRequest.Builder POST(HttpRequest.Builder builder) { + HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody(); + return builder.POST(noBody); + } + + static HttpRequest newRequest(URI uri, + Duration duration, + HttpClient.Version reqVersion, + Function<HttpRequest.Builder, HttpRequest.Builder> method) { + HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri) + .timeout(duration); + if (method != null) reqBuilder = method.apply(reqBuilder); + if (reqVersion != null) reqBuilder = reqBuilder.version(reqVersion); + HttpRequest request = reqBuilder.build(); + if (duration.compareTo(Duration.ofSeconds(1)) >= 0) { + if (method == null || !request.method().equalsIgnoreCase("get")) { + out.println("Skipping " + duration + " for " + request.method()); + return null; + } + } + return request; + } + + public static void test(HttpClient.Version version, + HttpClient.Version reqVersion, + String scheme, + Function<HttpRequest.Builder, HttpRequest.Builder> method, + ServerSocketFactory ssf) + throws Exception + { + HttpClient.Builder builder = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY); + if (version != null) builder.version(version); + HttpClient client = builder.build(); + out.printf("%ntest(version=%s, reqVersion=%s, scheme=%s)%n", version, reqVersion, scheme); + try (ServerSocket ss = ssf.createServerSocket(0, 20)) { int port = ss.getLocalPort(); - URI uri = new URI("http://127.0.0.1:" + port + "/"); + URI uri = new URI(scheme +"://127.0.0.1:" + port + "/"); -// out.println("--- TESTING Async"); -// for (Duration duration : TIMEOUTS) { -// out.println(" with duration of " + duration); -// HttpRequest request = HttpRequest.newBuilder(uri) -// .timeout(duration) -// .GET().build(); -// try { -// HttpResponse<?> resp = client.sendAsync(request, discard(null)).join(); -// throw new RuntimeException("Unexpected response: " + resp.statusCode()); -// } catch (CompletionException e) { -// if (!(e.getCause() instanceof HttpTimeoutException)) { -// throw new RuntimeException("Unexpected exception: " + e.getCause()); -// } else { -// out.println("Caught expected timeout: " + e.getCause()); -// } -// } -// } - - out.println("--- TESTING Sync"); + out.println("--- TESTING Async"); + int count = 0; for (Duration duration : TIMEOUTS) { out.println(" with duration of " + duration); - HttpRequest request = HttpRequest.newBuilder(uri) - .timeout(duration) - .GET() - .build(); + HttpRequest request = newRequest(uri, duration, reqVersion, method); + if (request == null) continue; + count++; + try { + HttpResponse<?> resp = client.sendAsync(request, discard(null)).join(); + throw new RuntimeException("Unexpected response: " + resp.statusCode()); + } catch (CompletionException e) { + if (!(e.getCause() instanceof HttpTimeoutException)) { + throw new RuntimeException("Unexpected exception: " + e.getCause()); + } else { + out.println("Caught expected timeout: " + e.getCause()); + } + } + } + assert count >= TIMEOUTS.size() -1; + + out.println("--- TESTING Sync"); + count = 0; + for (Duration duration : TIMEOUTS) { + out.println(" with duration of " + duration); + HttpRequest request = newRequest(uri, duration, reqVersion, method); + if (request == null) continue; + count++; try { client.send(request, discard(null)); } catch (HttpTimeoutException e) { out.println("Caught expected timeout: " + e); } } - } finally { - ((ExecutorService) client.executor()).shutdownNow(); + assert count >= TIMEOUTS.size() -1; + } } } diff --git a/test/jdk/java/net/httpclient/TimeoutOrdering.java b/test/jdk/java/net/httpclient/TimeoutOrdering.java index 8ead371cfa5..7505e27ecd7 100644 --- a/test/jdk/java/net/httpclient/TimeoutOrdering.java +++ b/test/jdk/java/net/httpclient/TimeoutOrdering.java @@ -135,8 +135,6 @@ public class TimeoutOrdering { if (error) throw new RuntimeException("Failed. Check output"); - } finally { - ((ExecutorService) client.executor()).shutdownNow(); } } diff --git a/test/jdk/java/net/httpclient/VersionTest.java b/test/jdk/java/net/httpclient/VersionTest.java index 9b791b5782b..6433281e18a 100644 --- a/test/jdk/java/net/httpclient/VersionTest.java +++ b/test/jdk/java/net/httpclient/VersionTest.java @@ -42,8 +42,7 @@ import java.net.InetSocketAddress; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; -import static jdk.incubator.http.HttpResponse.*; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; import static jdk.incubator.http.HttpResponse.BodyHandler.discard; import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; diff --git a/test/jdk/java/net/httpclient/ZeroRedirects.java b/test/jdk/java/net/httpclient/ZeroRedirects.java new file mode 100644 index 00000000000..d8968ce742b --- /dev/null +++ b/test/jdk/java/net/httpclient/ZeroRedirects.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2017, 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 8164941 + * @modules jdk.incubator.httpclient java.logging jdk.httpserver + * @run main/othervm ZeroRedirects + */ + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.net.InetSocketAddress; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpResponse.BodyHandler.asString; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; +import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; +import static jdk.incubator.http.HttpClient.Version.HTTP_2; + +/** + */ +public class ZeroRedirects { + static HttpServer s1 ; + static ExecutorService executor; + static int port; + static HttpClient client; + static URI uri; + + public static void main(String[] args) throws Exception { + initServer(); + + client = HttpClient.newBuilder() + .executor(executor) + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + try { + test(); + } finally { + s1.stop(0); + executor.shutdownNow(); + } + } + + public static void test() throws Exception { + System.setProperty("java.net.httpclient.redirects.retrylimit", "0"); + HttpRequest r = HttpRequest.newBuilder(uri) + .GET() + .build(); + HttpResponse<Void> resp = client.send(r, discard(null)); + System.out.printf("Client: response is %d\n", resp.statusCode()); + if (resp.statusCode() != 200) + throw new RuntimeException(); + } + + static void initServer() throws Exception { + InetSocketAddress addr = new InetSocketAddress (0); + s1 = HttpServer.create (addr, 0); + HttpHandler h = new Handler(); + + HttpContext c1 = s1.createContext("/", h); + + executor = Executors.newCachedThreadPool(); + s1.setExecutor(executor); + s1.start(); + + port = s1.getAddress().getPort(); + uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/foo"); + System.out.println("HTTP server port = " + port); + } +} + +class Handler implements HttpHandler { + + @Override + public synchronized void handle(HttpExchange t) + throws IOException + { + String reply = "Hello world"; + int len = reply.length(); + System.out.printf("Sending response 200\n"); + t.sendResponseHeaders(200, len); + OutputStream o = t.getResponseBody(); + o.write(reply.getBytes()); + t.close(); + } +} diff --git a/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt b/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt index bb219e18873..584209bb192 100644 --- a/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt +++ b/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/examples/WebSocketExample.java b/test/jdk/java/net/httpclient/examples/WebSocketExample.java index 82ff0cbb462..2bb5a4f980b 100644 --- a/test/jdk/java/net/httpclient/examples/WebSocketExample.java +++ b/test/jdk/java/net/httpclient/examples/WebSocketExample.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -24,6 +24,8 @@ import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.URI; +import java.util.concurrent.CompletableFuture; + import jdk.incubator.http.HttpClient; import jdk.incubator.http.WebSocket; @@ -42,9 +44,8 @@ public class WebSocketExample { public void newBuilderExample0() { HttpClient client = HttpClient.newHttpClient(); - WebSocket.Builder builder = client.newWebSocketBuilder( - URI.create("ws://websocket.example.com"), - listener); + CompletableFuture<WebSocket> ws = client.newWebSocketBuilder() + .buildAsync(URI.create("ws://websocket.example.com"), listener); } public void newBuilderExample1() { @@ -52,8 +53,7 @@ public class WebSocketExample { HttpClient client = HttpClient.newBuilder() .proxy(ProxySelector.of(addr)) .build(); - WebSocket.Builder builder = client.newWebSocketBuilder( - URI.create("ws://websocket.example.com"), - listener); + CompletableFuture<WebSocket> ws = client.newWebSocketBuilder() + .buildAsync(URI.create("ws://websocket.example.com"), listener); } } diff --git a/test/jdk/java/net/httpclient/http2/BasicTest.java b/test/jdk/java/net/httpclient/http2/BasicTest.java index 9744eda9034..2494a6516ea 100644 --- a/test/jdk/java/net/httpclient/http2/BasicTest.java +++ b/test/jdk/java/net/httpclient/http2/BasicTest.java @@ -26,21 +26,27 @@ * @bug 8087112 * @library /lib/testlibrary server * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors BasicTest */ +import java.io.IOException; import java.net.*; import jdk.incubator.http.*; import static jdk.incubator.http.HttpClient.Version.HTTP_2; import javax.net.ssl.*; import java.nio.file.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import jdk.testlibrary.SimpleSSLContext; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asFile; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; @@ -51,25 +57,28 @@ public class BasicTest { static int httpPort, httpsPort; static Http2TestServer httpServer, httpsServer; static HttpClient client = null; - static ExecutorService exec; + static ExecutorService clientExec; + static ExecutorService serverExec; static SSLContext sslContext; - static String httpURIString, httpsURIString; + static String pingURIString, httpURIString, httpsURIString; static void initialize() throws Exception { try { SimpleSSLContext sslct = new SimpleSSLContext(); sslContext = sslct.get(); client = getClient(); - httpServer = new Http2TestServer(false, 0, exec, sslContext); + httpServer = new Http2TestServer(false, 0, serverExec, sslContext); httpServer.addHandler(new Http2EchoHandler(), "/"); + httpServer.addHandler(new EchoWithPingHandler(), "/ping"); httpPort = httpServer.getAddress().getPort(); - httpsServer = new Http2TestServer(true, 0, exec, sslContext); + httpsServer = new Http2TestServer(true, 0, serverExec, sslContext); httpsServer.addHandler(new Http2EchoHandler(), "/"); httpsPort = httpsServer.getAddress().getPort(); httpURIString = "http://127.0.0.1:" + httpPort + "/foo/"; + pingURIString = "http://127.0.0.1:" + httpPort + "/ping/"; httpsURIString = "https://127.0.0.1:" + httpsPort + "/bar/"; httpServer.start(); @@ -81,16 +90,48 @@ public class BasicTest { } } - @Test(timeOut=3000000) + static List<CompletableFuture<Long>> cfs = Collections + .synchronizedList( new LinkedList<>()); + + static CompletableFuture<Long> currentCF; + + static class EchoWithPingHandler extends Http2EchoHandler { + private final Object lock = new Object(); + + @Override + public void handle(Http2TestExchange exchange) throws IOException { + // for now only one ping active at a time. don't want to saturate + synchronized(lock) { + CompletableFuture<Long> cf = currentCF; + if (cf == null || cf.isDone()) { + cf = exchange.sendPing(); + assert cf != null; + cfs.add(cf); + currentCF = cf; + } + } + super.handle(exchange); + } + } + + @Test public static void test() throws Exception { try { initialize(); - simpleTest(false); - simpleTest(true); + warmup(false); + warmup(true); + simpleTest(false, false); + simpleTest(false, true); + simpleTest(true, false); streamTest(false); streamTest(true); paramsTest(); - Thread.sleep(1000 * 4); + CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0])).join(); + synchronized (cfs) { + for (CompletableFuture<Long> cf : cfs) { + System.out.printf("Ping ack received in %d millisec\n", cf.get()); + } + } } catch (Throwable tt) { System.err.println("tt caught"); tt.printStackTrace(); @@ -98,15 +139,16 @@ public class BasicTest { } finally { httpServer.stop(); httpsServer.stop(); - exec.shutdownNow(); + //clientExec.shutdown(); } } static HttpClient getClient() { if (client == null) { - exec = Executors.newCachedThreadPool(); + serverExec = Executors.newCachedThreadPool(); + clientExec = Executors.newCachedThreadPool(); client = HttpClient.newBuilder() - .executor(exec) + .executor(clientExec) .sslContext(sslContext) .version(HTTP_2) .build(); @@ -115,10 +157,14 @@ public class BasicTest { } static URI getURI(boolean secure) { + return getURI(secure, false); + } + + static URI getURI(boolean secure, boolean ping) { if (secure) return URI.create(httpsURIString); else - return URI.create(httpURIString); + return URI.create(ping ? pingURIString: httpURIString); } static void checkStatus(int expected, int found) throws Exception { @@ -170,12 +216,11 @@ public class BasicTest { }); response.join(); compareFiles(src, dest); - System.err.println("DONE"); + System.err.println("streamTest: DONE"); } static void paramsTest() throws Exception { - Http2TestServer server = new Http2TestServer(true, 0, exec, sslContext); - server.addHandler((t -> { + httpsServer.addHandler((t -> { SSLSession s = t.getSSLSession(); String prot = s.getProtocol(); if (prot.equals("TLSv1.2")) { @@ -185,9 +230,7 @@ public class BasicTest { t.sendResponseHeaders(500, -1); } }), "/"); - server.start(); - int port = server.getAddress().getPort(); - URI u = new URI("https://127.0.0.1:"+port+"/foo"); + URI u = new URI("https://127.0.0.1:"+httpsPort+"/foo"); HttpClient client = getClient(); HttpRequest req = HttpRequest.newBuilder(u).build(); HttpResponse<String> resp = client.send(req, asString()); @@ -196,9 +239,10 @@ public class BasicTest { throw new RuntimeException("paramsTest failed " + Integer.toString(stat)); } + System.err.println("paramsTest: DONE"); } - static void simpleTest(boolean secure) throws Exception { + static void warmup(boolean secure) throws Exception { URI uri = getURI(secure); System.err.println("Request to " + uri); @@ -209,15 +253,17 @@ public class BasicTest { .POST(fromString(SIMPLE_STRING)) .build(); HttpResponse<String> response = client.send(req, asString()); - HttpHeaders h = response.headers(); - checkStatus(200, response.statusCode()); - String responseBody = response.body(); + HttpHeaders h = response.headers(); checkStrings(SIMPLE_STRING, responseBody); - checkStrings(h.firstValue("x-hello").get(), "world"); checkStrings(h.firstValue("x-bye").get(), "universe"); + } + + static void simpleTest(boolean secure, boolean ping) throws Exception { + URI uri = getURI(secure, ping); + System.err.println("Request to " + uri); // Do loops asynchronously @@ -237,6 +283,6 @@ public class BasicTest { Thread.sleep(100); } CompletableFuture.allOf(responses).join(); - System.err.println("DONE"); + System.err.println("simpleTest: DONE"); } } diff --git a/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java b/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java new file mode 100644 index 00000000000..58b353ecfb0 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2017, 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 + * @summary Test for CONTINUATION frame handling + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @library /lib/testlibrary server + * @build Http2TestServer + * @build jdk.testlibrary.SimpleSSLContext + * @run testng/othervm ContinuationFrameTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.internal.common.HttpHeadersImpl; +import jdk.incubator.http.internal.frame.ContinuationFrame; +import jdk.incubator.http.internal.frame.HeaderFrame; +import jdk.incubator.http.internal.frame.HeadersFrame; +import jdk.incubator.http.internal.frame.Http2Frame; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static java.lang.System.out; +import static jdk.incubator.http.HttpClient.Version.HTTP_2; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; +import static jdk.incubator.http.HttpResponse.BodyHandler.asString; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class ContinuationFrameTest { + + SSLContext sslContext; + Http2TestServer http2TestServer; // HTTP/2 ( h2c ) + Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + String http2URI; + String https2URI; + + /** + * A function that returns a list of 1) a HEADERS frame ( with an empty + * payload ), and 2) a CONTINUATION frame with the actual headers. + */ + static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> oneContinuation = + (Integer streamid, List<ByteBuffer> encodedHeaders) -> { + List<ByteBuffer> empty = List.of(ByteBuffer.wrap(new byte[0])); + HeadersFrame hf = new HeadersFrame(streamid, 0, empty); + ContinuationFrame cf = new ContinuationFrame(streamid, + HeaderFrame.END_HEADERS, + encodedHeaders); + return List.of(hf, cf); + }; + + /** + * A function that returns a list of a HEADERS frame followed by a number of + * CONTINUATION frames. Each frame contains just a single byte of payload. + */ + static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> byteAtATime = + (Integer streamid, List<ByteBuffer> encodedHeaders) -> { + assert encodedHeaders.get(0).hasRemaining(); + List<Http2Frame> frames = new ArrayList<>(); + ByteBuffer hb = ByteBuffer.wrap(new byte[] {encodedHeaders.get(0).get()}); + HeadersFrame hf = new HeadersFrame(streamid, 0, hb); + frames.add(hf); + for (ByteBuffer bb : encodedHeaders) { + while (bb.hasRemaining()) { + List<ByteBuffer> data = List.of(ByteBuffer.wrap(new byte[] {bb.get()})); + ContinuationFrame cf = new ContinuationFrame(streamid, 0, data); + frames.add(cf); + } + } + frames.get(frames.size() - 1).setFlag(HeaderFrame.END_HEADERS); + return frames; + }; + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][] { + { http2URI, false, oneContinuation }, + { https2URI, false, oneContinuation }, + { http2URI, true, oneContinuation }, + { https2URI, true, oneContinuation }, + + { http2URI, false, byteAtATime }, + { https2URI, false, byteAtATime }, + { http2URI, true, byteAtATime }, + { https2URI, true, byteAtATime }, + }; + } + + static final int ITERATION_COUNT = 20; + + @Test(dataProvider = "variants") + void test(String uri, + boolean sameClient, + BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> headerFramesSupplier) + throws Exception + { + CFTHttp2TestExchange.setHeaderFrameSupplier(headerFramesSupplier); + + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = HttpClient.newBuilder().sslContext(sslContext).build(); + + HttpRequest request = HttpRequest.newBuilder(URI.create(uri)) + .POST(fromString("Hello there!")) + .build(); + HttpResponse<String> resp; + if (i % 2 == 0) { + resp = client.send(request, asString()); + } else { + resp = client.sendAsync(request, asString()).join(); + } + + out.println("Got response: " + resp); + out.println("Got body: " + resp.body()); + assertTrue(resp.statusCode() == 200, + "Expected 200, got:" + resp.statusCode()); + assertEquals(resp.body(), "Hello there!"); + assertEquals(resp.version(), HTTP_2); + } + } + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + http2TestServer = new Http2TestServer("127.0.0.1", false, 0); + http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo"); + int port = http2TestServer.getAddress().getPort(); + http2URI = "http://127.0.0.1:" + port + "/http2/echo"; + + https2TestServer = new Http2TestServer("127.0.0.1", true, 0); + https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo"); + port = https2TestServer.getAddress().getPort(); + https2URI = "https://127.0.0.1:" + port + "/https2/echo"; + + // Override the default exchange supplier with a custom one to enable + // particular test scenarios + http2TestServer.setExchangeSupplier(CFTHttp2TestExchange::new); + https2TestServer.setExchangeSupplier(CFTHttp2TestExchange::new); + + http2TestServer.start(); + https2TestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + http2TestServer.stop(); + https2TestServer.stop(); + } + + static class Http2EchoHandler implements Http2Handler { + @Override + public void handle(Http2TestExchange t) throws IOException { + try (InputStream is = t.getRequestBody(); + OutputStream os = t.getResponseBody()) { + byte[] bytes = is.readAllBytes(); + t.getResponseHeaders().addHeader("just some", "noise"); + t.getResponseHeaders().addHeader("to add ", "payload in "); + t.getResponseHeaders().addHeader("the header", "frames"); + t.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + } + + // A custom Http2TestExchangeImpl that overrides sendResponseHeaders to + // allow headers to be sent with a number of CONTINUATION frames. + static class CFTHttp2TestExchange extends Http2TestExchangeImpl { + static volatile BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> headerFrameSupplier; + + static void setHeaderFrameSupplier(BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> hfs) { + headerFrameSupplier = hfs; + } + + CFTHttp2TestExchange(int streamid, String method, HttpHeadersImpl reqheaders, + HttpHeadersImpl rspheaders, URI uri, InputStream is, + SSLSession sslSession, BodyOutputStream os, + Http2TestServerConnection conn, boolean pushAllowed) { + super(streamid, method, reqheaders, rspheaders, uri, is, sslSession, + os, conn, pushAllowed); + + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException { + this.responseLength = responseLength; + if (responseLength > 0 || responseLength < 0) { + long clen = responseLength > 0 ? responseLength : 0; + rspheaders.setHeader("Content-length", Long.toString(clen)); + } + rspheaders.setHeader(":status", Integer.toString(rCode)); + + List<ByteBuffer> encodeHeaders = conn.encodeHeaders(rspheaders); + List<Http2Frame> headerFrames = headerFrameSupplier.apply(streamid, encodeHeaders); + assert headerFrames.size() > 0; // there must always be at least 1 + + if (responseLength < 0) { + headerFrames.get(headerFrames.size() -1).setFlag(HeadersFrame.END_STREAM); + os.closeInternal(); + } + + for (Http2Frame f : headerFrames) + conn.outputQ.put(f); + + os.goodToGo(); + System.err.println("Sent response headers " + rCode); + } + } +} diff --git a/test/jdk/java/net/httpclient/http2/ErrorTest.java b/test/jdk/java/net/httpclient/http2/ErrorTest.java index dd7a79761dc..921118cf60a 100644 --- a/test/jdk/java/net/httpclient/http2/ErrorTest.java +++ b/test/jdk/java/net/httpclient/http2/ErrorTest.java @@ -26,7 +26,8 @@ * @bug 8157105 * @library /lib/testlibrary server * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * java.security.jgss @@ -45,7 +46,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import jdk.testlibrary.SimpleSSLContext; import static jdk.incubator.http.HttpClient.Version.HTTP_2; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.discard; import org.testng.annotations.Test; diff --git a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java index 36a5255c54b..3d72c581628 100644 --- a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java +++ b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java @@ -26,7 +26,8 @@ * @bug 8087112 8177935 * @library /lib/testlibrary server * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors FixedThreadPoolTest @@ -39,8 +40,8 @@ import javax.net.ssl.*; import java.nio.file.*; import java.util.concurrent.*; import jdk.testlibrary.SimpleSSLContext; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asFile; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; @@ -81,7 +82,7 @@ public class FixedThreadPoolTest { } } - @Test(timeOut=3000000) + @Test public static void test() throws Exception { try { initialize(); @@ -104,6 +105,13 @@ public class FixedThreadPoolTest { static HttpClient getClient() { if (client == null) { exec = Executors.newCachedThreadPool(); + // Executor e1 = Executors.newFixedThreadPool(1); + // Executor e = (Runnable r) -> e1.execute(() -> { + // System.out.println("[" + Thread.currentThread().getName() + // + "] Executing: " + // + r.getClass().getName()); + // r.run(); + // }); client = HttpClient.newBuilder() .executor(Executors.newFixedThreadPool(2)) .sslContext(sslContext) diff --git a/test/jdk/java/net/httpclient/http2/HpackDriver.java b/test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java similarity index 72% rename from test/jdk/java/net/httpclient/http2/HpackDriver.java rename to test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java index 11ecf83030e..7f04afe8478 100644 --- a/test/jdk/java/net/httpclient/http2/HpackDriver.java +++ b/test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -30,10 +30,5 @@ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.BinaryPrimitivesTest - * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.CircularBufferTest - * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.DecoderTest - * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.EncoderTest - * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HuffmanTest - * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.TestHelper */ -public class HpackDriver { } +public class HpackBinaryTestDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java b/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java new file mode 100644 index 00000000000..7a9f034d437 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, 2017, 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 8153353 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @key randomness + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java + * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.CircularBufferTest + */ +public class HpackCircularBufferDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java b/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java new file mode 100644 index 00000000000..c05c3113c52 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, 2017, 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 8153353 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @key randomness + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java + * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.DecoderTest + */ +public class HpackDecoderDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java b/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java new file mode 100644 index 00000000000..5d4f169814f --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, 2017, 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 8153353 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @key randomness + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java + * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.EncoderTest + */ +public class HpackEncoderDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java b/test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java similarity index 92% rename from test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java rename to test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java index 5865852ef87..6d056bde87b 100644 --- a/test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java +++ b/test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -32,4 +32,4 @@ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HeaderTableTest */ -public class HpackDriverHeaderTable { } +public class HpackHeaderTableDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java b/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java new file mode 100644 index 00000000000..681ccf3ed1e --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, 2017, 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 8153353 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @key randomness + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java + * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HuffmanTest + */ +public class HpackHuffmanDriver { } diff --git a/test/jdk/java/net/httpclient/http2/HpackTestHelper.java b/test/jdk/java/net/httpclient/http2/HpackTestHelper.java new file mode 100644 index 00000000000..bc231b75f5c --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HpackTestHelper.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016, 2017, 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 8153353 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @key randomness + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java + * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java + * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.TestHelper + */ +public class HpackTestHelperDriver { } diff --git a/test/jdk/java/net/httpclient/http2/NoBody.java b/test/jdk/java/net/httpclient/http2/NoBody.java deleted file mode 100644 index a9644b6c20c..00000000000 --- a/test/jdk/java/net/httpclient/http2/NoBody.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2015, 2016, 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 8161157 - * @library /lib/testlibrary server - * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common - * jdk.incubator.httpclient/jdk.incubator.http.internal.frame - * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack - * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,frames,errors NoBody - */ - -import java.io.IOException; -import java.net.URI; -import jdk.incubator.http.HttpClient; -import jdk.incubator.http.HttpRequest; -import jdk.incubator.http.HttpResponse; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import java.util.concurrent.Executors; -import java.util.concurrent.ExecutorService; -import jdk.testlibrary.SimpleSSLContext; -import static jdk.incubator.http.HttpClient.Version.HTTP_2; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; -import static jdk.incubator.http.HttpResponse.BodyHandler.asString; - -import org.testng.annotations.Test; - -public class NoBody { - - static final String SIMPLE_STRING = "Hello world. Goodbye world"; - - @Test(timeOut=60000) - public void test() throws Exception { - SSLContext sslContext = (new SimpleSSLContext()).get(); - ExecutorService exec = Executors.newCachedThreadPool(); - HttpClient client = HttpClient.newBuilder() - .executor(exec) - .sslContext(sslContext) - .version(HTTP_2) - .build(); - - Http2TestServer httpsServer = null; - try { - httpsServer = new Http2TestServer(true, - 0, - exec, - sslContext); - httpsServer.addHandler(new NoBodyHandler(), "/"); - - int httpsPort = httpsServer.getAddress().getPort(); - String httpsURIString = "https://127.0.0.1:" + httpsPort + "/bar/"; - - httpsServer.start(); - URI uri = URI.create(httpsURIString); - System.err.println("Request to " + uri); - - HttpRequest req = HttpRequest.newBuilder(uri) - .PUT(fromString(SIMPLE_STRING)) - .build(); - HttpResponse<String> response = client.send(req, asString()); - String body = response.body(); - if (!body.equals("")) - throw new RuntimeException("expected empty body"); - System.err.println("DONE"); - } finally { - if (httpsServer != null ) { httpsServer.stop(); } - exec.shutdownNow(); - } - } -} diff --git a/test/jdk/java/net/httpclient/http2/ProxyTest2.java b/test/jdk/java/net/httpclient/http2/ProxyTest2.java index 3a06bd304e1..b00e182afb9 100644 --- a/test/jdk/java/net/httpclient/http2/ProxyTest2.java +++ b/test/jdk/java/net/httpclient/http2/ProxyTest2.java @@ -62,7 +62,8 @@ import java.util.concurrent.*; * tunnelling through an HTTP/1.1 proxy. * @modules jdk.incubator.httpclient * @library /lib/testlibrary server - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * @build jdk.testlibrary.SimpleSSLContext ProxyTest2 diff --git a/test/jdk/java/net/httpclient/http2/RedirectTest.java b/test/jdk/java/net/httpclient/http2/RedirectTest.java index cf0278585f3..8a5130bd375 100644 --- a/test/jdk/java/net/httpclient/http2/RedirectTest.java +++ b/test/jdk/java/net/httpclient/http2/RedirectTest.java @@ -24,36 +24,34 @@ /* * @test * @bug 8156514 - * @key intermittent * @library /lib/testlibrary server * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.frame - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * @run testng/othervm -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors RedirectTest */ import java.net.*; import jdk.incubator.http.*; -import static jdk.incubator.http.HttpClient.Version.HTTP_2; -import java.nio.file.*; +import java.util.Optional; import java.util.concurrent.*; import java.util.function.*; import java.util.Arrays; import java.util.Iterator; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import org.testng.annotations.Test; +import static jdk.incubator.http.HttpClient.Version.HTTP_2; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; -import org.testng.annotations.Test; - -@Test public class RedirectTest { - static int httpPort, altPort; - static Http2TestServer httpServer, altServer; + static int httpPort; + static Http2TestServer httpServer; static HttpClient client; - static ExecutorService exec; static String httpURIString, altURIString1, altURIString2; + static URI httpURI, altURI1, altURI2; static Supplier<String> sup(String... args) { Iterator<String> i = Arrays.asList(args).iterator(); @@ -61,28 +59,56 @@ public class RedirectTest { return () -> i.next(); } + static class Redirector extends Http2RedirectHandler { + private InetSocketAddress remoteAddr; + private boolean error = false; + + Redirector(Supplier<String> supplier) { + super(supplier); + } + + protected synchronized void examineExchange(Http2TestExchange ex) { + InetSocketAddress addr = ex.getRemoteAddress(); + if (remoteAddr == null) { + remoteAddr = addr; + return; + } + // check that the client addr/port stays the same, proving + // that the connection didn't get dropped. + if (!remoteAddr.equals(addr)) { + System.err.printf("Error %s/%s\n", remoteAddr.toString(), + addr.toString()); + error = true; + } + } + + public synchronized boolean error() { + return error; + } + } + static void initialize() throws Exception { try { client = getClient(); - httpServer = new Http2TestServer(false, 0, exec, null); - + httpServer = new Http2TestServer(false, 0, null, null); httpPort = httpServer.getAddress().getPort(); - altServer = new Http2TestServer(false, 0, exec, null); - altPort = altServer.getAddress().getPort(); - // urls are accessed in sequence below - // first two on different servers. Third on same server - // as second. So, the client should use the same http connection + // urls are accessed in sequence below. The first two are on + // different servers. Third on same server as second. So, the + // client should use the same http connection. httpURIString = "http://127.0.0.1:" + httpPort + "/foo/"; - altURIString1 = "http://127.0.0.1:" + altPort + "/redir"; - altURIString2 = "http://127.0.0.1:" + altPort + "/redir/again"; + httpURI = URI.create(httpURIString); + altURIString1 = "http://127.0.0.1:" + httpPort + "/redir"; + altURI1 = URI.create(altURIString1); + altURIString2 = "http://127.0.0.1:" + httpPort + "/redir_again"; + altURI2 = URI.create(altURIString2); - httpServer.addHandler(new RedirectHandler(sup(altURIString1)), "/foo"); - altServer.addHandler(new RedirectHandler(sup(altURIString2)), "/redir"); - altServer.addHandler(new Http2EchoHandler(), "/redir/again"); + Redirector r = new Redirector(sup(altURIString1, altURIString2)); + httpServer.addHandler(r, "/foo"); + httpServer.addHandler(r, "/redir"); + httpServer.addHandler(new Http2EchoHandler(), "/redir_again"); httpServer.start(); - altServer.start(); } catch (Throwable e) { System.err.println("Throwing now"); e.printStackTrace(); @@ -90,26 +116,19 @@ public class RedirectTest { } } - @Test(timeOut=3000000) + @Test public static void test() throws Exception { try { initialize(); simpleTest(); - } catch (Throwable tt) { - System.err.println("tt caught"); - tt.printStackTrace(); } finally { httpServer.stop(); - altServer.stop(); - exec.shutdownNow(); } } static HttpClient getClient() { if (client == null) { - exec = Executors.newCachedThreadPool(); client = HttpClient.newBuilder() - .executor(exec) .followRedirects(HttpClient.Redirect.ALWAYS) .version(HTTP_2) .build(); @@ -129,6 +148,15 @@ public class RedirectTest { } } + static void checkURIs(URI expected, URI found) throws Exception { + System.out.printf ("Expected: %s, Found: %s\n", expected.toString(), found.toString()); + if (!expected.equals(found)) { + System.err.printf ("Test failed: wrong URI %s/%s\n", + expected.toString(), found.toString()); + throw new RuntimeException("Test failed"); + } + } + static void checkStrings(String expected, String found) throws Exception { if (!expected.equals(found)) { System.err.printf ("Test failed: wrong string %s/%s\n", @@ -137,18 +165,17 @@ public class RedirectTest { } } - static Void compareFiles(Path path1, Path path2) { - return TestUtil.compareFiles(path1, path2); - } - - static Path tempFile() { - return TestUtil.tempFile(); + static void check(boolean cond, Object... msg) { + if (cond) + return; + StringBuilder sb = new StringBuilder(); + for (Object o : msg) + sb.append(o); + throw new RuntimeException(sb.toString()); } static final String SIMPLE_STRING = "Hello world Goodbye world"; - static final int FILESIZE = 64 * 1024 + 200; - static void simpleTest() throws Exception { URI uri = getURI(); System.err.println("Request to " + uri); @@ -163,8 +190,44 @@ public class RedirectTest { checkStatus(200, response.statusCode()); String responseBody = response.body(); checkStrings(SIMPLE_STRING, responseBody); + checkURIs(response.uri(), altURI2); + + // check two previous responses + HttpResponse<String> prev = response.previousResponse() + .orElseThrow(() -> new RuntimeException("no previous response")); + checkURIs(prev.uri(), altURI1); + + prev = prev.previousResponse() + .orElseThrow(() -> new RuntimeException("no previous response")); + checkURIs(prev.uri(), httpURI); + + checkPreviousRedirectResponses(req, response); System.err.println("DONE"); - Thread.sleep (6000); + } + + static void checkPreviousRedirectResponses(HttpRequest initialRequest, + HttpResponse<?> finalResponse) { + // there must be at least one previous response + finalResponse.previousResponse() + .orElseThrow(() -> new RuntimeException("no previous response")); + + HttpResponse<?> response = finalResponse; + do { + URI uri = response.uri(); + response = response.previousResponse().get(); + check(300 <= response.statusCode() && response.statusCode() <= 309, + "Expected 300 <= code <= 309, got:" + response.statusCode()); + check(response.body() == null, "Unexpected body: " + response.body()); + String locationHeader = response.headers().firstValue("Location") + .orElseThrow(() -> new RuntimeException("no previous Location")); + check(uri.toString().endsWith(locationHeader), + "URI: " + uri + ", Location: " + locationHeader); + } while (response.previousResponse().isPresent()); + + // initial + check(initialRequest.equals(response.request()), + "Expected initial request [%s] to equal last prev req [%s]", + initialRequest, response.request()); } } diff --git a/test/jdk/java/net/httpclient/http2/ServerPush.java b/test/jdk/java/net/httpclient/http2/ServerPush.java index 85ef2e1736a..63079b12019 100644 --- a/test/jdk/java/net/httpclient/http2/ServerPush.java +++ b/test/jdk/java/net/httpclient/http2/ServerPush.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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,10 +26,11 @@ * @bug 8087112 8159814 * @library /lib/testlibrary server * @build jdk.testlibrary.SimpleSSLContext - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack - * @run testng/othervm -Djdk.httpclient.HttpClient.log=errors,requests,responses ServerPush + * @run testng/othervm -Djdk.internal.httpclient.hpack.debug=true -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors,requests,responses ServerPush */ import java.io.*; @@ -37,7 +38,7 @@ import java.net.*; import java.nio.file.*; import java.nio.file.attribute.*; import jdk.incubator.http.*; -import jdk.incubator.http.HttpResponse.MultiProcessor; +import jdk.incubator.http.HttpResponse.MultiSubscriber; import jdk.incubator.http.HttpResponse.BodyHandler; import java.util.*; import java.util.concurrent.*; @@ -52,7 +53,7 @@ public class ServerPush { static Path tempFile; - @Test(timeOut=30000) + @Test public static void test() throws Exception { Http2TestServer server = null; final Path dir = Files.createTempDirectory("serverPush"); @@ -72,7 +73,7 @@ public class ServerPush { CompletableFuture<MultiMapResult<Path>> cf = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2) .executor(e).build().sendAsync( - request, MultiProcessor.asMap((req) -> { + request, MultiSubscriber.asMap((req) -> { URI u = req.uri(); Path path = Paths.get(dir.toString(), u.getPath()); try { diff --git a/test/jdk/java/net/httpclient/http2/TLSConnection.java b/test/jdk/java/net/httpclient/http2/TLSConnection.java index 55bccc0b093..0e816d063b8 100644 --- a/test/jdk/java/net/httpclient/http2/TLSConnection.java +++ b/test/jdk/java/net/httpclient/http2/TLSConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -29,19 +29,19 @@ import java.net.URI; import java.net.URISyntaxException; import jdk.incubator.http.HttpClient; import jdk.incubator.http.HttpRequest; -import jdk.incubator.http.HttpResponse; + import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; /* * @test * @bug 8150769 8157107 - * @key intermittent * @library server * @summary Checks that SSL parameters can be set for HTTP/2 connection - * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @modules java.base/sun.net.www.http + * jdk.incubator.httpclient/jdk.incubator.http.internal.common * jdk.incubator.httpclient/jdk.incubator.http.internal.frame * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack * @run main/othervm TLSConnection diff --git a/test/jdk/java/net/httpclient/http2/Timeout.java b/test/jdk/java/net/httpclient/http2/Timeout.java index 04f0ca93c85..178e80f6363 100644 --- a/test/jdk/java/net/httpclient/http2/Timeout.java +++ b/test/jdk/java/net/httpclient/http2/Timeout.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -29,13 +29,12 @@ import jdk.incubator.http.HttpRequest; import jdk.incubator.http.HttpResponse; import jdk.incubator.http.HttpTimeoutException; import java.time.Duration; -import java.util.concurrent.TimeUnit; import java.util.concurrent.CompletionException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; -import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString; +import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; import static jdk.incubator.http.HttpResponse.BodyHandler.asString; /* diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java index 429685bcef0..ee8c99c0887 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -24,6 +24,8 @@ package jdk.incubator.http.internal.hpack; import org.testng.annotations.Test; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; @@ -80,7 +82,7 @@ public final class BinaryPrimitivesTest { // for all x: readInteger(writeInteger(x)) == x // @Test - public void integerIdentity() { + public void integerIdentity() throws IOException { final int MAX_VALUE = 1 << 22; int totalCases = 0; int maxFilling = 0; @@ -119,7 +121,11 @@ public final class BinaryPrimitivesTest { Iterable<? extends ByteBuffer> buf = relocateBuffers(injectEmptyBuffers(buffers)); r.configure(N); for (ByteBuffer b : buf) { - r.read(b); + try { + r.read(b); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } assertEquals(r.get(), expected); r.reset(); @@ -155,7 +161,11 @@ public final class BinaryPrimitivesTest { if (!written) { fail("please increase bb size"); } - r.configure(N).read(concat(buf)); + try { + r.configure(N).read(concat(buf)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } // TODO: check payload here assertEquals(r.get(), expected); w.reset(); @@ -172,7 +182,7 @@ public final class BinaryPrimitivesTest { // for all x: readString(writeString(x)) == x // @Test - public void stringIdentity() { + public void stringIdentity() throws IOException { final int MAX_STRING_LENGTH = 4096; ByteBuffer bytes = ByteBuffer.allocate(MAX_STRING_LENGTH + 6); // it takes 6 bytes to encode string length of Integer.MAX_VALUE CharBuffer chars = CharBuffer.allocate(MAX_STRING_LENGTH); @@ -241,7 +251,11 @@ public final class BinaryPrimitivesTest { if (!written) { fail("please increase 'bytes' size"); } - reader.read(concat(buffers), chars); + try { + reader.read(concat(buffers), chars); + } catch (IOException e) { + throw new UncheckedIOException(e); + } chars.flip(); assertEquals(chars.toString(), expected); reader.reset(); @@ -279,7 +293,11 @@ public final class BinaryPrimitivesTest { forEachSplit(bytes, (buffers) -> { for (ByteBuffer buf : buffers) { int p0 = buf.position(); - reader.read(buf, chars); + try { + reader.read(buf, chars); + } catch (IOException e) { + throw new UncheckedIOException(e); + } buf.position(p0); } chars.flip(); @@ -333,7 +351,11 @@ public final class BinaryPrimitivesTest { private static void verifyRead(byte[] data, int expected, int N) { ByteBuffer buf = ByteBuffer.wrap(data, 0, data.length); IntegerReader reader = new IntegerReader(); - reader.configure(N).read(buf); + try { + reader.configure(N).read(buf); + } catch (IOException e) { + throw new UncheckedIOException(e); + } assertEquals(expected, reader.get()); } diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java index 3187a329265..0b4165eb11a 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java index 0e2e6d31c9c..6cbfc0b7a3d 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java index 79645730759..155603f241a 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -24,8 +24,8 @@ package jdk.incubator.http.internal.hpack; import org.testng.annotations.Test; +import java.io.IOException; import java.io.UncheckedIOException; -import java.net.ProtocolException; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedList; @@ -400,7 +400,7 @@ public final class DecoderTest { // This test is missing in the spec // @Test - public void sizeUpdate() { + public void sizeUpdate() throws IOException { Decoder d = new Decoder(4096); assertEquals(d.getTable().maxSize(), 4096); d.decode(ByteBuffer.wrap(new byte[]{0b00111110}), true, nopCallback()); // newSize = 30 @@ -421,20 +421,14 @@ public final class DecoderTest { b.flip(); { Decoder d = new Decoder(4096); - UncheckedIOException ex = assertVoidThrows(UncheckedIOException.class, + assertVoidThrows(IOException.class, () -> d.decode(b, true, (name, value) -> { })); - - assertNotNull(ex.getCause()); - assertEquals(ex.getCause().getClass(), ProtocolException.class); } b.flip(); { Decoder d = new Decoder(4096); - UncheckedIOException ex = assertVoidThrows(UncheckedIOException.class, + assertVoidThrows(IOException.class, () -> d.decode(b, false, (name, value) -> { })); - - assertNotNull(ex.getCause()); - assertEquals(ex.getCause().getClass(), ProtocolException.class); } } @@ -445,10 +439,8 @@ public final class DecoderTest { (byte) 0b11111111, // indexed (byte) 0b10011010 // 25 + ... }); - UncheckedIOException e = assertVoidThrows(UncheckedIOException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); - assertNotNull(e.getCause()); - assertEquals(e.getCause().getClass(), ProtocolException.class); assertExceptionMessageContains(e, "Unexpected end of header block"); } @@ -471,10 +463,10 @@ public final class DecoderTest { (byte) 0b00000111 }); - IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); - assertExceptionMessageContains(e, "index=2147483647"); + assertExceptionMessageContains(e.getCause(), "index=2147483647"); } @Test @@ -490,7 +482,7 @@ public final class DecoderTest { (byte) 0b00000111 }); - IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); assertExceptionMessageContains(e, "Integer overflow"); @@ -507,10 +499,8 @@ public final class DecoderTest { 0b00000000, // but only 3 octets available... 0b00000000 // / }); - UncheckedIOException e = assertVoidThrows(UncheckedIOException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); - assertNotNull(e.getCause()); - assertEquals(e.getCause().getClass(), ProtocolException.class); assertExceptionMessageContains(e, "Unexpected end of header block"); } @@ -527,10 +517,8 @@ public final class DecoderTest { 0b00000000, // / 0b00000000 // / }); - UncheckedIOException e = assertVoidThrows(UncheckedIOException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); - assertNotNull(e.getCause()); - assertEquals(e.getCause().getClass(), ProtocolException.class); assertExceptionMessageContains(e, "Unexpected end of header block"); } @@ -547,7 +535,7 @@ public final class DecoderTest { 0b00011001, 0b01001101, (byte) 0b11111111, (byte) 0b11111111, (byte) 0b11111111, (byte) 0b11111100 }); - IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); assertExceptionMessageContains(e, "Encountered EOS"); @@ -566,7 +554,7 @@ public final class DecoderTest { 0b00011001, 0b01001101, (byte) 0b11111111 // len("aei") + len(padding) = (5 + 5 + 5) + (9) }); - IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); assertExceptionMessageContains(e, "Padding is too long", "len=9"); @@ -597,7 +585,7 @@ public final class DecoderTest { (byte) 0b10000011, // huffman=true, length=3 0b00011001, 0b01111010, (byte) 0b11111110 }); - IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, + IOException e = assertVoidThrows(IOException.class, () -> d.decode(data, true, nopCallback())); assertExceptionMessageContains(e, "Not a EOS prefix"); @@ -648,13 +636,17 @@ public final class DecoderTest { Decoder d = supplier.get(); do { ByteBuffer n = i.next(); - d.decode(n, !i.hasNext(), (name, value) -> { - if (value == null) { - actual.add(name.toString()); - } else { - actual.add(name + ": " + value); - } - }); + try { + d.decode(n, !i.hasNext(), (name, value) -> { + if (value == null) { + actual.add(name.toString()); + } else { + actual.add(name + ": " + value); + } + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } while (i.hasNext()); assertEquals(d.getTable().getStateString(), expectedHeaderTable); assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList); @@ -671,13 +663,17 @@ public final class DecoderTest { ByteBuffer source = SpecHelper.toBytes(hexdump); List<String> actual = new LinkedList<>(); - d.decode(source, true, (name, value) -> { - if (value == null) { - actual.add(name.toString()); - } else { - actual.add(name + ": " + value); - } - }); + try { + d.decode(source, true, (name, value) -> { + if (value == null) { + actual.add(name.toString()); + } else { + actual.add(name + ": " + value); + } + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } assertEquals(d.getTable().getStateString(), expectedHeaderTable); assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList); diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java index 50b07a63e52..fa2ce93e32f 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -24,6 +24,7 @@ package jdk.incubator.http.internal.hpack; import org.testng.annotations.Test; +import java.io.IOException; import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -520,7 +521,7 @@ public final class EncoderTest { } @Test - public void initialSizeUpdateDefaultEncoder() { + public void initialSizeUpdateDefaultEncoder() throws IOException { Function<Integer, Encoder> e = Encoder::new; testSizeUpdate(e, 1024, asList(), asList(0)); testSizeUpdate(e, 1024, asList(1024), asList(0)); @@ -531,7 +532,7 @@ public final class EncoderTest { } @Test - public void initialSizeUpdateCustomEncoder() { + public void initialSizeUpdateCustomEncoder() throws IOException { Function<Integer, Encoder> e = EncoderTest::newCustomEncoder; testSizeUpdate(e, 1024, asList(), asList(1024)); testSizeUpdate(e, 1024, asList(1024), asList(1024)); @@ -542,7 +543,7 @@ public final class EncoderTest { } @Test - public void seriesOfSizeUpdatesDefaultEncoder() { + public void seriesOfSizeUpdatesDefaultEncoder() throws IOException { Function<Integer, Encoder> e = c -> { Encoder encoder = new Encoder(c); drainInitialUpdate(encoder); @@ -563,7 +564,7 @@ public final class EncoderTest { // https://tools.ietf.org/html/rfc7541#section-4.2 // @Test - public void seriesOfSizeUpdatesCustomEncoder() { + public void seriesOfSizeUpdatesCustomEncoder() throws IOException { Function<Integer, Encoder> e = c -> { Encoder encoder = newCustomEncoder(c); drainInitialUpdate(encoder); @@ -638,7 +639,7 @@ public final class EncoderTest { private void testSizeUpdate(Function<Integer, Encoder> encoder, int initialSize, List<Integer> updates, - List<Integer> expected) { + List<Integer> expected) throws IOException { Encoder e = encoder.apply(initialSize); updates.forEach(e::setMaxCapacity); ByteBuffer b = ByteBuffer.allocate(64); diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java index 4d0e08fc20f..6018e5859c8 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -116,7 +116,7 @@ public class HeaderTableTest { @Test public void staticData() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); Map<Integer, HeaderField> staticHeaderFields = createStaticEntries(); Map<String, Integer> minimalIndexes = new HashMap<>(); @@ -159,7 +159,7 @@ public class HeaderTableTest { @Test public void constructorSetsMaxSize() { int size = rnd.nextInt(64); - HeaderTable t = new HeaderTable(size); + HeaderTable t = new HeaderTable(size, HPACK.getLogger()); assertEquals(t.size(), 0); assertEquals(t.maxSize(), size); } @@ -169,13 +169,13 @@ public class HeaderTableTest { int maxSize = -(rnd.nextInt(100) + 1); // [-100, -1] IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class, - () -> new HeaderTable(0).setMaxSize(maxSize)); + () -> new HeaderTable(0, HPACK.getLogger()).setMaxSize(maxSize)); assertExceptionMessageContains(e, "maxSize"); } @Test public void zeroMaximumSize() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); table.setMaxSize(0); assertEquals(table.maxSize(), 0); } @@ -183,41 +183,41 @@ public class HeaderTableTest { @Test public void negativeIndex() { int idx = -(rnd.nextInt(256) + 1); // [-256, -1] - IllegalArgumentException e = - assertVoidThrows(IllegalArgumentException.class, - () -> new HeaderTable(0).get(idx)); + IndexOutOfBoundsException e = + assertVoidThrows(IndexOutOfBoundsException.class, + () -> new HeaderTable(0, HPACK.getLogger()).get(idx)); assertExceptionMessageContains(e, "index"); } @Test public void zeroIndex() { - IllegalArgumentException e = - assertThrows(IllegalArgumentException.class, - () -> new HeaderTable(0).get(0)); + IndexOutOfBoundsException e = + assertThrows(IndexOutOfBoundsException.class, + () -> new HeaderTable(0, HPACK.getLogger()).get(0)); assertExceptionMessageContains(e, "index"); } @Test public void length() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); assertEquals(table.length(), STATIC_TABLE_LENGTH); } @Test public void indexOutsideStaticRange() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); int idx = table.length() + (rnd.nextInt(256) + 1); - IllegalArgumentException e = - assertThrows(IllegalArgumentException.class, + IndexOutOfBoundsException e = + assertThrows(IndexOutOfBoundsException.class, () -> table.get(idx)); assertExceptionMessageContains(e, "index"); } @Test public void entryPutAfterStaticArea() { - HeaderTable table = new HeaderTable(256); + HeaderTable table = new HeaderTable(256, HPACK.getLogger()); int idx = table.length() + 1; - assertThrows(IllegalArgumentException.class, () -> table.get(idx)); + assertThrows(IndexOutOfBoundsException.class, () -> table.get(idx)); byte[] bytes = new byte[32]; rnd.nextBytes(bytes); @@ -232,13 +232,13 @@ public class HeaderTableTest { @Test public void staticTableHasZeroSize() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); assertEquals(0, table.size()); } @Test public void lowerIndexPriority() { - HeaderTable table = new HeaderTable(256); + HeaderTable table = new HeaderTable(256, HPACK.getLogger()); int oldLength = table.length(); table.put("bender", "rodriguez"); table.put("bender", "rodriguez"); @@ -251,7 +251,7 @@ public class HeaderTableTest { @Test public void lowerIndexPriority2() { - HeaderTable table = new HeaderTable(256); + HeaderTable table = new HeaderTable(256, HPACK.getLogger()); int oldLength = table.length(); int idx = rnd.nextInt(oldLength) + 1; HeaderField f = table.get(idx); @@ -267,9 +267,11 @@ public class HeaderTableTest { @Test public void fifo() { - HeaderTable t = new HeaderTable(Integer.MAX_VALUE); // Let's add a series of header fields int NUM_HEADERS = 32; + HeaderTable t = new HeaderTable((32 + 4) * NUM_HEADERS, HPACK.getLogger()); + // ^ ^ + // entry overhead symbols per entry (max 2x2 digits) for (int i = 1; i <= NUM_HEADERS; i++) { String s = String.valueOf(i); t.put(s, s); @@ -293,9 +295,11 @@ public class HeaderTableTest { @Test public void indexOf() { - HeaderTable t = new HeaderTable(Integer.MAX_VALUE); // Let's put a series of header fields int NUM_HEADERS = 32; + HeaderTable t = new HeaderTable((32 + 4) * NUM_HEADERS, HPACK.getLogger()); + // ^ ^ + // entry overhead symbols per entry (max 2x2 digits) for (int i = 1; i <= NUM_HEADERS; i++) { String s = String.valueOf(i); t.put(s, s); @@ -326,18 +330,25 @@ public class HeaderTableTest { @Test public void testToStringDifferentLocale() { + Locale locale = Locale.getDefault(); Locale.setDefault(Locale.FRENCH); - String s = format("%.1f", 3.1); - assertEquals("3,1", s); // assumption of the test, otherwise the test is useless - testToString0(); + try { + String s = format("%.1f", 3.1); + assertEquals("3,1", s); // assumption of the test, otherwise the test is useless + testToString0(); + } finally { + Locale.setDefault(locale); + } } private void testToString0() { - HeaderTable table = new HeaderTable(0); + HeaderTable table = new HeaderTable(0, HPACK.getLogger()); { - table.setMaxSize(2048); - String expected = - format("entries: %d; used %s/%s (%.1f%%)", 0, 0, 2048, 0.0); + int maxSize = 2048; + table.setMaxSize(maxSize); + String expected = format( + "dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)", + 0, STATIC_TABLE_LENGTH, 0, maxSize, 0.0); assertEquals(expected, table.toString()); } @@ -353,8 +364,9 @@ public class HeaderTableTest { int used = name.length() + value.length() + 32; double ratio = used * 100.0 / size; - String expected = - format("entries: 1; used %s/%s (%.1f%%)", used, size, ratio); + String expected = format( + "dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)", + 1, STATIC_TABLE_LENGTH + 1, used, size, ratio); assertEquals(expected, s); } @@ -364,14 +376,15 @@ public class HeaderTableTest { table.put(":status", ""); String s = table.toString(); String expected = - format("entries: %d; used %s/%s (%.1f%%)", 2, 78, 78, 100.0); + format("dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)", + 2, STATIC_TABLE_LENGTH + 2, 78, 78, 100.0); assertEquals(expected, s); } } @Test public void stateString() { - HeaderTable table = new HeaderTable(256); + HeaderTable table = new HeaderTable(256, HPACK.getLogger()); table.put("custom-key", "custom-header"); // @formatter:off assertEquals("[ 1] (s = 55) custom-key: custom-header\n" + diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java index adb24a7ffec..d8b8b9397b9 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -24,6 +24,8 @@ package jdk.incubator.http.internal.hpack; import org.testng.annotations.Test; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.util.Stack; import java.util.regex.Matcher; @@ -302,7 +304,7 @@ public final class HuffmanTest { // @formatter:on @Test - public void read_table() { + public void read_table() throws IOException { Pattern line = Pattern.compile( "\\(\\s*(?<ascii>\\d+)\\s*\\)\\s*(?<binary>(\\|(0|1)+)+)\\s*" + "(?<hex>[0-9a-zA-Z]+)\\s*\\[\\s*(?<len>\\d+)\\s*\\]"); @@ -555,7 +557,11 @@ public final class HuffmanTest { private static void read(String hexdump, String decoded) { ByteBuffer source = SpecHelper.toBytes(hexdump); Appendable actual = new StringBuilder(); - new Huffman.Reader().read(source, actual, true); + try { + new Huffman.Reader().read(source, actual, true); + } catch (IOException e) { + throw new UncheckedIOException(e); + } assertEquals(actual.toString(), decoded); } diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java index 6bf441bd5a4..e0c01d18405 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java index 86cc17016a6..b8ebd5644bf 100644 --- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java +++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java b/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java index 47cf2fa872c..dbfefe58e78 100644 --- a/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java +++ b/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -23,9 +23,8 @@ import java.io.*; import java.nio.ByteBuffer; +import java.util.List; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Queue; import jdk.incubator.http.internal.common.Utils; import jdk.incubator.http.internal.frame.DataFrame; import jdk.incubator.http.internal.frame.Http2Frame; @@ -63,9 +62,6 @@ class BodyInputStream extends InputStream { Http2Frame frame; do { frame = q.take(); - if (frame.type() == ResetFrame.TYPE) { - conn.handleStreamReset((ResetFrame) frame); // throws IOException - } // ignoring others for now Wupdates handled elsewhere if (frame.type() != DataFrame.TYPE) { System.out.println("Ignoring " + frame.toString() + " CHECK THIS"); @@ -87,13 +83,13 @@ class BodyInputStream extends InputStream { if (df == null) { return null; } - ByteBufferReference[] data = df.getData(); - int len = Utils.remaining(data); + List<ByteBuffer> data = df.getData(); + long len = Utils.remaining(data); if ((len == 0) && eof) { return null; } - buffers = ByteBufferReference.toBuffers(data); + buffers = data.toArray(Utils.EMPTY_BB_ARRAY); nextIndex = 0; } buffer = buffers[nextIndex++]; diff --git a/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java b/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java index 759005aa34f..95a18007461 100644 --- a/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java +++ b/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -24,8 +24,6 @@ import java.io.*; import java.nio.ByteBuffer; -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Queue; import jdk.incubator.http.internal.frame.DataFrame; /** @@ -38,7 +36,7 @@ class BodyOutputStream extends OutputStream { final int streamid; int window; - boolean closed; + volatile boolean closed; boolean goodToGo = false; // not allowed to send until headers sent final Http2TestServerConnection conn; final Queue outputQ; @@ -100,7 +98,7 @@ class BodyOutputStream extends OutputStream { buffer.put(buf, offset, len); buffer.flip(); assert streamid != 0; - DataFrame df = new DataFrame(streamid, flags, ByteBufferReference.of(buffer)); + DataFrame df = new DataFrame(streamid, flags, buffer); outputQ.put(df); } @@ -118,10 +116,11 @@ class BodyOutputStream extends OutputStream { @Override public void close() { - if (closed) { - return; + if (closed) return; + synchronized (this) { + if (closed) return; + closed = true; } - closed = true; try { send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM); } catch (IOException ex) { diff --git a/test/jdk/java/net/httpclient/http2/server/EchoHandler.java b/test/jdk/java/net/httpclient/http2/server/EchoHandler.java index 77a2a19494d..c36410317ee 100644 --- a/test/jdk/java/net/httpclient/http2/server/EchoHandler.java +++ b/test/jdk/java/net/httpclient/http2/server/EchoHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java b/test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java similarity index 86% rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java rename to test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java index d0d5689bf8b..b27117602f2 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java +++ b/test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java @@ -1,12 +1,10 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * 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 @@ -23,8 +21,6 @@ * questions. */ -package jdk.incubator.http.internal.common; - import java.io.Closeable; import java.io.IOException; diff --git a/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java b/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java index c1ebebe0d2f..b6872e54227 100644 --- a/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java @@ -31,7 +31,7 @@ public class Http2EchoHandler implements Http2Handler { public void handle(Http2TestExchange t) throws IOException { try { - System.err.println("EchoHandler received request to " + t.getRequestURI()); + System.err.printf("EchoHandler received request to %s from %s\n", t.getRequestURI(), t.getRemoteAddress()); InputStream is = t.getRequestBody(); HttpHeadersImpl map = t.getRequestHeaders(); HttpHeadersImpl map1 = t.getResponseHeaders(); diff --git a/test/jdk/java/net/httpclient/http2/server/Http2Handler.java b/test/jdk/java/net/httpclient/http2/server/Http2Handler.java index a6402d3f850..1addb8bffdd 100644 --- a/test/jdk/java/net/httpclient/http2/server/Http2Handler.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2Handler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/server/RedirectHandler.java b/test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java similarity index 61% rename from test/jdk/java/net/httpclient/http2/server/RedirectHandler.java rename to test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java index 5c68d053a8b..5b7fcb79748 100644 --- a/test/jdk/java/net/httpclient/http2/server/RedirectHandler.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -21,46 +21,42 @@ * questions. */ -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.function.Supplier; import jdk.incubator.http.internal.common.HttpHeadersImpl; -import static java.nio.charset.StandardCharsets.ISO_8859_1; -public class RedirectHandler implements Http2Handler { +public class Http2RedirectHandler implements Http2Handler { final Supplier<String> supplier; - public RedirectHandler(Supplier<String> redirectSupplier) { + public Http2RedirectHandler(Supplier<String> redirectSupplier) { supplier = redirectSupplier; } - static String consume(InputStream is) throws IOException { - byte[] b = new byte[1024]; - int i; - StringBuilder sb = new StringBuilder(); - - while ((i=is.read(b)) != -1) { - sb.append(new String(b, 0, i, ISO_8859_1)); - } - is.close(); - return sb.toString(); - } - @Override public void handle(Http2TestExchange t) throws IOException { - try { - consume(t.getRequestBody()); + examineExchange(t); + try (InputStream is = t.getRequestBody()) { + is.readAllBytes(); String location = supplier.get(); - System.err.println("RedirectHandler received request to " + t.getRequestURI()); + System.err.printf("RedirectHandler request to %s from %s\n", + t.getRequestURI().toString(), t.getRemoteAddress().toString()); System.err.println("Redirecting to: " + location); HttpHeadersImpl map1 = t.getResponseHeaders(); map1.addHeader("Location", location); - t.sendResponseHeaders(301, 0); - // return the number of bytes received (no echo) + t.sendResponseHeaders(301, 1024); + byte[] bb = new byte[1024]; + OutputStream os = t.getResponseBody(); + os.write(bb); + os.close(); t.close(); - } catch (Throwable e) { - e.printStackTrace(); - throw new IOException(e); } } + + // override in sub-class to examine the exchange, but don't + // alter transaction state by reading the request body etc. + protected void examineExchange(Http2TestExchange t) { + } } diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java index 325aaa23247..6e55021af9e 100644 --- a/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -21,148 +21,52 @@ * questions. */ +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; import java.net.URI; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import javax.net.ssl.SSLSession; import jdk.incubator.http.internal.common.HttpHeadersImpl; -import jdk.incubator.http.internal.frame.HeaderFrame; -import jdk.incubator.http.internal.frame.HeadersFrame; -public class Http2TestExchange { +public interface Http2TestExchange { - final HttpHeadersImpl reqheaders; - final HttpHeadersImpl rspheaders; - final URI uri; - final String method; - final InputStream is; - final BodyOutputStream os; - final SSLSession sslSession; - final int streamid; - final boolean pushAllowed; - final Http2TestServerConnection conn; - final Http2TestServer server; + HttpHeadersImpl getRequestHeaders(); - int responseCode = -1; - long responseLength; + HttpHeadersImpl getResponseHeaders(); - Http2TestExchange(int streamid, - String method, - HttpHeadersImpl reqheaders, - HttpHeadersImpl rspheaders, - URI uri, - InputStream is, - SSLSession sslSession, - BodyOutputStream os, - Http2TestServerConnection conn, - boolean pushAllowed) { - this.reqheaders = reqheaders; - this.rspheaders = rspheaders; - this.uri = uri; - this.method = method; - this.is = is; - this.streamid = streamid; - this.os = os; - this.sslSession = sslSession; - this.pushAllowed = pushAllowed; - this.conn = conn; - this.server = conn.server; - } + URI getRequestURI(); - public HttpHeadersImpl getRequestHeaders() { - return reqheaders; - } + String getRequestMethod(); - public HttpHeadersImpl getResponseHeaders() { - return rspheaders; - } + SSLSession getSSLSession(); - public URI getRequestURI() { - return uri; - } + void close(); - public String getRequestMethod() { - return method; - } + InputStream getRequestBody(); - public SSLSession getSSLSession() { - return sslSession; - } + OutputStream getResponseBody(); - public void close() { - try { - is.close(); - os.close(); - } catch (IOException e) { - System.err.println("TestServer: HttpExchange.close exception: " + e); - e.printStackTrace(); - } - } + void sendResponseHeaders(int rCode, long responseLength) throws IOException; - public InputStream getRequestBody() { - return is; - } + InetSocketAddress getRemoteAddress(); - public OutputStream getResponseBody() { - return os; - } + int getResponseCode(); - public void sendResponseHeaders(int rCode, long responseLength) throws IOException { - this.responseLength = responseLength; - if (responseLength > 0 || responseLength < 0) { - long clen = responseLength > 0 ? responseLength : 0; - rspheaders.setHeader("Content-length", Long.toString(clen)); - } + InetSocketAddress getLocalAddress(); - rspheaders.setHeader(":status", Integer.toString(rCode)); + String getProtocol(); - Http2TestServerConnection.ResponseHeaders response - = new Http2TestServerConnection.ResponseHeaders(rspheaders); - response.streamid(streamid); - response.setFlag(HeaderFrame.END_HEADERS); - if (responseLength < 0) { - response.setFlag(HeadersFrame.END_STREAM); - os.closeInternal(); - } - conn.outputQ.put(response); - os.goodToGo(); - System.err.println("Sent response headers " + rCode); - } + boolean serverPushAllowed(); - public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); - } + void serverPush(URI uri, HttpHeadersImpl headers, InputStream content); - public int getResponseCode() { - return responseCode; - } - - public InetSocketAddress getLocalAddress() { - return server.getAddress(); - } - - public String getProtocol() { - return "HTTP/2"; - } - - public boolean serverPushAllowed() { - return pushAllowed; - } - - public void serverPush(URI uri, HttpHeadersImpl headers, InputStream content) { - OutgoingPushPromise pp = new OutgoingPushPromise( - streamid, uri, headers, content); - headers.setHeader(":method", "GET"); - headers.setHeader(":scheme", uri.getScheme()); - headers.setHeader(":authority", uri.getAuthority()); - headers.setHeader(":path", uri.getPath()); - try { - conn.outputQ.put(pp); - // writeLoop will spin up thread to read the InputStream - } catch (IOException ex) { - System.err.println("TestServer: pushPromise exception: " + ex); - } - } + /** + * Send a PING on this exchanges connection, and completes the returned CF + * with the number of milliseconds it took to get a valid response. + * It may also complete exceptionally + */ + CompletableFuture<Long> sendPing(); } diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java new file mode 100644 index 00000000000..4d0b83f2763 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2016, 2017, 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. + */ + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import javax.net.ssl.SSLSession; +import jdk.incubator.http.internal.common.HttpHeadersImpl; +import jdk.incubator.http.internal.frame.HeaderFrame; +import jdk.incubator.http.internal.frame.HeadersFrame; + +public class Http2TestExchangeImpl implements Http2TestExchange { + + final HttpHeadersImpl reqheaders; + final HttpHeadersImpl rspheaders; + final URI uri; + final String method; + final InputStream is; + final BodyOutputStream os; + final SSLSession sslSession; + final int streamid; + final boolean pushAllowed; + final Http2TestServerConnection conn; + final Http2TestServer server; + + int responseCode = -1; + long responseLength; + + Http2TestExchangeImpl(int streamid, + String method, + HttpHeadersImpl reqheaders, + HttpHeadersImpl rspheaders, + URI uri, + InputStream is, + SSLSession sslSession, + BodyOutputStream os, + Http2TestServerConnection conn, + boolean pushAllowed) { + this.reqheaders = reqheaders; + this.rspheaders = rspheaders; + this.uri = uri; + this.method = method; + this.is = is; + this.streamid = streamid; + this.os = os; + this.sslSession = sslSession; + this.pushAllowed = pushAllowed; + this.conn = conn; + this.server = conn.server; + } + + @Override + public HttpHeadersImpl getRequestHeaders() { + return reqheaders; + } + + @Override + public CompletableFuture<Long> sendPing() { + return conn.sendPing(); + } + + @Override + public HttpHeadersImpl getResponseHeaders() { + return rspheaders; + } + + @Override + public URI getRequestURI() { + return uri; + } + + @Override + public String getRequestMethod() { + return method; + } + + @Override + public SSLSession getSSLSession() { + return sslSession; + } + + @Override + public void close() { + try { + is.close(); + os.close(); + } catch (IOException e) { + System.err.println("TestServer: HttpExchange.close exception: " + e); + e.printStackTrace(); + } + } + + @Override + public InputStream getRequestBody() { + return is; + } + + @Override + public OutputStream getResponseBody() { + return os; + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException { + this.responseLength = responseLength; + if (responseLength > 0 || responseLength < 0) { + long clen = responseLength > 0 ? responseLength : 0; + rspheaders.setHeader("Content-length", Long.toString(clen)); + } + + rspheaders.setHeader(":status", Integer.toString(rCode)); + + Http2TestServerConnection.ResponseHeaders response + = new Http2TestServerConnection.ResponseHeaders(rspheaders); + response.streamid(streamid); + response.setFlag(HeaderFrame.END_HEADERS); + + + if (responseLength < 0) { + response.setFlag(HeadersFrame.END_STREAM); + os.closeInternal(); + } + conn.outputQ.put(response); + os.goodToGo(); + System.err.println("Sent response headers " + rCode); + } + + @Override + public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) conn.socket.getRemoteSocketAddress(); + } + + @Override + public int getResponseCode() { + return responseCode; + } + + @Override + public InetSocketAddress getLocalAddress() { + return server.getAddress(); + } + + @Override + public String getProtocol() { + return "HTTP/2"; + } + + @Override + public boolean serverPushAllowed() { + return pushAllowed; + } + + @Override + public void serverPush(URI uri, HttpHeadersImpl headers, InputStream content) { + OutgoingPushPromise pp = new OutgoingPushPromise( + streamid, uri, headers, content); + headers.setHeader(":method", "GET"); + headers.setHeader(":scheme", uri.getScheme()); + headers.setHeader(":authority", uri.getAuthority()); + headers.setHeader(":path", uri.getPath()); + try { + conn.outputQ.put(pp); + // writeLoop will spin up thread to read the InputStream + } catch (IOException ex) { + System.err.println("TestServer: pushPromise exception: " + ex); + } + } +} diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java new file mode 100644 index 00000000000..d0c3ea127f0 --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 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. + */ + +import javax.net.ssl.SSLSession; +import java.io.InputStream; +import java.net.URI; +import jdk.incubator.http.internal.common.HttpHeadersImpl; + +/** + * A supplier of Http2TestExchanges. If the default Http2TestExchange impl is + * not sufficient, then a supplier may be set on an Http2TestServer through its + * {@link Http2TestServer#setExchangeSupplier(Http2TestExchangeSupplier)}. + * + * Useful for testing scenarios where non-standard or specific server behaviour + * is required, either direct control over the frames sent, "bad" behaviour, or + * something else. + */ +public interface Http2TestExchangeSupplier { + + Http2TestExchange get(int streamid, + String method, + HttpHeadersImpl reqheaders, + HttpHeadersImpl rspheaders, + URI uri, + InputStream is, + SSLSession sslSession, + BodyOutputStream os, + Http2TestServerConnection conn, + boolean pushAllowed); + + static Http2TestExchangeSupplier ofDefault() { + return Http2TestExchangeImpl::new; + } +} diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java b/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java index 6889cf7aec8..721cb960d99 100644 --- a/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -28,12 +28,14 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SNIServerName; +import jdk.incubator.http.internal.frame.ErrorFrame; /** * Waits for incoming TCP connections from a client and establishes @@ -131,6 +133,18 @@ public class Http2TestServer implements AutoCloseable { handlers.put(path, handler); } + volatile Http2TestExchangeSupplier exchangeSupplier = Http2TestExchangeSupplier.ofDefault(); + + /** + * Sets an explicit exchange handler to be used for all future connections. + * Useful for testing scenarios where non-standard or specific server + * behaviour is required, either direct control over the frames sent, "bad" + * behaviour, or something else. + */ + public void setExchangeSupplier(Http2TestExchangeSupplier exchangeSupplier) { + this.exchangeSupplier = exchangeSupplier; + } + Http2Handler getHandlerFor(String path) { if (path == null || path.equals("")) path = "/"; @@ -159,8 +173,9 @@ public class Http2TestServer implements AutoCloseable { public void stop() { // TODO: clean shutdown GoAway stopping = true; + System.err.printf("Server stopping %d connections\n", connections.size()); for (Http2TestServerConnection connection : connections.values()) { - connection.close(); + connection.close(ErrorFrame.NO_ERROR); } try { server.close(); @@ -199,23 +214,25 @@ public class Http2TestServer implements AutoCloseable { while (!stopping) { Socket socket = server.accept(); InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress(); - Http2TestServerConnection c = new Http2TestServerConnection(this, socket); + Http2TestServerConnection c = + new Http2TestServerConnection(this, socket, exchangeSupplier); connections.put(addr, c); try { c.run(); - } catch(Throwable e) { + } catch (Throwable e) { // we should not reach here, but if we do // the connection might not have been closed // and if so then the client might wait // forever. connections.remove(addr, c); - c.close(); - throw e; + c.close(ErrorFrame.PROTOCOL_ERROR); + System.err.println("TestServer: start exception: " + e); + //throw e; } } } catch (Throwable e) { if (!stopping) { - System.err.println("TestServer: start exception: " + e); + System.err.println("TestServer: terminating, caught " + e); e.printStackTrace(); } } diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java index 5645c200692..afb3066f76e 100644 --- a/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java +++ b/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -36,19 +36,17 @@ import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; - -import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.frame.FramesDecoder; - -import jdk.incubator.http.internal.common.BufferHandler; import jdk.incubator.http.internal.common.HttpHeadersImpl; -import jdk.incubator.http.internal.common.Queue; import jdk.incubator.http.internal.frame.*; import jdk.incubator.http.internal.hpack.Decoder; import jdk.incubator.http.internal.hpack.DecodingCallback; import jdk.incubator.http.internal.hpack.Encoder; +import sun.net.www.http.ChunkedInputStream; +import sun.net.www.http.HttpClient; import static jdk.incubator.http.internal.frame.SettingsFrame.HEADER_TABLE_SIZE; /** @@ -59,10 +57,12 @@ public class Http2TestServerConnection { final Http2TestServer server; @SuppressWarnings({"rawtypes","unchecked"}) final Map<Integer, Queue> streams; // input q per stream + final Map<Integer, BodyOutputStream> outStreams; // output q per stream final HashSet<Integer> pushStreams; final Queue<Http2Frame> outputQ; volatile int nextstream; final Socket socket; + final Http2TestExchangeSupplier exchangeSupplier; final InputStream is; final OutputStream os; volatile Encoder hpackOut; @@ -73,21 +73,66 @@ public class Http2TestServerConnection { final boolean secure; volatile boolean stopping; volatile int nextPushStreamId = 2; + ConcurrentLinkedQueue<PingRequest> pings = new ConcurrentLinkedQueue<>(); final static ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); final static byte[] EMPTY_BARRAY = new byte[0]; + final Random random; final static byte[] clientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(); - Http2TestServerConnection(Http2TestServer server, Socket socket) throws IOException { + static class Sentinel extends Http2Frame { + Sentinel() { super(-1,-1);} + } + + static final Sentinel sentinel = new Sentinel(); + + class PingRequest { + final byte[] pingData; + final long pingStamp; + final CompletableFuture<Long> response; + + PingRequest() { + pingData = new byte[8]; + random.nextBytes(pingData); + pingStamp = System.currentTimeMillis(); + response = new CompletableFuture<>(); + } + + PingFrame frame() { + return new PingFrame(0, pingData); + } + + CompletableFuture<Long> response() { + return response; + } + + void success() { + response.complete(System.currentTimeMillis() - pingStamp); + } + + void fail(Throwable t) { + response.completeExceptionally(t); + } + } + + Http2TestServerConnection(Http2TestServer server, + Socket socket, + Http2TestExchangeSupplier exchangeSupplier) + throws IOException + { if (socket instanceof SSLSocket) { handshake(server.serverName(), (SSLSocket)socket); } System.err.println("TestServer: New connection from " + socket); this.server = server; + this.exchangeSupplier = exchangeSupplier; this.streams = Collections.synchronizedMap(new HashMap<>()); - this.outputQ = new Queue<>(); + this.outStreams = Collections.synchronizedMap(new HashMap<>()); + this.outputQ = new Queue<>(sentinel); + this.random = new Random(); this.socket = socket; + this.socket.setTcpNoDelay(true); this.serverSettings = SettingsFrame.getDefaultSettings(); this.exec = server.exec; this.secure = server.secure; @@ -96,6 +141,67 @@ public class Http2TestServerConnection { os = new BufferedOutputStream(socket.getOutputStream()); } + /** + * Sends a PING frame on this connection, and completes the returned + * CF when the PING ack is received. The CF is given + * an integer, whose value is the number of milliseconds + * between PING and ACK. + */ + CompletableFuture<Long> sendPing() { + PingRequest ping = null; + try { + ping = new PingRequest(); + pings.add(ping); + outputQ.put(ping.frame()); + } catch (Throwable t) { + ping.fail(t); + } + return ping.response(); + } + + void goAway(int error) throws IOException { + int laststream = nextstream >= 3 ? nextstream - 2 : 1; + + GoAwayFrame go = new GoAwayFrame(laststream, error); + outputQ.put(go); + } + + /** + * Returns the first PingRequest from Queue + */ + private PingRequest getNextRequest() { + return pings.poll(); + } + + /** + * Handles incoming Ping, which could be an ack + * or a client originated Ping + */ + void handlePing(PingFrame ping) throws IOException { + if (ping.streamid() != 0) { + System.err.println("Invalid ping received"); + close(ErrorFrame.PROTOCOL_ERROR); + return; + } + if (ping.getFlag(PingFrame.ACK)) { + // did we send a Ping? + PingRequest request = getNextRequest(); + if (request == null) { + System.err.println("Invalid ping ACK received"); + close(ErrorFrame.PROTOCOL_ERROR); + return; + } else if (!Arrays.equals(request.pingData, ping.getData())) { + request.fail(new RuntimeException("Wrong ping data in ACK")); + } else { + request.success(); + } + } else { + // client originated PING. Just send it back with ACK set + ping.setFlag(PingFrame.ACK); + outputQ.put(ping); + } + } + private static boolean compareIPAddrs(InetAddress addr1, String host) { try { InetAddress addr2 = InetAddress.getByName(host); @@ -132,15 +238,25 @@ public class Http2TestServerConnection { sock.getSession(); // blocks until handshake done } - void close() { + void closeIncoming() { + close(-1); + } + + void close(int error) { + if (stopping) + return; stopping = true; + System.err.printf("Server connection to %s stopping. %d streams\n", + socket.getRemoteSocketAddress().toString(), streams.size()); streams.forEach((i, q) -> { - q.close(); + q.orderlyClose(); }); try { + if (error != -1) + goAway(error); + outputQ.orderlyClose(); socket.close(); - // TODO: put a reset on each stream - } catch (IOException e) { + } catch (Exception e) { } } @@ -186,8 +302,8 @@ public class Http2TestServerConnection { new byte[] {0, 0, (byte)payload.length, 4, 0, 0, 0, 0, 0}); List<Http2Frame> frames = new ArrayList<>(); FramesDecoder reader = new FramesDecoder(frames::add); - reader.decode(ByteBufferReference.of(bb0)); - reader.decode(ByteBufferReference.of(bb1)); + reader.decode(bb0); + reader.decode(bb1); if (frames.size()!=1) throw new IOException("Expected 1 frame got "+frames.size()) ; Http2Frame frame = frames.get(0); @@ -222,46 +338,51 @@ public class Http2TestServerConnection { nextstream = 3; } - exec.submit(this::readLoop); - exec.submit(this::writeLoop); + (new ConnectionThread("readLoop", this::readLoop)).start(); + (new ConnectionThread("writeLoop", this::writeLoop)).start(); } - static class BufferPool implements BufferHandler { - - public void setMinBufferSize(int size) { + class ConnectionThread extends Thread { + final Runnable r; + ConnectionThread(String name, Runnable r) { + setName(name); + setDaemon(true); + this.r = r; } - @Override - public ByteBuffer getBuffer() { - int size = 32 * 1024; - return ByteBuffer.allocate(size); - } - - @Override - public void returnBuffer(ByteBuffer buffer) { + public void run() { + r.run(); } } private void writeFrame(Http2Frame frame) throws IOException { - ByteBufferReference[] refs = new FramesEncoder().encodeFrame(frame); + List<ByteBuffer> bufs = new FramesEncoder().encodeFrame(frame); //System.err.println("TestServer: Writing frame " + frame.toString()); int c = 0; - for (ByteBufferReference ref : refs) { - ByteBuffer buf = ref.get(); + for (ByteBuffer buf : bufs) { byte[] ba = buf.array(); int start = buf.arrayOffset() + buf.position(); c += buf.remaining(); os.write(ba, start, buf.remaining()); + +// System.out.println("writing byte at a time"); +// while (buf.hasRemaining()) { +// byte b = buf.get(); +// os.write(b); +// os.flush(); +// try { +// Thread.sleep(1); +// } catch(InterruptedException e) { +// UncheckedIOException uie = new UncheckedIOException(new IOException("")); +// uie.addSuppressed(e); +// throw uie; +// } +// } } os.flush(); //System.err.printf("TestServer: wrote %d bytes\n", c); } - void handleStreamReset(ResetFrame resetFrame) throws IOException { - // TODO: cleanup - throw new IOException("Stream reset"); - } - private void handleCommonFrame(Http2Frame f) throws IOException { if (f instanceof SettingsFrame) { SettingsFrame sf = (SettingsFrame) f; @@ -276,9 +397,13 @@ public class Http2TestServerConnection { frame.streamid(0); outputQ.put(frame); return; - } - //System.err.println("TestServer: Received ---> " + f.toString()); - throw new UnsupportedOperationException("Not supported yet."); + } else if (f instanceof GoAwayFrame) { + System.err.println("Closing: "+ f.toString()); + close(ErrorFrame.NO_ERROR); + } else if (f instanceof PingFrame) { + handlePing((PingFrame)f); + } else + throw new UnsupportedOperationException("Not supported yet: " + f.toString()); } void sendWindowUpdates(int len, int streamid) throws IOException { @@ -290,7 +415,7 @@ public class Http2TestServerConnection { outputQ.put(wup); } - HttpHeadersImpl decodeHeaders(List<HeaderFrame> frames) { + HttpHeadersImpl decodeHeaders(List<HeaderFrame> frames) throws IOException { HttpHeadersImpl headers = new HttpHeadersImpl(); DecodingCallback cb = (name, value) -> { @@ -298,9 +423,9 @@ public class Http2TestServerConnection { }; for (HeaderFrame frame : frames) { - ByteBufferReference[] buffers = frame.getHeaderBlock(); - for (ByteBufferReference buffer : buffers) { - hpackIn.decode(buffer.get(), false, cb); + List<ByteBuffer> buffers = frame.getHeaderBlock(); + for (ByteBuffer buffer : buffers) { + hpackIn.decode(buffer, false, cb); } } hpackIn.decode(EMPTY_BUFFER, true, cb); @@ -359,7 +484,7 @@ public class Http2TestServerConnection { headers.setHeader(":scheme", "http"); // always in this case headers.setHeader(":authority", host); headers.setHeader(":path", uri.getPath()); - Queue q = new Queue(); + Queue q = new Queue(sentinel); String body = getRequestBody(request); addHeaders(getHeaders(request), headers); headers.setHeader("Content-length", Integer.toString(body.length())); @@ -401,7 +526,7 @@ public class Http2TestServerConnection { } boolean endStreamReceived = endStream; HttpHeadersImpl headers = decodeHeaders(frames); - Queue q = new Queue(); + Queue q = new Queue(sentinel); streams.put(streamid, q); exec.submit(() -> { handleRequest(headers, q, streamid, endStreamReceived); @@ -428,7 +553,7 @@ public class Http2TestServerConnection { System.err.printf("TestServer: %s %s\n", method, path); HttpHeadersImpl rspheaders = new HttpHeadersImpl(); int winsize = clientSettings.getParameter( - SettingsFrame.INITIAL_WINDOW_SIZE); + SettingsFrame.INITIAL_WINDOW_SIZE); //System.err.println ("Stream window size = " + winsize); final InputStream bis; @@ -442,10 +567,11 @@ public class Http2TestServerConnection { try (bis; BodyOutputStream bos = new BodyOutputStream(streamid, winsize, this)) { + outStreams.put(streamid, bos); String us = scheme + "://" + authority + path; URI uri = new URI(us); boolean pushAllowed = clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1; - Http2TestExchange exchange = new Http2TestExchange(streamid, method, + Http2TestExchange exchange = exchangeSupplier.get(streamid, method, headers, rspheaders, uri, bis, getSSLSession(), bos, this, pushAllowed); @@ -473,7 +599,11 @@ public class Http2TestServerConnection { void readLoop() { try { while (!stopping) { - Http2Frame frame = readFrame(); + Http2Frame frame = readFrameImpl(); + if (frame == null) { + closeIncoming(); + return; + } //System.err.printf("TestServer: received frame %s\n", frame); int stream = frame.streamid(); if (stream == 0) { @@ -497,7 +627,7 @@ public class Http2TestServerConnection { } else { if (q == null && !pushStreams.contains(stream)) { System.err.printf("Non Headers frame received with"+ - " non existing stream (%d) ", frame.streamid()); + " non existing stream (%d) ", frame.streamid()); System.err.println(frame); continue; } @@ -507,6 +637,17 @@ public class Http2TestServerConnection { Consumer<Integer> r = updaters.get(stream); r.accept(wup.getUpdate()); } + } else if (frame.type() == ResetFrame.TYPE) { + // do orderly close on input q + // and close the output q immediately + // This should mean depending on what the + // handler is doing: either an EOF on read + // or an IOException if writing the response. + q.orderlyClose(); + BodyOutputStream oq = outStreams.get(stream); + if (oq != null) + oq.closeInternal(); + } else { q.put(frame); } @@ -518,11 +659,11 @@ public class Http2TestServerConnection { System.err.println("Http server reader thread shutdown"); e.printStackTrace(); } - close(); + close(ErrorFrame.PROTOCOL_ERROR); } } - ByteBufferReference[] encodeHeaders(HttpHeadersImpl headers) { + List<ByteBuffer> encodeHeaders(HttpHeadersImpl headers) { List<ByteBuffer> buffers = new LinkedList<>(); ByteBuffer buf = getBuffer(); @@ -544,7 +685,7 @@ public class Http2TestServerConnection { } buf.flip(); buffers.add(buf); - return ByteBufferReference.toReferences(buffers.toArray(bbarray)); + return buffers; } static void closeIgnore(Closeable c) { @@ -560,6 +701,8 @@ public class Http2TestServerConnection { Http2Frame frame; try { frame = outputQ.take(); + if (stopping) + break; } catch(IOException x) { if (stopping && x.getCause() instanceof InterruptedException) { break; @@ -587,7 +730,11 @@ public class Http2TestServerConnection { private void handlePush(OutgoingPushPromise op) throws IOException { int promisedStreamid = nextPushStreamId; - PushPromiseFrame pp = new PushPromiseFrame(op.parentStream, HeaderFrame.END_HEADERS, promisedStreamid, encodeHeaders(op.headers), 0); + PushPromiseFrame pp = new PushPromiseFrame(op.parentStream, + HeaderFrame.END_HEADERS, + promisedStreamid, + encodeHeaders(op.headers), + 0); pushStreams.add(promisedStreamid); nextPushStreamId += 2; pp.streamid(op.parentStream); @@ -597,6 +744,7 @@ public class Http2TestServerConnection { promisedStreamid, clientSettings.getParameter( SettingsFrame.INITIAL_WINDOW_SIZE), this); + outStreams.put(promisedStreamid, oo); oo.goodToGo(); exec.submit(() -> { try { @@ -630,27 +778,46 @@ public class Http2TestServerConnection { } private Http2Frame readFrame() throws IOException { - byte[] buf = new byte[9]; - if (is.readNBytes(buf, 0, 9) != 9) - throw new IOException("readFrame: connection closed"); - int len = 0; - for (int i = 0; i < 3; i++) { - int n = buf[i] & 0xff; - //System.err.println("n = " + n); - len = (len << 8) + n; - } - byte[] rest = new byte[len]; - int n = is.readNBytes(rest, 0, len); - if (n != len) - throw new IOException("Error reading frame"); - List<Http2Frame> frames = new ArrayList<>(); - FramesDecoder reader = new FramesDecoder(frames::add); - reader.decode(ByteBufferReference.of(ByteBuffer.wrap(buf))); - reader.decode(ByteBufferReference.of(ByteBuffer.wrap(rest))); - if (frames.size()!=1) - throw new IOException("Expected 1 frame got "+frames.size()) ; + Http2Frame f = readFrameImpl(); + if (f == null) + throw new IOException("connection closed"); + return f; + } - return frames.get(0); + // does not throw an exception for EOF + private Http2Frame readFrameImpl() throws IOException { + try { + byte[] buf = new byte[9]; + int ret; + ret=is.readNBytes(buf, 0, 9); + if (ret == 0) { + return null; + } else if (ret != 9) { + throw new IOException("readFrame: connection closed"); + } + int len = 0; + for (int i = 0; i < 3; i++) { + int n = buf[i] & 0xff; + //System.err.println("n = " + n); + len = (len << 8) + n; + } + byte[] rest = new byte[len]; + int n = is.readNBytes(rest, 0, len); + if (n != len) + throw new IOException("Error reading frame"); + List<Http2Frame> frames = new ArrayList<>(); + FramesDecoder reader = new FramesDecoder(frames::add); + reader.decode(ByteBuffer.wrap(buf)); + reader.decode(ByteBuffer.wrap(rest)); + if (frames.size()!=1) + throw new IOException("Expected 1 frame got "+frames.size()) ; + + return frames.get(0); + } catch (IOException ee) { + if (stopping) + return null; + throw ee; + } } void sendSettingsFrame() throws IOException { @@ -721,11 +888,29 @@ public class Http2TestServerConnection { String readHttp1Request() throws IOException { String headers = readUntil(CRLF + CRLF); int clen = getContentLength(headers); - // read the content. - byte[] buf = new byte[clen]; - is.readNBytes(buf, 0, clen); - String body = new String(buf, StandardCharsets.US_ASCII); - return headers + body; + String te = getHeader(headers, "Transfer-encoding"); + byte[] buf = new byte[0]; + try { + if (clen >= 0) { + // HTTP/1.1 fixed length content ( may be 0 ), read it + buf = new byte[clen]; + is.readNBytes(buf, 0, clen); + } else if ("chunked".equalsIgnoreCase(te)) { + // HTTP/1.1 chunked data, read it + buf = readChunkedInputStream(is); + } + String body = new String(buf, StandardCharsets.US_ASCII); + return headers + body; + } catch (IOException e) { + System.err.println("TestServer: headers read: [ " + headers + " ]"); + throw e; + } + } + + // This is a quick hack to get a chunked input stream reader. + private static byte[] readChunkedInputStream(InputStream is) throws IOException { + ChunkedInputStream cis = new ChunkedInputStream(is, new HttpClient() {}, null); + return cis.readAllBytes(); } void sendHttp1Response(int code, String msg, String... headers) throws IOException { @@ -771,7 +956,7 @@ public class Http2TestServerConnection { @SuppressWarnings({"rawtypes","unchecked"}) void addRequestBodyToQueue(String body, Queue q) throws IOException { ByteBuffer buf = ByteBuffer.wrap(body.getBytes(StandardCharsets.US_ASCII)); - DataFrame df = new DataFrame(1, DataFrame.END_STREAM, ByteBufferReference.of(buf)); + DataFrame df = new DataFrame(1, DataFrame.END_STREAM, buf); // only used for primordial stream q.put(df); } @@ -795,13 +980,13 @@ public class Http2TestServerConnection { * @param amount */ synchronized void obtainConnectionWindow(int amount) throws InterruptedException { - while (amount > 0) { - int n = Math.min(amount, sendWindow); - amount -= n; - sendWindow -= n; - if (amount > 0) - wait(); - } + while (amount > 0) { + int n = Math.min(amount, sendWindow); + amount -= n; + sendWindow -= n; + if (amount > 0) + wait(); + } } synchronized void updateConnectionWindow(int amount) { @@ -823,9 +1008,9 @@ public class Http2TestServerConnection { } static class NullInputStream extends InputStream { - static final NullInputStream INSTANCE = new NullInputStream(); - private NullInputStream() {} - public int read() { return -1; } - public int available() { return 0; } - } + static final NullInputStream INSTANCE = new NullInputStream(); + private NullInputStream() {} + public int read() { return -1; } + public int available() { return 0; } + } } diff --git a/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java b/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java index b80a398a6bb..6cf7ea3d4ed 100644 --- a/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java +++ b/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java b/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java index 905ee3d4a38..7ef4684e591 100644 --- a/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java +++ b/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/http2/server/PushHandler.java b/test/jdk/java/net/httpclient/http2/server/PushHandler.java index e2af22b822d..fcfb4ce7fcd 100644 --- a/test/jdk/java/net/httpclient/http2/server/PushHandler.java +++ b/test/jdk/java/net/httpclient/http2/server/PushHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java b/test/jdk/java/net/httpclient/http2/server/Queue.java similarity index 63% rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java rename to test/jdk/java/net/httpclient/http2/server/Queue.java index 004c3a4d5e4..cc789272317 100644 --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java +++ b/test/jdk/java/net/httpclient/http2/server/Queue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -23,37 +23,34 @@ * questions. */ -package jdk.incubator.http.internal.common; - import java.io.IOException; import java.util.LinkedList; -import java.util.stream.Stream; import java.util.Objects; +import java.util.stream.Stream; // Each stream has one of these for input. Each Http2Connection has one // for output. Can be used blocking or asynchronously. -public class Queue<T> implements ExceptionallyCloseable { +public class Queue<T> implements ExceptionallyCloseable { private final LinkedList<T> q = new LinkedList<>(); - private volatile boolean closed = false; - private volatile Throwable exception = null; - private Runnable callback; - private boolean callbackDisabled = false; + private boolean closed = false; + private boolean closing = false; + private Throwable exception = null; private int waiters; // true if someone waiting + private final T closeSentinel; + + Queue(T closeSentinel) { + this.closeSentinel = Objects.requireNonNull(closeSentinel); + } public synchronized int size() { return q.size(); } - public synchronized boolean tryPut(T obj) throws IOException { - if (closed) return false; - put(obj); - return true; - } - public synchronized void put(T obj) throws IOException { - if (closed) { + Objects.requireNonNull(obj); + if (closed || closing) { throw new IOException("stream closed"); } @@ -62,45 +59,27 @@ public class Queue<T> implements ExceptionallyCloseable { if (waiters > 0) { notifyAll(); } + } - if (callbackDisabled) { + // Other close() variants are immediate and abortive + // This allows whatever is on Q to be processed first. + + public synchronized void orderlyClose() { + if (closing || closed) return; - } - if (q.size() > 0 && callback != null) { - // Note: calling callback while holding the lock is - // dangerous and may lead to deadlocks. - callback.run(); - } - } - - public synchronized void disableCallback() { - callbackDisabled = true; - } - - public synchronized void enableCallback() { - callbackDisabled = false; - while (q.size() > 0) { - callback.run(); - } - } - - /** - * callback is invoked any time put is called where - * the Queue was empty. - */ - public synchronized void registerPutCallback(Runnable callback) { - Objects.requireNonNull(callback); - this.callback = callback; - if (q.size() > 0) { - // Note: calling callback while holding the lock is - // dangerous and may lead to deadlocks. - callback.run(); + try { + put(closeSentinel); + } catch (IOException e) { + e.printStackTrace(); } + closing = true; } @Override public synchronized void close() { + if (closed) + return; closed = true; notifyAll(); } @@ -133,7 +112,13 @@ public class Queue<T> implements ExceptionallyCloseable { } waiters--; } - return q.removeFirst(); + T item = q.removeFirst(); + if (item.equals(closeSentinel)) { + closed = true; + assert q.isEmpty(); + return null; + } + return item; } catch (InterruptedException ex) { throw new IOException(ex); } @@ -147,24 +132,7 @@ public class Queue<T> implements ExceptionallyCloseable { if (q.isEmpty()) { return null; } - T res = q.removeFirst(); - return res; - } - - public synchronized T[] pollAll(T[] type) throws IOException { - T[] ret = q.toArray(type); - q.clear(); - return ret; - } - - public synchronized void pushback(T v) { - q.addFirst(v); - } - - public synchronized void pushbackAll(T[] v) { - for (int i=v.length-1; i>=0; i--) { - q.addFirst(v[i]); - } + return take(); } private IOException newIOException(String msg) { @@ -174,5 +142,4 @@ public class Queue<T> implements ExceptionallyCloseable { return new IOException(msg, exception); } } - } diff --git a/test/jdk/java/net/httpclient/http2/server/TestUtil.java b/test/jdk/java/net/httpclient/http2/server/TestUtil.java index 2bd85a29a59..a439f0a2596 100644 --- a/test/jdk/java/net/httpclient/http2/server/TestUtil.java +++ b/test/jdk/java/net/httpclient/http2/server/TestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 diff --git a/test/jdk/java/net/httpclient/security/0.policy b/test/jdk/java/net/httpclient/security/0.policy index 4eeb7f3a8d2..d9b7a757bdc 100644 --- a/test/jdk/java/net/httpclient/security/0.policy +++ b/test/jdk/java/net/httpclient/security/0.policy @@ -1,10 +1,32 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy: 0 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/1.policy b/test/jdk/java/net/httpclient/security/1.policy index 6527ba9219f..964e8b643dc 100644 --- a/test/jdk/java/net/httpclient/security/1.policy +++ b/test/jdk/java/net/httpclient/security/1.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 1 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,9 +62,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/10.policy b/test/jdk/java/net/httpclient/security/10.policy index ae806ff2019..32a985c33f8 100644 --- a/test/jdk/java/net/httpclient/security/10.policy +++ b/test/jdk/java/net/httpclient/security/10.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 10 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -39,8 +61,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/11.policy b/test/jdk/java/net/httpclient/security/11.policy index 28210946243..4db4cfe6e80 100644 --- a/test/jdk/java/net/httpclient/security/11.policy +++ b/test/jdk/java/net/httpclient/security/11.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 11 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/12.policy b/test/jdk/java/net/httpclient/security/12.policy index 28210946243..4db4cfe6e80 100644 --- a/test/jdk/java/net/httpclient/security/12.policy +++ b/test/jdk/java/net/httpclient/security/12.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 11 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/14.policy b/test/jdk/java/net/httpclient/security/14.policy index 859c4f85a4e..5cd778beb87 100644 --- a/test/jdk/java/net/httpclient/security/14.policy +++ b/test/jdk/java/net/httpclient/security/14.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 14 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/15.policy b/test/jdk/java/net/httpclient/security/15.policy index c25941bfc2a..5418bbea054 100644 --- a/test/jdk/java/net/httpclient/security/15.policy +++ b/test/jdk/java/net/httpclient/security/15.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 11 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -43,8 +65,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/2.policy b/test/jdk/java/net/httpclient/security/2.policy index efb4876bae3..2b3e1b9f7ed 100644 --- a/test/jdk/java/net/httpclient/security/2.policy +++ b/test/jdk/java/net/httpclient/security/2.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 2 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/3.policy b/test/jdk/java/net/httpclient/security/3.policy index f64232d1f7d..b876accab84 100644 --- a/test/jdk/java/net/httpclient/security/3.policy +++ b/test/jdk/java/net/httpclient/security/3.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 3 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/4.policy b/test/jdk/java/net/httpclient/security/4.policy index 8f6011743fd..f06f38433bc 100644 --- a/test/jdk/java/net/httpclient/security/4.policy +++ b/test/jdk/java/net/httpclient/security/4.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 4 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/5.policy b/test/jdk/java/net/httpclient/security/5.policy index 0b7d146daf5..3afcb50c676 100644 --- a/test/jdk/java/net/httpclient/security/5.policy +++ b/test/jdk/java/net/httpclient/security/5.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 5 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/6.policy b/test/jdk/java/net/httpclient/security/6.policy index 166f60906f1..b2dffc008c8 100644 --- a/test/jdk/java/net/httpclient/security/6.policy +++ b/test/jdk/java/net/httpclient/security/6.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 6 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/7.policy b/test/jdk/java/net/httpclient/security/7.policy index ea963c3f449..b0f6bc49308 100644 --- a/test/jdk/java/net/httpclient/security/7.policy +++ b/test/jdk/java/net/httpclient/security/7.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 7 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/8.policy b/test/jdk/java/net/httpclient/security/8.policy index 63265f80d6e..b5dbcf2366a 100644 --- a/test/jdk/java/net/httpclient/security/8.policy +++ b/test/jdk/java/net/httpclient/security/8.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 8 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/9.policy b/test/jdk/java/net/httpclient/security/9.policy index 4597c2f7dae..2aa216552b4 100644 --- a/test/jdk/java/net/httpclient/security/9.policy +++ b/test/jdk/java/net/httpclient/security/9.policy @@ -1,9 +1,31 @@ +// +// Copyright (c) 2016, 2017, 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. +// + // Policy 9 grant { // permissions common to all tests permission java.util.PropertyPermission "*", "read"; permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete"; - permission java.net.NetPermission "getDefaultHttpClient"; permission java.lang.RuntimePermission "modifyThread"; permission java.util.logging.LoggingPermission "control", ""; permission java.net.SocketPermission "localhost:1024-", "accept,listen"; @@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" { permission java.util.PropertyPermission "jdk.httpclient.*","read"; - // ## these permissions do not appear in the NetPermission spec!!! JDK bug? - permission java.net.NetPermission "getSSLContext"; - permission java.net.NetPermission "setSSLContext"; + permission java.net.NetPermission "getProxySelector"; }; diff --git a/test/jdk/java/net/httpclient/security/Driver.java b/test/jdk/java/net/httpclient/security/Driver.java index 48d3d78dc27..3ccec62fdb5 100644 --- a/test/jdk/java/net/httpclient/security/Driver.java +++ b/test/jdk/java/net/httpclient/security/Driver.java @@ -34,7 +34,7 @@ * @compile ../ProxyServer.java * @build Security * - * @run driver/timeout=90 Driver + * @run main/othervm Driver */ /** @@ -52,8 +52,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; - -import jdk.testlibrary.OutputAnalyzer; import jdk.testlibrary.Utils; /** @@ -123,6 +121,8 @@ public class Driver { while (retval == 10) { List<String> cmd = new ArrayList<>(); cmd.add(javaCmd); + cmd.add("-ea"); + cmd.add("-esa"); cmd.add("-Dtest.jdk=" + testJdk); cmd.add("-Dtest.src=" + testSrc); cmd.add("-Dtest.classes=" + testClasses); @@ -142,11 +142,15 @@ public class Driver { .redirectErrorStream(true); String cmdLine = cmd.stream().collect(Collectors.joining(" ")); + long start = System.currentTimeMillis(); Process child = processBuilder.start(); Logger log = new Logger(cmdLine, child, testClasses); log.start(); retval = child.waitFor(); - System.out.println("retval = " + retval); + long elapsed = System.currentTimeMillis() - start; + System.out.println("Security " + testnum + + ": retval = " + retval + + ", duration=" + elapsed+" ms"); } if (retval != 0) { Thread.sleep(2000); diff --git a/test/jdk/java/net/httpclient/security/Security.java b/test/jdk/java/net/httpclient/security/Security.java index 4fa142dfcff..d6b1c623efd 100644 --- a/test/jdk/java/net/httpclient/security/Security.java +++ b/test/jdk/java/net/httpclient/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -82,7 +82,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Flow; @@ -297,15 +296,15 @@ public class Security { CompletableFuture<HttpResponse<String>> cf = client.sendAsync(request, new HttpResponse.BodyHandler<String>() { @Override - public HttpResponse.BodyProcessor<String> apply(int status, HttpHeaders responseHeaders) { - final HttpResponse.BodyProcessor<String> stproc = sth.apply(status, responseHeaders); - return new HttpResponse.BodyProcessor<String>() { + public HttpResponse.BodySubscriber<String> apply(int status, HttpHeaders responseHeaders) { + final HttpResponse.BodySubscriber<String> stproc = sth.apply(status, responseHeaders); + return new HttpResponse.BodySubscriber<String>() { @Override public CompletionStage<String> getBody() { return stproc.getBody(); } @Override - public void onNext(ByteBuffer item) { + public void onNext(List<ByteBuffer> item) { SecurityManager sm = System.getSecurityManager(); // should succeed. sm.checkPermission(new RuntimePermission("foobar")); @@ -337,6 +336,9 @@ public class Security { Throwable t = e.getCause(); if (t instanceof SecurityException) throw (SecurityException)t; + else if ((t instanceof IOException) + && (t.getCause() instanceof SecurityException)) + throw ((SecurityException)t.getCause()); else throw new RuntimeException(t); } @@ -379,7 +381,7 @@ public class Security { r.execute(); if (!succeeds) { System.out.println("FAILED: expected security exception"); - throw new RuntimeException("Failed"); + throw new RuntimeException("FAILED: expected security exception\""); } System.out.println (policy + " succeeded as expected"); } catch (BindException e) { @@ -419,12 +421,6 @@ public class Security { } finally { s1.stop(0); executor.shutdownNow(); - for (HttpClient client : clients) { - Executor e = client.executor(); - if (e instanceof ExecutorService) { - ((ExecutorService)e).shutdownNow(); - } - } } } diff --git a/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java new file mode 100644 index 00000000000..d8bf837adbc --- /dev/null +++ b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017, 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 + * @summary Basic checks for SecurityException from body processors APIs + * @run testng/othervm/java.security.policy=httpclient.policy FileProcessorPermissionTest + */ + +import java.io.FilePermission; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.Permissions; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.util.List; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import org.testng.annotations.Test; +import static java.nio.file.StandardOpenOption.*; +import static org.testng.Assert.*; + +public class FileProcessorPermissionTest { + + static final String testSrc = System.getProperty("test.src", "."); + static final Path fromFilePath = Paths.get(testSrc, "FileProcessorPermissionTest.java"); + static final Path asFilePath = Paths.get(testSrc, "asFile.txt"); + static final Path CWD = Paths.get("."); + static final Class<SecurityException> SE = SecurityException.class; + + static AccessControlContext withPermissions(Permission... perms) { + Permissions p = new Permissions(); + for (Permission perm : perms) { + p.add(perm); + } + ProtectionDomain pd = new ProtectionDomain(null, p); + return new AccessControlContext(new ProtectionDomain[]{ pd }); + } + + static AccessControlContext noPermissions() { + return withPermissions(/*empty*/); + } + + @Test + public void test() throws Exception { + List<PrivilegedExceptionAction<?>> list = List.of( + () -> HttpRequest.BodyPublisher.fromFile(fromFilePath), + + () -> HttpResponse.BodyHandler.asFile(asFilePath), + () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE), + () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE), + () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE, READ), + () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE, READ, DELETE_ON_CLOSE), + + () -> HttpResponse.BodyHandler.asFileDownload(CWD), + () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE), + () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE), + () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE, READ), + () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE, READ, DELETE_ON_CLOSE), + + // TODO: what do these even mean by themselves, maybe ok means nothing? + () -> HttpResponse.BodyHandler.asFile(asFilePath, DELETE_ON_CLOSE), + () -> HttpResponse.BodyHandler.asFile(asFilePath, READ) + ); + + // sanity, just run http ( no security manager ) + System.setSecurityManager(null); + try { + for (PrivilegedExceptionAction pa : list) { + AccessController.doPrivileged(pa); + } + } finally { + System.setSecurityManager(new SecurityManager()); + } + + // Run with all permissions, i.e. no further restrictions than test's AllPermission + for (PrivilegedExceptionAction pa : list) { + try { + assert System.getSecurityManager() != null; + AccessController.doPrivileged(pa, null, new Permission[] { }); + } catch (PrivilegedActionException pae) { + fail("UNEXPECTED Exception:" + pae); + pae.printStackTrace(); + } + } + + // Run with limited permissions, i.e. just what is required + AccessControlContext minimalACC = withPermissions( + new FilePermission(fromFilePath.toString() , "read"), + new FilePermission(asFilePath.toString(), "read,write,delete"), + new FilePermission(CWD.toString(), "read,write,delete") + ); + for (PrivilegedExceptionAction pa : list) { + try { + assert System.getSecurityManager() != null; + AccessController.doPrivileged(pa, minimalACC); + } catch (PrivilegedActionException pae) { + fail("UNEXPECTED Exception:" + pae); + pae.printStackTrace(); + } + } + + // Run with NO permissions, i.e. expect SecurityException + for (PrivilegedExceptionAction pa : list) { + try { + assert System.getSecurityManager() != null; + AccessController.doPrivileged(pa, noPermissions()); + fail("EXPECTED SecurityException"); + } catch (SecurityException expected) { + System.out.println("Caught expected SE:" + expected); + } + } + } +} diff --git a/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy b/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy new file mode 100644 index 00000000000..8a2c74df9b7 --- /dev/null +++ b/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy @@ -0,0 +1,68 @@ +// +// Copyright (c) 2017, 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. +// + +grant codeBase "jrt:/jdk.incubator.httpclient" { + permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; + + // ## why is SP not good enough. Check API @throws signatures and impl + permission java.net.SocketPermission "*","connect,resolve"; + permission java.net.URLPermission "http:*","*:*"; + permission java.net.URLPermission "https:*","*:*"; + permission java.net.URLPermission "ws:*","*:*"; + permission java.net.URLPermission "wss:*","*:*"; + permission java.net.URLPermission "socket:*","CONNECT"; // proxy + + // For request/response body processors, fromFile, asFile + permission java.io.FilePermission "<<ALL FILES>>","read,write,delete"; + + // ## look at the different property names! + permission java.util.PropertyPermission "jdk.httpclient.HttpClient.log","read"; // name! + permission java.util.PropertyPermission "jdk.httpclient.auth.retrylimit","read"; + permission java.util.PropertyPermission "jdk.httpclient.connectionWindowSize","read"; + permission java.util.PropertyPermission "jdk.httpclient.enablepush","read"; + permission java.util.PropertyPermission "jdk.httpclient.hpack.maxheadertablesize","read"; + permission java.util.PropertyPermission "jdk.httpclient.keepalive.timeout","read"; + permission java.util.PropertyPermission "jdk.httpclient.maxframesize","read"; + permission java.util.PropertyPermission "jdk.httpclient.maxstreams","read"; + permission java.util.PropertyPermission "jdk.httpclient.redirects.retrylimit","read"; + permission java.util.PropertyPermission "jdk.httpclient.windowsize","read"; + permission java.util.PropertyPermission "jdk.httpclient.bufsize","read"; + permission java.util.PropertyPermission "jdk.httpclient.internal.selector.timeout","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.debug","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.debug","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.log.level","read"; + permission java.util.PropertyPermission "test.src","read"; + + permission java.net.NetPermission "getProxySelector"; + + permission java.security.SecurityPermission "createAccessControlContext"; +}; + +// bootstrap to get the test going, it will do its own restrictions +grant codeBase "file:${test.classes}/*" { + permission java.security.AllPermission; +}; + diff --git a/test/jdk/java/net/httpclient/websocket/WSDriver.java b/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java similarity index 52% rename from test/jdk/java/net/httpclient/websocket/WSDriver.java rename to test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java index b7dcab17b43..b45e177da5f 100644 --- a/test/jdk/java/net/httpclient/websocket/WSDriver.java +++ b/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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,16 +25,6 @@ * @test * @bug 8159053 * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open - * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/websocket/TestSupport.java - * - * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.HeaderWriterTest - * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReaderTest - * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MaskerTest * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.BuildingWebSocketTest */ -public final class WSDriver { -// * @run testng/othervm -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MockListenerTest -// * @run testng/othervm -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MockChannelTest -// * @run testng/othervm/timeout=1000 -Ddataproviderthreadcount=16 -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.PingTest -} - +public final class BuildingWebSocketDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java b/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java index e89ce790404..b62527f4e99 100644 --- a/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java +++ b/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java @@ -31,14 +31,10 @@ import java.net.URI; * @test * @bug 8164625 * @summary Verifies HttpClient yields the connection to the WebSocket + * @build DummyWebSocketServer * @run main/othervm -Djdk.httpclient.HttpClient.log=trace ConnectionHandover */ public class ConnectionHandover { - - static { - LoggingHelper.setupLogging(); - } - /* * An I/O channel associated with the connection is closed by WebSocket.abort(). * If this connection is returned to the connection pool, then the second @@ -52,17 +48,15 @@ public class ConnectionHandover { server.open(); URI uri = server.getURI(); WebSocket.Builder webSocketBuilder = - HttpClient.newHttpClient().newWebSocketBuilder(uri, new WebSocket.Listener() { }); + HttpClient.newHttpClient().newWebSocketBuilder(); - WebSocket ws1 = webSocketBuilder.buildAsync().join(); - try { - ws1.abort(); - } catch (IOException ignored) { } + WebSocket ws1 = webSocketBuilder + .buildAsync(uri, new WebSocket.Listener() { }).join(); + ws1.abort(); - WebSocket ws2 = webSocketBuilder.buildAsync().join(); // Exception here if the connection was pooled - try { - ws2.abort(); - } catch (IOException ignored) { } + WebSocket ws2 = webSocketBuilder + .buildAsync(uri, new WebSocket.Listener() { }).join(); // Exception here if the connection was pooled + ws2.abort(); } } } diff --git a/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java b/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java index 2282e180b10..7e72d52e7f4 100644 --- a/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java +++ b/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java @@ -34,6 +34,7 @@ import java.nio.channels.SocketChannel; import java.nio.charset.CharacterCodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -47,9 +48,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import static java.lang.String.format; -import static java.lang.System.Logger.Level.ERROR; -import static java.lang.System.Logger.Level.INFO; -import static java.lang.System.Logger.Level.TRACE; +import static java.lang.System.err; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; @@ -83,7 +82,6 @@ import static java.util.Objects.requireNonNull; */ public final class DummyWebSocketServer implements Closeable { - private final static System.Logger log = System.getLogger(DummyWebSocketServer.class.getName()); private final AtomicBoolean started = new AtomicBoolean(); private final Thread thread; private volatile ServerSocketChannel ssc; @@ -98,9 +96,9 @@ public final class DummyWebSocketServer implements Closeable { thread = new Thread(() -> { try { while (!Thread.currentThread().isInterrupted()) { - log.log(INFO, "Accepting next connection at: " + ssc); + err.println("Accepting next connection at: " + ssc); SocketChannel channel = ssc.accept(); - log.log(INFO, "Accepted: " + channel); + err.println("Accepted: " + channel); try { channel.configureBlocking(true); StringBuilder request = new StringBuilder(); @@ -117,18 +115,18 @@ public final class DummyWebSocketServer implements Closeable { b.clear(); } } catch (IOException e) { - log.log(TRACE, () -> "Error in connection: " + channel, e); + err.println("Error in connection: " + channel + ", " + e); } finally { - log.log(INFO, "Closed: " + channel); + err.println("Closed: " + channel); close(channel); } } } catch (ClosedByInterruptException ignored) { } catch (IOException e) { - log.log(ERROR, e); + err.println(e); } finally { close(ssc); - log.log(INFO, "Stopped at: " + getURI()); + err.println("Stopped at: " + getURI()); } }); thread.setName("DummyWebSocketServer"); @@ -136,7 +134,7 @@ public final class DummyWebSocketServer implements Closeable { } public void open() throws IOException { - log.log(INFO, "Starting"); + err.println("Starting"); if (!started.compareAndSet(false, true)) { throw new IllegalStateException("Already started"); } @@ -149,12 +147,12 @@ public final class DummyWebSocketServer implements Closeable { } catch (IOException e) { close(ssc); } - log.log(INFO, "Started at: " + getURI()); + err.println("Started at: " + getURI()); } @Override public void close() { - log.log(INFO, "Stopping: " + getURI()); + err.println("Stopping: " + getURI()); thread.interrupt(); close(ssc); } @@ -210,12 +208,13 @@ public final class DummyWebSocketServer implements Closeable { if (!iterator.hasNext()) { throw new IllegalStateException("The request is empty"); } - if (!"GET / HTTP/1.1".equals(iterator.next())) { + String statusLine = iterator.next(); + if (!(statusLine.startsWith("GET /") && statusLine.endsWith(" HTTP/1.1"))) { throw new IllegalStateException ("Unexpected status line: " + request.get(0)); } response.add("HTTP/1.1 101 Switching Protocols"); - Map<String, String> requestHeaders = new HashMap<>(); + Map<String, List<String>> requestHeaders = new HashMap<>(); while (iterator.hasNext()) { String header = iterator.next(); String[] split = header.split(": "); @@ -224,10 +223,8 @@ public final class DummyWebSocketServer implements Closeable { ("Unexpected header: " + header + ", split=" + Arrays.toString(split)); } - if (requestHeaders.put(split[0], split[1]) != null) { - throw new IllegalStateException - ("Duplicating headers: " + Arrays.toString(split)); - } + requestHeaders.computeIfAbsent(split[0], k -> new ArrayList<>()).add(split[1]); + } if (requestHeaders.containsKey("Sec-WebSocket-Protocol")) { throw new IllegalStateException("Subprotocols are not expected"); @@ -240,17 +237,20 @@ public final class DummyWebSocketServer implements Closeable { expectHeader(requestHeaders, "Upgrade", "websocket"); response.add("Upgrade: websocket"); expectHeader(requestHeaders, "Sec-WebSocket-Version", "13"); - String key = requestHeaders.get("Sec-WebSocket-Key"); - if (key == null) { + List<String> key = requestHeaders.get("Sec-WebSocket-Key"); + if (key == null || key.isEmpty()) { throw new IllegalStateException("Sec-WebSocket-Key is missing"); } + if (key.size() != 1) { + throw new IllegalStateException("Sec-WebSocket-Key has too many values : " + key); + } MessageDigest sha1 = null; try { sha1 = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { throw new InternalError(e); } - String x = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + String x = key.get(0) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; sha1.update(x.getBytes(ISO_8859_1)); String v = Base64.getEncoder().encodeToString(sha1.digest()); response.add("Sec-WebSocket-Accept: " + v); @@ -258,17 +258,17 @@ public final class DummyWebSocketServer implements Closeable { }; } - protected static String expectHeader(Map<String, String> headers, + protected static String expectHeader(Map<String, List<String>> headers, String name, String value) { - String v = headers.get(name); - if (!value.equals(v)) { + List<String> v = headers.get(name); + if (!v.contains(value)) { throw new IllegalStateException( format("Expected '%s: %s', actual: '%s: %s'", name, value, name, v) ); } - return v; + return value; } private static void close(AutoCloseable... acs) { diff --git a/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java new file mode 100644 index 00000000000..f806bd402c1 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 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 8159053 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open + * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.HeaderWriterTest + */ +public final class HeaderWriterDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/MaskerDriver.java b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java new file mode 100644 index 00000000000..facf0a812d5 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 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 8159053 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open + * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MaskerTest + */ +public final class MaskerDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/ReaderDriver.java b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java new file mode 100644 index 00000000000..d43e48eb088 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 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 8159053 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open + * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReaderTest + */ +public final class ReaderDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java b/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java new file mode 100644 index 00000000000..e327c9a5d38 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open + * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReceivingTest + */ +public class ReceivingTestDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java b/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java new file mode 100644 index 00000000000..85a061a2126 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open + * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.SendingTest + */ +public class SendingTestDriver { } diff --git a/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java b/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java new file mode 100644 index 00000000000..eebd38e87fc --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, 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 + * @summary Basic test for WebSocketHandshakeException + * @library /lib/testlibrary + * @build jdk.testlibrary.SimpleSSLContext + * @modules jdk.incubator.httpclient + * jdk.httpserver + * @run testng/othervm WSHandshakeException + */ +; +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.incubator.http.HttpClient; +import javax.net.ssl.SSLContext; +import jdk.incubator.http.WebSocket; +import jdk.incubator.http.WebSocketHandshakeException; +import jdk.testlibrary.SimpleSSLContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; +import static org.testng.Assert.assertTrue; + +public class WSHandshakeException { + + SSLContext sslContext; + HttpServer httpTestServer; // HTTP/1.1 [ 2 servers ] + HttpsServer httpsTestServer; // HTTPS/1.1 + String httpURI; + String httpsURI; + + + static final int ITERATION_COUNT = 10; + // a shared executor helps reduce the amount of threads created by the test + static final Executor executor = Executors.newCachedThreadPool(); + + @DataProvider(name = "variants") + public Object[][] variants() { + return new Object[][]{ + { httpURI, false }, + { httpsURI, false }, + { httpURI, true }, + { httpsURI, true }, + }; + } + + HttpClient newHttpClient() { + return HttpClient.newBuilder() + .executor(executor) + .sslContext(sslContext) + .build(); + } + + @Test(dataProvider = "variants") + public void test(String uri, boolean sameClient) throws Exception { + HttpClient client = null; + for (int i=0; i< ITERATION_COUNT; i++) { + if (!sameClient || client == null) + client = newHttpClient(); + + try { + client.newWebSocketBuilder() + .buildAsync(URI.create(uri), new WebSocket.Listener() { }) + .join(); + fail("Expected to throw"); + } catch (CompletionException ce) { + Throwable t = ce.getCause(); + assertTrue(t instanceof WebSocketHandshakeException); + WebSocketHandshakeException wse = (WebSocketHandshakeException) t; + assertEquals(wse.getResponse().statusCode(), 404); + } + } + } + + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + // HTTP/1.1 + InetSocketAddress sa = new InetSocketAddress("localhost", 0); + httpTestServer = HttpServer.create(sa, 0); + httpURI = "ws://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/"; + + httpsTestServer = HttpsServer.create(sa, 0); + httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + httpsURI = "wss://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/"; + + httpTestServer.start(); + httpsTestServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + httpTestServer.stop(0); + httpsTestServer.stop(0); + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java index 15e70e38fbc..852c903da90 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -34,6 +34,7 @@ import java.util.List; import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally; import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows; @@ -45,85 +46,77 @@ import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows; */ public class BuildingWebSocketTest { + private final static URI VALID_URI = URI.create("ws://websocket.example.com"); + @Test - public void nulls() { + public void nullArguments() { HttpClient c = HttpClient.newHttpClient(); - URI uri = URI.create("ws://websocket.example.com"); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(null, listener())); + () -> c.newWebSocketBuilder() + .buildAsync(null, listener())); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, null)); + () -> c.newWebSocketBuilder() + .buildAsync(VALID_URI, null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(null, null)); + () -> c.newWebSocketBuilder() + .buildAsync(null, null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .header(null, "value")); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .header("name", null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .header(null, null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .subprotocols(null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) - .subprotocols(null, "sub1")); + () -> c.newWebSocketBuilder() + .subprotocols(null, "sub2.example.com")); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .subprotocols("sub1.example.com", (String) null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() .subprotocols("sub1.example.com", (String[]) null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) - .subprotocols("sub1.example.com", - "sub2.example.com", - null)); + () -> c.newWebSocketBuilder() + .subprotocols("sub1.example.com", "sub2.example.com", null)); assertThrows(NullPointerException.class, - () -> c.newWebSocketBuilder(uri, listener()) + () -> c.newWebSocketBuilder() + .subprotocols("sub1.example.com", null, "sub3.example.com")); + assertThrows(NullPointerException.class, + () -> c.newWebSocketBuilder() .connectTimeout(null)); } @Test(dataProvider = "badURIs") - void illegalURI(String u) { - WebSocket.Builder b = HttpClient.newHttpClient() - .newWebSocketBuilder(URI.create(u), listener()); - assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync()); + void illegalURI(URI uri) { + WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder(); + assertCompletesExceptionally(IllegalArgumentException.class, + b.buildAsync(uri, listener())); } @Test public void illegalHeaders() { - List<String> headers = List.of("Authorization", - "Connection", - "Cookie", - "Content-Length", - "Date", - "Expect", - "From", - "Host", - "Origin", - "Proxy-Authorization", - "Referer", - "User-agent", - "Upgrade", - "Via", - "Warning", - "Sec-WebSocket-Accept", - "Sec-WebSocket-Extensions", - "Sec-WebSocket-Key", - "Sec-WebSocket-Protocol", - "Sec-WebSocket-Version").stream() - .map(String::new).collect(Collectors.toList()); + List<String> headers = + List.of("Sec-WebSocket-Accept", + "Sec-WebSocket-Extensions", + "Sec-WebSocket-Key", + "Sec-WebSocket-Protocol", + "Sec-WebSocket-Version") + .stream() + .flatMap(s -> Stream.of(s, new String(s))) // a string and a copy of it + .collect(Collectors.toList()); Function<String, CompletionStage<?>> f = - header -> HttpClient - .newHttpClient() - .newWebSocketBuilder(URI.create("ws://websocket.example.com"), - listener()) - .buildAsync(); + header -> HttpClient.newHttpClient() + .newWebSocketBuilder() + .header(header, "value") + .buildAsync(VALID_URI, listener()); headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h))); } @@ -134,38 +127,38 @@ public class BuildingWebSocketTest { @Test(dataProvider = "badSubprotocols") public void illegalSubprotocolsSyntax(String s) { WebSocket.Builder b = HttpClient.newHttpClient() - .newWebSocketBuilder(URI.create("ws://websocket.example.com"), - listener()); - b.subprotocols(s); - assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync()); + .newWebSocketBuilder() + .subprotocols(s); + assertCompletesExceptionally(IllegalArgumentException.class, + b.buildAsync(VALID_URI, listener())); } @Test(dataProvider = "duplicatingSubprotocols") public void illegalSubprotocolsDuplicates(String mostPreferred, String[] lesserPreferred) { WebSocket.Builder b = HttpClient.newHttpClient() - .newWebSocketBuilder(URI.create("ws://websocket.example.com"), - listener()); - b.subprotocols(mostPreferred, lesserPreferred); - assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync()); + .newWebSocketBuilder() + .subprotocols(mostPreferred, lesserPreferred); + assertCompletesExceptionally(IllegalArgumentException.class, + b.buildAsync(VALID_URI, listener())); } @Test(dataProvider = "badConnectTimeouts") public void illegalConnectTimeout(Duration d) { WebSocket.Builder b = HttpClient.newHttpClient() - .newWebSocketBuilder(URI.create("ws://websocket.example.com"), - listener()); - b.connectTimeout(d); - assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync()); + .newWebSocketBuilder() + .connectTimeout(d); + assertCompletesExceptionally(IllegalArgumentException.class, + b.buildAsync(VALID_URI, listener())); } @DataProvider public Object[][] badURIs() { return new Object[][]{ - {"http://example.com"}, - {"ftp://example.com"}, - {"wss://websocket.example.com/hello#fragment"}, - {"ws://websocket.example.com/hello#fragment"}, + {URI.create("http://example.com")}, + {URI.create("ftp://example.com")}, + {URI.create("wss://websocket.example.com/hello#fragment")}, + {URI.create("ws://websocket.example.com/hello#fragment")}, }; } @@ -193,6 +186,7 @@ public class BuildingWebSocketTest { @DataProvider public static Object[][] badSubprotocols() { return new Object[][]{ + {""}, {new String("")}, {"round-brackets("}, {"round-brackets)"}, diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java deleted file mode 100644 index 7e15e794a5d..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ - -package jdk.incubator.http.internal.websocket; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import jdk.incubator.http.WebSocket; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.StandardCharsets; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect; -import static jdk.incubator.http.internal.websocket.TestSupport.cartesianIterator; -import static java.util.Arrays.asList; -import static java.util.List.of; - -/* - * Tests for Close message handling: examines sendClose/onClose contracts. - */ -public final class CloseTest { - - /* - * Verifies the domain of the arguments of sendClose(code, reason). - */ - @Test(dataProvider = "sendClose") - public void testSendCloseArguments(int code, String reason) { - WebSocket ws = newWebSocket(); - ifExpect( - reason == null, - NullPointerException.class::isInstance) - .orExpect( - !isOutgoingCodeLegal(code), - IllegalArgumentException.class::isInstance) - .orExpect( - !isReasonLegal(reason), - IllegalArgumentException.class::isInstance) - .assertThrows(() -> ws.sendClose(code, reason)); - } - - /* - * After sendClose(code, reason) has returned normally or exceptionally, no - * more messages can be sent. However, if the invocation has thrown IAE/NPE - * (i.e. programming error) messages can still be sent (failure atomicity). - */ - public void testSendClose(int code, String reason) { - newWebSocket().sendClose(10, ""); - } - - /* - * After sendClose() has been invoked, no more messages can be sent. - */ - public void testSendClose() { - WebSocket ws = newWebSocket(); - CompletableFuture<WebSocket> cf = ws.sendClose(); - } - - // TODO: sendClose can be invoked whenever is suitable without ISE - // + idempotency - - /* - * An invocation of sendClose(code, reason) will cause a Close message with - * the same code and the reason to appear on the wire. - */ - public void testSendCloseWysiwyg(int code, String reason) { - - } - - /* - * An invocation of sendClose() will cause an empty Close message to appear - * on the wire. - */ - public void testSendCloseWysiwyg() { - - } - - /* - * Automatic Closing handshake. Listener receives onClose() and returns from - * it. WebSocket closes in accordance to the returned value. - */ - public void testClosingHandshake1() { - // TODO: closed if observed shortly after the returned CS completes - } - - /* - * sendClose is invoked from within onClose. After sendClose has returned, - * isClosed() reports true. - */ - public void testClosingHandshake2() { - // 1. newWebSocket().sendClose(); - // 2. onClose return null - // 3. isClosed() == true - } - - /* - * sendClose has been invoked, then onClose. Shortly after onClose has - * returned, isClosed reports true. - */ - public void testClosingHandshake3() { - } - - /* - * Return from onClose with nevercompleting CS then sendClose(). - */ - public void testClosingHandshake4() { - - } - - /* - * Exceptions thrown from onClose and exceptions a CS returned from onClose - * "completes exceptionally" with are ignored. In other words, they are - * never reported to onError(). - */ - public void testOnCloseExceptions() { - - } - - /* - * An incoming Close message on the wire will cause an invocation of onClose - * with appropriate values. However, if this message violates the WebSocket - * Protocol, onError is invoked instead. - * - * // TODO: automatic close (if error) AND isClose returns true from onError - */ - public void testOnCloseWysiwyg() { - - } - - /* - * Data is read off the wire. An end-of-stream has been reached while - * reading a frame. - * - * onError is invoked with java.net.ProtocolException and the WebSocket this - * listener has been attached to - */ - public void testUnexpectedEOS() { - - } - - /* - * Data is read off the wire. An end-of-stream has been reached in between - * frames, and no Close frame has been received yet. - * - * onClose is invoked with the status code 1006 and the WebSocket this - * listener has been attached to - */ - public void testEOS() { - - } - - // TODO: check buffers for change - - @DataProvider(name = "sendClose") - public Iterator<Object[]> createData() { - List<Integer> codes = asList( - Integer.MIN_VALUE, -1, 0, 1, 500, 998, 999, 1000, 1001, 1002, - 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, - 1013, 1014, 1015, 1016, 2998, 2999, 3000, 3001, 3998, 3999, - 4000, 4001, 4998, 4999, 5000, 5001, 32768, 65535, 65536, - Integer.MAX_VALUE); - String longReason1 = "This is a reason string. Nothing special except " + - "its UTF-8 representation is a bit " + - "longer than one hundred and twenty three bytes."; - assert longReason1.getBytes(StandardCharsets.UTF_8).length > 123; - - // Russian alphabet repeated cyclically until it's enough to pass "123" - // bytes length - StringBuilder b = new StringBuilder(); - char c = '\u0410'; - for (int i = 0; i < 62; i++) { - b.append(c); - if (++c > '\u042F') { - c = '\u0410'; - } - } - String longReason2 = b.toString(); - assert longReason2.length() <= 123 - && longReason2.getBytes(StandardCharsets.UTF_8).length > 123; - - String malformedReason = new String(new char[]{0xDC00, 0xD800}); - - List<String> reasons = asList - (null, "", "abc", longReason1, longReason2, malformedReason); - - return cartesianIterator(of(codes, reasons), args -> args); - } - - private boolean isReasonLegal(String reason) { - if (reason == null) { - return false; - } - ByteBuffer result; - try { - result = StandardCharsets.UTF_8.newEncoder().encode(CharBuffer.wrap(reason)); - } catch (CharacterCodingException e) { - return false; - } - return result.remaining() <= 123; - } - - private static boolean isOutgoingCodeLegal(int code) { - if (code < 1000 || code > 4999) { - return false; - } - if (code < 1016) { - return code == 1000 || code == 1001 || code == 1008 || code == 1011; - } - return code >= 3000; - } - - private WebSocket newWebSocket() { - WebSocket.Listener l = new WebSocket.Listener() { }; - return new WebSocketImpl(URI.create("ws://example.com"), - "", - new MockChannel.Builder().build(), - l); - } -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java deleted file mode 100644 index a0b49e67c50..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ -package jdk.incubator.http.internal.websocket; - -import org.testng.annotations.DataProvider; - -import jdk.incubator.http.internal.websocket.TestSupport.F5; -import java.nio.ByteBuffer; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Stream; - -import static jdk.incubator.http.internal.websocket.TestSupport.cartesianIterator; -import static jdk.incubator.http.internal.websocket.TestSupport.concat; -import static jdk.incubator.http.internal.websocket.TestSupport.iteratorOf; -import static jdk.incubator.http.internal.websocket.TestSupport.iteratorOf1; -import static java.util.List.of; - -/* - * Data providers for WebSocket tests - */ -public final class DataProviders { - - /* - * Various ByteBuffer-s to be passed to sendPing/sendPong. - * - * Actual data is put in the middle of the buffer to make sure the code under - * test relies on position/limit rather than on 0 and capacity. - * - * +-------------------+-------~ ~-------------+--------------+ - * |<---- leading ---->|<------~ ~--- data --->|<- trailing ->| - * +-------------------+-------~ ~-------------+--------------+ - * ^0 ^position ^limit ^capacity - */ - @DataProvider(name = "outgoingData", parallel = true) - public static Iterator<Object[]> outgoingData() { - List<Integer> leading = of(0, 1, 17, 125); - List<Integer> trailing = of(0, 1, 19, 123); - List<Integer> sizes = of(0, 1, 2, 17, 32, 64, 122, 123, 124, 125, 126, 127, 128, 256); - List<Boolean> direct = of(true, false); - List<Boolean> readonly = of(false); // TODO: return readonly (true) - F5<Integer, Integer, Integer, Boolean, Boolean, Object[]> f = - (l, t, s, d, r) -> { - ByteBuffer b; - if (d) { - b = ByteBuffer.allocateDirect(l + t + s); - } else { - b = ByteBuffer.allocate(l + t + s); - } - fill(b); - if (r) { - b = b.asReadOnlyBuffer(); - } - b.position(l).limit(l + s); - return new ByteBuffer[]{b}; - }; - Iterator<Object[]> product = cartesianIterator(leading, trailing, sizes, direct, readonly, f); - Iterator<Object[]> i = iteratorOf1(new Object[]{null}); - return concat(iteratorOf(i, product)); - } - - @DataProvider(name = "incomingData", parallel = true) - public static Iterator<Object[]> incomingData() { - return Stream.of(0, 1, 2, 17, 63, 125) - .map(i -> new Object[]{fill(ByteBuffer.allocate(i))}) - .iterator(); - } - - @DataProvider(name = "incorrectFrame") - public static Iterator<Object[]> incorrectFrame() { - List<Boolean> fin = of(true, false ); - List<Boolean> rsv1 = of(true, false ); - List<Boolean> rsv2 = of(true, false ); - List<Boolean> rsv3 = of(true, false ); - List<Integer> sizes = of(0, 126, 1024); - return cartesianIterator(fin, rsv1, rsv2, rsv3, sizes, - (a, b, c, d, e) -> new Object[]{a, b, c, d, ByteBuffer.allocate(e)}); - } - - private static ByteBuffer fill(ByteBuffer b) { - int i = 0; - while (b.hasRemaining()) { - b.put((byte) (++i & 0xff)); - } - return b; - } -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java index 0ad2f06940c..62f550b29f3 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java index a5fc20a7303..7d6e03f9314 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java deleted file mode 100644 index 03a94a0a77d..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ -package jdk.incubator.http.internal.websocket; - -import jdk.incubator.http.WebSocket.MessagePart; -import jdk.incubator.http.internal.websocket.Frame.Opcode; -import jdk.incubator.http.internal.websocket.TestSupport.F1; -import jdk.incubator.http.internal.websocket.TestSupport.F2; -import jdk.incubator.http.internal.websocket.TestSupport.InvocationChecker; -import jdk.incubator.http.internal.websocket.TestSupport.InvocationExpectation; -import jdk.incubator.http.internal.websocket.TestSupport.Mock; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.SelectionKey; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.OptionalInt; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -import static jdk.incubator.http.internal.websocket.Frame.MAX_HEADER_SIZE_BYTES; - -final class MockChannel implements RawChannel, Mock { - - /* Reads and writes must be able to be served concurrently, thus 2 threads */ // TODO: test this - private final Executor executor = Executors.newFixedThreadPool(2); - private final Object stateLock = new Object(); - private final Object readLock = new Object(); - private final Object writeLock = new Object(); - private volatile boolean closed; - private boolean isInputOpen = true; - private boolean isOutputOpen = true; - private final Frame.Reader reader = new Frame.Reader(); - private final MockFrameConsumer delegate; - private final Iterator<ReadRule> readScenario; - private ReadRule currentRule; - private final AtomicBoolean handedOver = new AtomicBoolean(); - - private MockChannel(Iterable<ReadRule> scenario, - Iterable<InvocationExpectation> expectations) { - Iterator<ReadRule> iterator = scenario.iterator(); - if (!iterator.hasNext()) { - throw new RuntimeException(); - } - this.readScenario = iterator; - this.currentRule = iterator.next(); - this.delegate = new MockFrameConsumer(expectations); - } - - @Override - public void registerEvent(RawEvent event) throws IOException { - int ops = event.interestOps(); - if ((ops & SelectionKey.OP_WRITE) != 0) { - synchronized (stateLock) { - checkOpen(); - executor.execute(event::handle); - } - } else if ((ops & SelectionKey.OP_READ) != 0) { - CompletionStage<?> cs; - synchronized (readLock) { - cs = currentRule().whenReady(); - synchronized (stateLock) { - checkOpen(); - cs.thenRun(() -> executor.execute(event::handle)); - } - } - } else { - throw new RuntimeException("Unexpected registration: " + ops); - } - } - - @Override - public ByteBuffer initialByteBuffer() throws IllegalStateException { - if (!handedOver.compareAndSet(false, true)) { - throw new IllegalStateException(); - } - return ByteBuffer.allocate(0); - } - - @Override - public ByteBuffer read() throws IOException { - synchronized (readLock) { - checkOpen(); - synchronized (stateLock) { - if (!isInputOpen) { - return null; - } - } - ByteBuffer r = currentRule().read(); - checkOpen(); - return r; - } - } - - @Override - public long write(ByteBuffer[] src, int offset, int len) throws IOException { - synchronized (writeLock) { - checkOpen(); - synchronized (stateLock) { - if (!isOutputOpen) { - throw new ClosedChannelException(); - } - } - long n = 0; - for (int i = offset; i < offset + len && isOpen(); i++) { - ByteBuffer b = src[i]; - int rem = src[i].remaining(); - while (b.hasRemaining() && isOpen()) { - reader.readFrame(b, delegate); - } - n += rem; - } - checkOpen(); - return n; - } - } - - public boolean isOpen() { - return !closed; - } - - @Override - public void shutdownInput() throws IOException { - synchronized (stateLock) { - if (!isOpen()) { - throw new ClosedChannelException(); - } - isInputOpen = false; - } - } - - @Override - public void shutdownOutput() throws IOException { - synchronized (stateLock) { - if (!isOpen()) { - throw new ClosedChannelException(); - } - isOutputOpen = false; - } - } - - @Override - public void close() { - synchronized (stateLock) { - closed = true; - } - } - - @Override - public String toString() { - return super.toString() + "[" + (closed ? "closed" : "open") + "]"; - } - - private ReadRule currentRule() { - assert Thread.holdsLock(readLock); - while (!currentRule.applies()) { // There should be the terminal rule which always applies - currentRule = readScenario.next(); - } - return currentRule; - } - - private void checkOpen() throws ClosedChannelException { - if (!isOpen()) { - throw new ClosedChannelException(); - } - } - - @Override - public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { - return delegate.expectations(timeout, unit); - } - - private static class MockFrameConsumer extends FrameConsumer implements Mock { - - private final Frame.Masker masker = new Frame.Masker(); - - MockFrameConsumer(Iterable<InvocationExpectation> expectations) { - super(new MockMessageStreamConsumer(expectations)); - } - - @Override - public void mask(boolean value) { - } - - @Override - public void maskingKey(int value) { - masker.mask(value); - } - - @Override - public void payloadData(ByteBuffer data) { - int p = data.position(); - int l = data.limit(); - masker.transferMasking(data, data); -// select(p, l, data); FIXME - super.payloadData(data); - } - - @Override - public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { - return ((Mock) getOutput()).expectations(timeout, unit); - } - } - - private static final class MockMessageStreamConsumer implements MessageStreamConsumer, Mock { - - private final InvocationChecker checker; - - MockMessageStreamConsumer(Iterable<InvocationExpectation> expectations) { - checker = new InvocationChecker(expectations); - } - - @Override - public void onText(MessagePart part, CharSequence data) { - checker.checkInvocation("onText", part, data); - } - - @Override - public void onBinary(MessagePart part, ByteBuffer data) { - checker.checkInvocation("onBinary", part, data); - } - - @Override - public void onPing(ByteBuffer data) { - checker.checkInvocation("onPing", data); - } - - @Override - public void onPong(ByteBuffer data) { - checker.checkInvocation("onPong", data); - } - - @Override - public void onClose(OptionalInt statusCode, CharSequence reason) { - checker.checkInvocation("onClose", statusCode, reason); - } - - @Override - public void onError(Exception e) { - checker.checkInvocation("onError", e); - } - - @Override - public void onComplete() { - checker.checkInvocation("onComplete"); - } - - @Override - public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { - return checker.expectations(timeout, unit); - } - } - - public static final class Builder { - - private final Frame.HeaderWriter b = new Frame.HeaderWriter(); - private final List<InvocationExpectation> expectations = new LinkedList<>(); - private final List<ReadRule> scenario = new LinkedList<>(); - - Builder expectPing(F1<? super ByteBuffer, Boolean> predicate) { - InvocationExpectation e = new InvocationExpectation("onPing", - args -> predicate.apply((ByteBuffer) args[0])); - expectations.add(e); - return this; - } - - Builder expectPong(F1<? super ByteBuffer, Boolean> predicate) { - InvocationExpectation e = new InvocationExpectation("onPong", - args -> predicate.apply((ByteBuffer) args[0])); - expectations.add(e); - return this; - } - - Builder expectClose(F2<? super Integer, ? super String, Boolean> predicate) { - InvocationExpectation e = new InvocationExpectation("onClose", - args -> predicate.apply((Integer) args[0], (String) args[1])); - expectations.add(e); - return this; - } - - Builder provideFrame(boolean fin, boolean rsv1, boolean rsv2, - boolean rsv3, Opcode opcode, ByteBuffer data) { - - ByteBuffer b = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES + data.remaining()); - this.b.fin(fin).rsv1(rsv1).rsv2(rsv2).rsv3(rsv3).opcode(opcode).noMask() - .payloadLen(data.remaining()).write(b); - - int p = data.position(); - int l = data.limit(); - b.put(data); - b.flip(); -// select(p, l, data); FIXME - - ReadRule r = new ReadRule() { - - private volatile boolean provided; - - @Override - public CompletionStage<?> whenReady() { - return NOW; - } - - @Override - public ByteBuffer read() throws IOException { - provided = true; - return data; - } - - @Override - public boolean applies() { - return !provided; - } - }; - scenario.add(r); - return this; - } - - Builder provideEos() { - ReadRule r = new ReadRule() { - - @Override - public CompletionStage<?> whenReady() { - return NOW; - } - - @Override - public ByteBuffer read() throws IOException { - return null; - } - - @Override - public boolean applies() { - return true; - } - }; - scenario.add(r); - return this; - } - - Builder provideException(Supplier<? extends IOException> s) { - return this; - } - - MockChannel build() { - LinkedList<ReadRule> scenario = new LinkedList<>(this.scenario); - scenario.add(new Terminator()); - return new MockChannel(scenario, new LinkedList<>(expectations)); - } - } - - private interface ReadRule { - - /* - * Returns a CS which when completed means `read(ByteBuffer dst)` can be - * invoked - */ - CompletionStage<?> whenReady(); - - ByteBuffer read() throws IOException; - - /* - * Returns true if this rule still applies, otherwise returns false - */ - boolean applies(); - } - - public static final class Terminator implements ReadRule { - - @Override - public CompletionStage<?> whenReady() { - return NEVER; - } - - @Override - public ByteBuffer read() { - return ByteBuffer.allocate(0); - } - - @Override - public boolean applies() { - return true; - } - } - - private static final CompletionStage<?> NOW = CompletableFuture.completedStage(null); - private static final CompletionStage<?> NEVER = new CompletableFuture(); -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java deleted file mode 100644 index 5b64ab64552..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ -package jdk.incubator.http.internal.websocket; - -import org.testng.annotations.Test; -import jdk.incubator.http.internal.websocket.Frame.Opcode; - -import java.io.IOException; -import jdk.incubator.http.internal.websocket.TestSupport.AssertionFailedException; - -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; - -import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows; -import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations; -import static jdk.incubator.http.internal.websocket.Frame.MAX_HEADER_SIZE_BYTES; - -public final class MockChannelTest { - - // TODO: tests for read (stubbing) - - @Test - public void testPass01() { - MockChannel ch = new MockChannel.Builder().build(); - checkExpectations(1, TimeUnit.SECONDS, ch); - } - - @Test - public void testPass02() throws IOException { - int len = 8; - ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES); - ByteBuffer data = ByteBuffer.allocate(len); - new Frame.HeaderWriter() - .fin(true).opcode(Opcode.PONG).payloadLen(len).mask(0x12345678) - .write(header); - header.flip(); - MockChannel ch = new MockChannel.Builder() - .expectPong(bb -> bb.remaining() == len) - .build(); - ch.write(new ByteBuffer[]{header, data}, 0, 2); - checkExpectations(1, TimeUnit.SECONDS, ch); - } - - @Test - public void testPass03() throws IOException { - int len = 8; - ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES); - ByteBuffer data = ByteBuffer.allocate(len - 2); // not all data is written - new Frame.HeaderWriter() - .fin(true).opcode(Opcode.PONG).payloadLen(len).mask(0x12345678) - .write(header); - header.flip(); - MockChannel ch = new MockChannel.Builder().build(); // expected no invocations - ch.write(new ByteBuffer[]{header, data}, 0, 2); - checkExpectations(1, TimeUnit.SECONDS, ch); - } - - @Test - public void testFail01() { - MockChannel ch = new MockChannel.Builder() - .expectClose((code, reason) -> code == 1002 && reason.isEmpty()) - .build(); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, ch)); - } - - @Test - public void testFail02() throws IOException { - ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES); - new Frame.HeaderWriter() - .fin(true).opcode(Opcode.CLOSE).payloadLen(2).mask(0x12345678) - .write(header); - header.flip(); - ByteBuffer data = ByteBuffer.allocate(2).putChar((char) 1004).flip(); - MockChannel ch = new MockChannel.Builder() - .expectClose((code, reason) -> code == 1002 && reason.isEmpty()) - .build(); - ch.write(new ByteBuffer[]{header, data}, 0, 2); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, ch)); - } -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java index 0d09ac6abcc..9147304573b 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -20,112 +20,383 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.incubator.http.internal.websocket; -import jdk.incubator.http.internal.websocket.TestSupport.F1; -import jdk.incubator.http.internal.websocket.TestSupport.F2; -import jdk.incubator.http.internal.websocket.TestSupport.F3; -import jdk.incubator.http.internal.websocket.TestSupport.InvocationChecker; -import jdk.incubator.http.internal.websocket.TestSupport.InvocationExpectation; -import jdk.incubator.http.internal.websocket.TestSupport.Mock; import jdk.incubator.http.WebSocket; -import jdk.incubator.http.WebSocket.Listener; import jdk.incubator.http.WebSocket.MessagePart; + import java.nio.ByteBuffer; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; -final class MockListener implements Listener, Mock { +import static jdk.incubator.http.internal.websocket.TestSupport.fullCopy; - private final InvocationChecker checker; +public class MockListener implements WebSocket.Listener { - @Override - public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { - return checker.expectations(timeout, unit); - } + private final long bufferSize; + private long count; + private final List<ListenerInvocation> invocations = new ArrayList<>(); + private final CompletableFuture<?> lastCall = new CompletableFuture<>(); - public static final class Builder { - - private final List<InvocationExpectation> expectations = new LinkedList<>(); - - Builder expectOnOpen(F1<? super WebSocket, Boolean> predicate) { - InvocationExpectation e = new InvocationExpectation("onOpen", - args -> predicate.apply((WebSocket) args[0])); - expectations.add(e); - return this; + /* + * Typical buffer sizes: 1, n, Long.MAX_VALUE + */ + public MockListener(long bufferSize) { + if (bufferSize < 1) { + throw new IllegalArgumentException(); } - - Builder expectOnPing(F2<? super WebSocket, ? super ByteBuffer, Boolean> predicate) { - InvocationExpectation e = new InvocationExpectation("onPing", - args -> predicate.apply((WebSocket) args[0], (ByteBuffer) args[1])); - expectations.add(e); - return this; - } - - Builder expectOnClose(F3<? super WebSocket, ? super Integer, ? super String, Boolean> predicate) { - expectations.add(new InvocationExpectation("onClose", - args -> predicate.apply((WebSocket) args[0], (Integer) args[1], (String) args[2]))); - return this; - } - - Builder expectOnError(F2<? super WebSocket, ? super Throwable, Boolean> predicate) { - expectations.add(new InvocationExpectation("onError", - args -> predicate.apply((WebSocket) args[0], (Throwable) args[1]))); - return this; - } - - MockListener build() { - return new MockListener(new LinkedList<>(expectations)); - } - } - - private MockListener(List<InvocationExpectation> expectations) { - this.checker = new InvocationChecker(expectations); + this.bufferSize = bufferSize; } @Override public void onOpen(WebSocket webSocket) { - checker.checkInvocation("onOpen", webSocket); + System.out.printf("onOpen(%s)%n", webSocket); + invocations.add(new OnOpen(webSocket)); + onOpen0(webSocket); + } + + protected void onOpen0(WebSocket webSocket) { + replenish(webSocket); } @Override - public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, + public CompletionStage<?> onText(WebSocket webSocket, + CharSequence message, MessagePart part) { - checker.checkInvocation("onText", webSocket, message, part); + System.out.printf("onText(%s, %s, %s)%n", webSocket, message, part); + invocations.add(new OnText(webSocket, message.toString(), part)); + return onText0(webSocket, message, part); + } + + protected CompletionStage<?> onText0(WebSocket webSocket, + CharSequence message, + MessagePart part) { + replenish(webSocket); return null; } @Override - public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message, + public CompletionStage<?> onBinary(WebSocket webSocket, + ByteBuffer message, MessagePart part) { - checker.checkInvocation("onBinary", webSocket, message, part); + System.out.printf("onBinary(%s, %s, %s)%n", webSocket, message, part); + invocations.add(new OnBinary(webSocket, fullCopy(message), part)); + return onBinary0(webSocket, message, part); + } + + protected CompletionStage<?> onBinary0(WebSocket webSocket, + ByteBuffer message, + MessagePart part) { + replenish(webSocket); return null; } @Override public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) { - checker.checkInvocation("onPing", webSocket, message); + System.out.printf("onPing(%s, %s)%n", webSocket, message); + invocations.add(new OnPing(webSocket, fullCopy(message))); + return onPing0(webSocket, message); + } + + protected CompletionStage<?> onPing0(WebSocket webSocket, ByteBuffer message) { + replenish(webSocket); return null; } @Override public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) { - checker.checkInvocation("onPong", webSocket, message); + System.out.printf("onPong(%s, %s)%n", webSocket, message); + invocations.add(new OnPong(webSocket, fullCopy(message))); + return onPong0(webSocket, message); + } + + protected CompletionStage<?> onPong0(WebSocket webSocket, ByteBuffer message) { + replenish(webSocket); return null; } @Override - public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, + public CompletionStage<?> onClose(WebSocket webSocket, + int statusCode, String reason) { - checker.checkInvocation("onClose", webSocket, statusCode, reason); + System.out.printf("onClose(%s, %s, %s)%n", webSocket, statusCode, reason); + invocations.add(new OnClose(webSocket, statusCode, reason)); + lastCall.complete(null); return null; } @Override public void onError(WebSocket webSocket, Throwable error) { - checker.checkInvocation("onError", webSocket, error); + System.out.printf("onError(%s, %s)%n", webSocket, error); + invocations.add(new OnError(webSocket, error == null ? null : error.getClass())); + lastCall.complete(null); + } + + public CompletableFuture<?> onCloseOrOnErrorCalled() { + return lastCall.copy(); + } + + protected void replenish(WebSocket webSocket) { + if (--count <= 0) { + count = bufferSize - bufferSize / 2; + } + webSocket.request(count); + } + + public List<ListenerInvocation> invocations() { + return new ArrayList<>(invocations); + } + + public abstract static class ListenerInvocation { + + public static OnOpen onOpen(WebSocket webSocket) { + return new OnOpen(webSocket); + } + + public static OnText onText(WebSocket webSocket, + String text, + MessagePart part) { + return new OnText(webSocket, text, part); + } + + public static OnBinary onBinary(WebSocket webSocket, + ByteBuffer data, + MessagePart part) { + return new OnBinary(webSocket, data, part); + } + + public static OnPing onPing(WebSocket webSocket, + ByteBuffer data) { + return new OnPing(webSocket, data); + } + + public static OnPong onPong(WebSocket webSocket, + ByteBuffer data) { + return new OnPong(webSocket, data); + } + + public static OnClose onClose(WebSocket webSocket, + int statusCode, + String reason) { + return new OnClose(webSocket, statusCode, reason); + } + + public static OnError onError(WebSocket webSocket, + Class<? extends Throwable> clazz) { + return new OnError(webSocket, clazz); + } + + final WebSocket webSocket; + + private ListenerInvocation(WebSocket webSocket) { + this.webSocket = webSocket; + } + } + + public static final class OnOpen extends ListenerInvocation { + + public OnOpen(WebSocket webSocket) { + super(webSocket); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ListenerInvocation that = (ListenerInvocation) o; + return Objects.equals(webSocket, that.webSocket); + } + + @Override + public int hashCode() { + return Objects.hashCode(webSocket); + } + } + + public static final class OnText extends ListenerInvocation { + + final String text; + final MessagePart part; + + public OnText(WebSocket webSocket, String text, MessagePart part) { + super(webSocket); + this.text = text; + this.part = part; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnText onText = (OnText) o; + return Objects.equals(text, onText.text) && + part == onText.part && + Objects.equals(webSocket, onText.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(text, part, webSocket); + } + + @Override + public String toString() { + return String.format("onText(%s, %s, %s)", webSocket, text, part); + } + } + + public static final class OnBinary extends ListenerInvocation { + + final ByteBuffer data; + final MessagePart part; + + public OnBinary(WebSocket webSocket, ByteBuffer data, MessagePart part) { + super(webSocket); + this.data = data; + this.part = part; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnBinary onBinary = (OnBinary) o; + return Objects.equals(data, onBinary.data) && + part == onBinary.part && + Objects.equals(webSocket, onBinary.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(data, part, webSocket); + } + + @Override + public String toString() { + return String.format("onBinary(%s, %s, %s)", webSocket, data, part); + } + } + + public static final class OnPing extends ListenerInvocation { + + final ByteBuffer data; + + public OnPing(WebSocket webSocket, ByteBuffer data) { + super(webSocket); + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnPing onPing = (OnPing) o; + return Objects.equals(data, onPing.data) && + Objects.equals(webSocket, onPing.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(data, webSocket); + } + + @Override + public String toString() { + return String.format("onPing(%s, %s)", webSocket, data); + } + } + + public static final class OnPong extends ListenerInvocation { + + final ByteBuffer data; + + public OnPong(WebSocket webSocket, ByteBuffer data) { + super(webSocket); + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnPong onPong = (OnPong) o; + return Objects.equals(data, onPong.data) && + Objects.equals(webSocket, onPong.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(data, webSocket); + } + + @Override + public String toString() { + return String.format("onPong(%s, %s)", webSocket, data); + } + } + + public static final class OnClose extends ListenerInvocation { + + final int statusCode; + final String reason; + + public OnClose(WebSocket webSocket, int statusCode, String reason) { + super(webSocket); + this.statusCode = statusCode; + this.reason = reason; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnClose onClose = (OnClose) o; + return statusCode == onClose.statusCode && + Objects.equals(reason, onClose.reason) && + Objects.equals(webSocket, onClose.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(statusCode, reason, webSocket); + } + + @Override + public String toString() { + return String.format("onClose(%s, %s, %s)", webSocket, statusCode, reason); + } + } + + public static final class OnError extends ListenerInvocation { + + final Class<? extends Throwable> clazz; + + public OnError(WebSocket webSocket, Class<? extends Throwable> clazz) { + super(webSocket); + this.clazz = clazz; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OnError onError = (OnError) o; + return Objects.equals(clazz, onError.clazz) && + Objects.equals(webSocket, onError.webSocket); + } + + @Override + public int hashCode() { + return Objects.hash(clazz, webSocket); + } + + @Override + public String toString() { + return String.format("onError(%s, %s)", webSocket, clazz); + } } } diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java deleted file mode 100644 index 33a2a1ff600..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ -package jdk.incubator.http.internal.websocket; - -import org.testng.annotations.Test; - -import jdk.incubator.http.internal.websocket.TestSupport.AssertionFailedException; -import java.util.concurrent.TimeUnit; - -import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows; -import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations; - -public class MockListenerTest { - - @Test - public void testPass01() { - MockListener l = new MockListener.Builder().build(); - checkExpectations(1, TimeUnit.SECONDS, l); - } - - @Test - public void testPass02() { - MockListener l = new MockListener.Builder() - .expectOnOpen(ws -> ws == null) - .build(); - l.onOpen(null); - checkExpectations(1, TimeUnit.SECONDS, l); - } - - @Test - public void testPass03() { - MockListener l = new MockListener.Builder() - .expectOnOpen(ws -> ws == null) - .expectOnClose((ws, code, reason) -> - ws == null && code == 1002 && "blah".equals(reason)) - .build(); - l.onOpen(null); - l.onClose(null, 1002, "blah"); - checkExpectations(1, TimeUnit.SECONDS, l); - } - - @Test - public void testFail01() { - MockListener l = new MockListener.Builder() - .expectOnOpen(ws -> ws != null) - .build(); - l.onOpen(null); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, l)); - } - - @Test - public void testFail02() { - MockListener l = new MockListener.Builder() - .expectOnOpen(ws -> true) - .build(); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, l)); - } - - @Test - public void testFail03() { - MockListener l = new MockListener.Builder() - .expectOnOpen(ws -> true) - .build(); - l.onOpen(null); - l.onClose(null, 1002, ""); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, l)); - } - - @Test - public void testFail04() { - MockListener l = new MockListener.Builder().build(); - l.onClose(null, 1002, ""); - assertThrows(AssertionFailedException.class, - () -> checkExpectations(1, TimeUnit.SECONDS, l)); - } -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java new file mode 100644 index 00000000000..5129847d2dd --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.websocket; + +import jdk.incubator.http.internal.common.Pair; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; + +public class MockReceiver extends Receiver { + + private final Iterator<Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>>> iterator; + private final MessageStreamConsumer consumer; + + public MockReceiver(MessageStreamConsumer consumer, RawChannel channel, + Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>>... pairs) { + super(consumer, channel); + this.consumer = consumer; + iterator = Arrays.asList(pairs).iterator(); + } + + @Override + protected SequentialScheduler createScheduler() { + class X { // Class is hack needed to allow the task to refer to the scheduler + SequentialScheduler scheduler = new SequentialScheduler(task()); + + SequentialScheduler.RestartableTask task() { + return new SequentialScheduler.RestartableTask() { + @Override + public void run(DeferredCompleter taskCompleter) { + if (!scheduler.isStopped() && !demand.isFulfilled()) { + if (!iterator.hasNext()) { + taskCompleter.complete(); + return; + } + Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>> p = iterator.next(); + CompletableFuture<?> cf = p.first.toCompletableFuture(); + if (cf.isDone()) { // Forcing synchronous execution + p.second.accept(consumer); + repeat(taskCompleter); + } else { + cf.whenCompleteAsync((r, e) -> { + p.second.accept(consumer); + repeat(taskCompleter); + }); + } + } else { + taskCompleter.complete(); + } + } + + private void repeat(DeferredCompleter taskCompleter) { + taskCompleter.complete(); + scheduler.runOrSchedule(); + } + }; + } + } + return new X().scheduler; + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java new file mode 100644 index 00000000000..c65ec585dca --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.websocket; + +import java.util.Queue; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Consumer; + +public abstract class MockTransmitter extends Transmitter { + + private final long startTime = System.currentTimeMillis(); + + private final Queue<OutgoingMessage> messages = new ConcurrentLinkedQueue<>(); + + public MockTransmitter() { + super(null); + } + + @Override + public void send(OutgoingMessage message, + Consumer<Exception> completionHandler) { + System.out.printf("[%6s ms.] begin send(%s)%n", + System.currentTimeMillis() - startTime, + message); + messages.add(message); + whenSent().whenComplete((r, e) -> { + System.out.printf("[%6s ms.] complete send(%s)%n", + System.currentTimeMillis() - startTime, + message); + if (e != null) { + completionHandler.accept((Exception) e); + } else { + completionHandler.accept(null); + } + }); + System.out.printf("[%6s ms.] end send(%s)%n", + System.currentTimeMillis() - startTime, + message); + } + + @Override + public void close() { } + + protected abstract CompletionStage<?> whenSent(); + + public Queue<OutgoingMessage> queue() { + return messages; + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java new file mode 100644 index 00000000000..bdbf8b1ac27 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.websocket; + +import java.nio.ByteBuffer; + +public class MockTransport extends TransportSupplier { + + public MockTransport() { + super(new NullRawChannel()); + } + + public static class NullRawChannel implements RawChannel { + + @Override + public void registerEvent(RawEvent event) { + throw new UnsupportedOperationException(); + } + + @Override + public ByteBuffer initialByteBuffer() { + return ByteBuffer.allocate(0); + } + + @Override + public ByteBuffer read() { + throw new UnsupportedOperationException(); + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) { + throw new UnsupportedOperationException(); + } + + @Override + public void shutdownInput() { + } + + @Override + public void shutdownOutput() { + } + + @Override + public void close() { + } + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java deleted file mode 100644 index 183ebb8d9c5..00000000000 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ -package jdk.incubator.http.internal.websocket; - -import org.testng.SkipException; -import org.testng.annotations.Test; -import jdk.incubator.http.internal.websocket.Frame.Opcode; - -import java.io.IOException; -import java.net.ProtocolException; -import jdk.incubator.http.WebSocket; -import java.nio.ByteBuffer; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect; -import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally; -import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations; -import static org.testng.Assert.assertSame; - -/* - * Examines sendPing/onPing contracts - */ -public final class PingTest { - - /* - * sendPing(message) is invoked. If the `message` argument is illegal, then - * the method must throw an appropriate exception. Otherwise no exception - * must be thrown. - */ -// @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) - public void testSendPingArguments(ByteBuffer message) { - WebSocket ws = newWebSocket(); - ifExpect( - message == null, - NullPointerException.class::isInstance) - .orExpect( - message != null && message.remaining() > 125, - IllegalArgumentException.class::isInstance) - .assertThrows( - () -> ws.sendPing(message) - ); - } - - /* - * sendPing(message) with a legal argument has been invoked, then: - * - * 1. A Ping message with the same payload appears on the wire - * 2. The CF returned from the method completes normally with the same - * WebSocket that sendPing has been called on - */ - @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class) - public void testSendPingWysiwyg(ByteBuffer message) throws ExecutionException, InterruptedException { - if (message == null || message.remaining() > 125) { - return; - } - ByteBuffer snapshot = copy(message); - MockChannel channel = new MockChannel.Builder() - .expectPing(snapshot::equals) - .build(); - WebSocket ws = newWebSocket(channel); - CompletableFuture<WebSocket> cf = ws.sendPing(message); - WebSocket ws1 = cf.join(); - assertSame(ws1, ws); // (2) - checkExpectations(channel); // (1) - } - - /* - * If an I/O error occurs while Ping messages is being sent, then: - * - * 1. The CF returned from sendPing completes exceptionally with this I/O - * error as the cause - */ -// @Test - public void testSendPingIOException() { - MockChannel ch = new MockChannel.Builder() -// .provideWriteException(IOException::new) - .build(); - WebSocket ws = newWebSocket(ch); - CompletableFuture<WebSocket> cf = ws.sendPing(ByteBuffer.allocate(16)); - assertCompletesExceptionally(IOException.class, cf); - } - - /* - * If an incorrect Ping frame appears on the wire, then: - * - * 1. onError with the java.net.ProtocolException is invoked - * 1. A Close frame with status code 1002 appears on the wire - */ -// @Test(dataProvider = "incorrectFrame", dataProviderClass = DataProviders.class) - public void testOnPingIncorrect(boolean fin, boolean rsv1, boolean rsv2, - boolean rsv3, ByteBuffer data) { - if (fin && !rsv1 && !rsv2 && !rsv3 && data.remaining() <= 125) { - throw new SkipException("Correct frame"); - } - CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); - MockChannel channel = new MockChannel.Builder() - .provideFrame(fin, rsv1, rsv2, rsv3, Opcode.PING, data) - .expectClose((code, reason) -> - Integer.valueOf(1002).equals(code) && "".equals(reason)) - .build(); - MockListener listener = new MockListener.Builder() - .expectOnOpen((ws) -> true) - .expectOnError((ws, error) -> error instanceof ProtocolException) - .build(); - webSocket.complete(newWebSocket(channel, listener)); - checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); - } - - /* - * If a Ping message has been read off the wire, then: - * - * 1. onPing is invoked with the data and the WebSocket the listener has - * been attached to - * 2. A Pong message with the same contents will be sent in reply - */ - @Test(dataProvider = "incomingData", dataProviderClass = DataProviders.class) - public void testOnPingReply(ByteBuffer data) { - CompletableFuture<WebSocket> webSocket = new CompletableFuture<>(); - MockChannel channel = new MockChannel.Builder() - .provideFrame(true, false, false, false, Opcode.PING, data) - .expectPong(data::equals) - .build(); - MockListener listener = new MockListener.Builder() - .expectOnOpen((ws) -> true) // maybe should capture with a CF? - .expectOnPing((ws, bb) -> data.equals(bb)) - .build(); - webSocket.complete(newWebSocket(channel, listener)); - checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener); - } - - /* - * If onPing throws an exception or CS returned from it completes - * exceptionally, then: - * - * 1. onError is invoked with this particular exception as the cause and the - * WebSocket the listener has been attached to - */ - public void testOnPingExceptions() { - } - - /* - * If a Ping message has been read off the wire and an I/O error occurs - * while WebSocket sends a Pong reply to it, then: - * - * 1. onError is invoked with this error as the cause and the WebSocket this - * listener has been attached to - */ - public void testOnPingReplyIOException() { - } - - private WebSocket newWebSocket() { - return newWebSocket(new MockChannel.Builder().build()); - } - - private WebSocket newWebSocket(RawChannel ch) { - return newWebSocket(ch, new WebSocket.Listener() { }); - } - - private WebSocket newWebSocket(RawChannel ch, WebSocket.Listener l) { -// WebSocketImpl ws = new WebSocketImpl("", ch, l, Executors.newCachedThreadPool()); -// ws.(); -// ws.request(Long.MAX_VALUE); - return null; // FIXME - } - - public static ByteBuffer copy(ByteBuffer src) { - int pos = src.position(); - ByteBuffer b = ByteBuffer.allocate(src.remaining()).put(src).flip(); - src.position(pos); - return b; - } -} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java index 0602860e39b..97cfe962137 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java new file mode 100644 index 00000000000..4a4858b651a --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.websocket; + +import jdk.incubator.http.WebSocket; +import org.testng.annotations.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; + +import static java.util.concurrent.CompletableFuture.completedStage; +import static jdk.incubator.http.WebSocket.MessagePart.FIRST; +import static jdk.incubator.http.WebSocket.MessagePart.LAST; +import static jdk.incubator.http.WebSocket.MessagePart.PART; +import static jdk.incubator.http.WebSocket.MessagePart.WHOLE; +import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE; +import static jdk.incubator.http.internal.common.Pair.pair; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onClose; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onError; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onOpen; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onPing; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onPong; +import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onText; +import static org.testng.Assert.assertEquals; + +public class ReceivingTest { + + // TODO: request in onClose/onError + // TODO: throw exception in onClose/onError + // TODO: exception is thrown from request() + + @Test + public void testNonPositiveRequest() throws Exception { + MockListener listener = new MockListener(Long.MAX_VALUE) { + @Override + protected void onOpen0(WebSocket webSocket) { + webSocket.request(0); + } + }; + MockTransport transport = new MockTransport() { + @Override + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new MockReceiver(consumer, channel, pair(now(), m -> m.onText("1", WHOLE))); + } + }; + WebSocket ws = newInstance(listener, transport); + listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS); + List<MockListener.ListenerInvocation> invocations = listener.invocations(); + assertEquals(invocations, List.of(onOpen(ws), onError(ws, IllegalArgumentException.class))); + } + + @Test + public void testText1() throws Exception { + MockListener listener = new MockListener(Long.MAX_VALUE); + MockTransport transport = new MockTransport() { + @Override + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new MockReceiver(consumer, channel, + pair(now(), m -> m.onText("1", FIRST)), + pair(now(), m -> m.onText("2", PART)), + pair(now(), m -> m.onText("3", LAST)), + pair(now(), m -> m.onClose(NORMAL_CLOSURE, "no reason"))); + } + }; + WebSocket ws = newInstance(listener, transport); + listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS); + List<MockListener.ListenerInvocation> invocations = listener.invocations(); + assertEquals(invocations, List.of(onOpen(ws), + onText(ws, "1", FIRST), + onText(ws, "2", PART), + onText(ws, "3", LAST), + onClose(ws, NORMAL_CLOSURE, "no reason"))); + } + + @Test + public void testText2() throws Exception { + MockListener listener = new MockListener(Long.MAX_VALUE); + MockTransport transport = new MockTransport() { + @Override + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new MockReceiver(consumer, channel, + pair(now(), m -> m.onText("1", FIRST)), + pair(seconds(1), m -> m.onText("2", PART)), + pair(now(), m -> m.onText("3", LAST)), + pair(seconds(1), m -> m.onClose(NORMAL_CLOSURE, "no reason"))); + } + }; + WebSocket ws = newInstance(listener, transport); + listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS); + List<MockListener.ListenerInvocation> invocations = listener.invocations(); + assertEquals(invocations, List.of(onOpen(ws), + onText(ws, "1", FIRST), + onText(ws, "2", PART), + onText(ws, "3", LAST), + onClose(ws, NORMAL_CLOSURE, "no reason"))); + } + + @Test + public void testTextIntermixedWithPongs() throws Exception { + MockListener listener = new MockListener(Long.MAX_VALUE); + MockTransport transport = new MockTransport() { + @Override + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new MockReceiver(consumer, channel, + pair(now(), m -> m.onText("1", FIRST)), + pair(now(), m -> m.onText("2", PART)), + pair(now(), m -> m.onPong(ByteBuffer.allocate(16))), + pair(seconds(1), m -> m.onPong(ByteBuffer.allocate(32))), + pair(now(), m -> m.onText("3", LAST)), + pair(now(), m -> m.onPong(ByteBuffer.allocate(64))), + pair(now(), m -> m.onClose(NORMAL_CLOSURE, "no reason"))); + } + }; + WebSocket ws = newInstance(listener, transport); + listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS); + List<MockListener.ListenerInvocation> invocations = listener.invocations(); + assertEquals(invocations, List.of(onOpen(ws), + onText(ws, "1", FIRST), + onText(ws, "2", PART), + onPong(ws, ByteBuffer.allocate(16)), + onPong(ws, ByteBuffer.allocate(32)), + onText(ws, "3", LAST), + onPong(ws, ByteBuffer.allocate(64)), + onClose(ws, NORMAL_CLOSURE, "no reason"))); + } + + @Test + public void testTextIntermixedWithPings() throws Exception { + MockListener listener = new MockListener(Long.MAX_VALUE); + MockTransport transport = new MockTransport() { + @Override + protected Receiver newReceiver(MessageStreamConsumer consumer) { + return new MockReceiver(consumer, channel, + pair(now(), m -> m.onText("1", FIRST)), + pair(now(), m -> m.onText("2", PART)), + pair(now(), m -> m.onPing(ByteBuffer.allocate(16))), + pair(seconds(1), m -> m.onPing(ByteBuffer.allocate(32))), + pair(now(), m -> m.onText("3", LAST)), + pair(now(), m -> m.onPing(ByteBuffer.allocate(64))), + pair(now(), m -> m.onClose(NORMAL_CLOSURE, "no reason"))); + } + + @Override + protected Transmitter newTransmitter() { + return new MockTransmitter() { + @Override + protected CompletionStage<?> whenSent() { + return now(); + } + }; + } + }; + WebSocket ws = newInstance(listener, transport); + listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS); + List<MockListener.ListenerInvocation> invocations = listener.invocations(); + System.out.println(invocations); + assertEquals(invocations, List.of(onOpen(ws), + onText(ws, "1", FIRST), + onText(ws, "2", PART), + onPing(ws, ByteBuffer.allocate(16)), + onPing(ws, ByteBuffer.allocate(32)), + onText(ws, "3", LAST), + onPing(ws, ByteBuffer.allocate(64)), + onClose(ws, NORMAL_CLOSURE, "no reason"))); + } + + private static CompletionStage<?> seconds(long s) { + return new CompletableFuture<>().completeOnTimeout(null, s, TimeUnit.SECONDS); + } + + private static CompletionStage<?> now() { + return completedStage(null); + } + + private static WebSocket newInstance(WebSocket.Listener listener, + TransportSupplier transport) { + URI uri = URI.create("ws://localhost"); + String subprotocol = ""; + return WebSocketImpl.newInstance(uri, subprotocol, listener, transport); + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java new file mode 100644 index 00000000000..4150a87bbfe --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.websocket; + +import jdk.incubator.http.WebSocket; +import org.testng.annotations.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE; +import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally; +import static jdk.incubator.http.internal.websocket.WebSocketImpl.newInstance; +import static org.testng.Assert.assertEquals; + +public class SendingTest { + + @Test + public void sendTextImmediately() { + MockTransmitter t = new MockTransmitter() { + @Override + protected CompletionStage<?> whenSent() { + return CompletableFuture.completedFuture(null); + } + }; + WebSocket ws = newWebSocket(t); + CompletableFuture.completedFuture(ws) + .thenCompose(w -> w.sendText("1", true)) + .thenCompose(w -> w.sendText("2", true)) + .thenCompose(w -> w.sendText("3", true)) + .join(); + + assertEquals(t.queue().size(), 3); + } + + @Test + public void sendTextWithDelay() { + MockTransmitter t = new MockTransmitter() { + @Override + protected CompletionStage<?> whenSent() { + return new CompletableFuture<>() + .completeOnTimeout(null, 1, TimeUnit.SECONDS); + } + }; + WebSocket ws = newWebSocket(t); + CompletableFuture.completedFuture(ws) + .thenCompose(w -> w.sendText("1", true)) + .thenCompose(w -> w.sendText("2", true)) + .thenCompose(w -> w.sendText("3", true)) + .join(); + + assertEquals(t.queue().size(), 3); + } + + @Test + public void sendTextMixedDelay() { + Random r = new Random(); + + MockTransmitter t = new MockTransmitter() { + @Override + protected CompletionStage<?> whenSent() { + return r.nextBoolean() ? + new CompletableFuture<>().completeOnTimeout(null, 1, TimeUnit.SECONDS) : + CompletableFuture.completedFuture(null); + } + }; + WebSocket ws = newWebSocket(t); + CompletableFuture.completedFuture(ws) + .thenCompose(w -> w.sendText("1", true)) + .thenCompose(w -> w.sendText("2", true)) + .thenCompose(w -> w.sendText("3", true)) + .thenCompose(w -> w.sendText("4", true)) + .thenCompose(w -> w.sendText("5", true)) + .thenCompose(w -> w.sendText("6", true)) + .thenCompose(w -> w.sendText("7", true)) + .thenCompose(w -> w.sendText("8", true)) + .thenCompose(w -> w.sendText("9", true)) + .join(); + + assertEquals(t.queue().size(), 9); + } + + @Test + public void sendControlMessagesConcurrently() { + + CompletableFuture<?> first = new CompletableFuture<>(); + + MockTransmitter t = new MockTransmitter() { + + final AtomicInteger i = new AtomicInteger(); + + @Override + protected CompletionStage<?> whenSent() { + if (i.incrementAndGet() == 1) { + return first; + } else { + return CompletableFuture.completedFuture(null); + } + } + }; + WebSocket ws = newWebSocket(t); + + CompletableFuture<?> cf1 = ws.sendPing(ByteBuffer.allocate(0)); + CompletableFuture<?> cf2 = ws.sendPong(ByteBuffer.allocate(0)); + CompletableFuture<?> cf3 = ws.sendClose(NORMAL_CLOSURE, ""); + CompletableFuture<?> cf4 = ws.sendClose(NORMAL_CLOSURE, ""); + CompletableFuture<?> cf5 = ws.sendPing(ByteBuffer.allocate(0)); + CompletableFuture<?> cf6 = ws.sendPong(ByteBuffer.allocate(0)); + + first.complete(null); + // Don't care about exceptional completion, only that all of them have + // completed + CompletableFuture.allOf(cf1, cf2, cf3, cf4, cf5, cf6) + .handle((v, e) -> null).join(); + + cf3.join(); /* Check that sendClose has completed normally */ + cf4.join(); /* Check that repeated sendClose has completed normally */ + assertCompletesExceptionally(IllegalStateException.class, cf5); + assertCompletesExceptionally(IllegalStateException.class, cf6); + + assertEquals(t.queue().size(), 3); // 6 minus 3 that were not accepted + } + + private static WebSocket newWebSocket(Transmitter transmitter) { + URI uri = URI.create("ws://localhost"); + String subprotocol = ""; + TransportSupplier transport = new MockTransport() { + @Override + public Transmitter transmitter() { + return transmitter; + } + }; + return newInstance(uri, + subprotocol, + new MockListener(Long.MAX_VALUE), + transport); + } +} diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java index 3c9bf63bcee..d74fc8dbe1d 100644 --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -23,13 +23,18 @@ package jdk.incubator.http.internal.websocket; import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Stack; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.util.List.of; import static java.util.Objects.requireNonNull; @@ -188,43 +193,6 @@ final class TestSupport { }; } -// static <T> Iterator<T> filter(Iterator<? extends T> source, -// Predicate<? super T> predicate) { -// return new Iterator<>() { -// -// { findNext(); } -// -// T next; -// boolean hasNext; -// -// @Override -// public boolean hasNext() { -// return hasNext; -// } -// -// @Override -// public T next() { -// if (!hasNext) { -// throw new NoSuchElementException(); -// } -// T n = this.next; -// findNext(); -// return n; -// } -// -// void findNext() { -// while (source.hasNext()) { -// T n = source.next(); -// if (predicate.test(n)) { -// hasNext = true; -// next = n; -// break; -// } -// } -// } -// }; -// } - static ByteBuffer fullCopy(ByteBuffer src) { ByteBuffer copy = ByteBuffer.allocate(src.capacity()); int p = src.position(); @@ -297,141 +265,6 @@ final class TestSupport { a[j] = x; } - static <T> Iterator<T> concat(Iterator<? extends Iterator<? extends T>> iterators) { - requireNonNull(iterators); - return new Iterator<>() { - - private Iterator<? extends T> current = Collections.emptyIterator(); - - @Override - public boolean hasNext() { - while (!current.hasNext()) { - if (!iterators.hasNext()) { - return false; - } else { - current = iterators.next(); - } - } - return true; - } - - @Override - public T next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return current.next(); - } - }; - } - - interface Mock { - - /* - * Completes exceptionally if there are any expectations that haven't - * been met within the given time period, otherwise completes normally - */ - CompletableFuture<Void> expectations(long timeout, TimeUnit unit); - } - - static final class InvocationChecker { - - private final Object lock = new Object(); - private final Iterator<InvocationExpectation> expectations; - private final CompletableFuture<Void> expectationsViolation - = new CompletableFuture<>(); - - InvocationChecker(Iterable<InvocationExpectation> expectations) { - this.expectations = requireNonNull(expectations).iterator(); - } - - /* - * Completes exceptionally if there are any expectations that haven't - * been met within the given time period, otherwise completes normally - */ - CompletableFuture<Void> expectations(long timeout, TimeUnit unit) { - return expectationsViolation - .orTimeout(timeout, unit) - .handle((v, t) -> { - if (t == null) { - throw new InternalError( - "Unexpected normal completion: " + v); - } else if (t instanceof TimeoutException) { - synchronized (lock) { - if (!expectations.hasNext()) { - return null; - } else { - throw new AssertionFailedException( - "More invocations were expected"); - } - } - } else if (t instanceof AssertionFailedException) { - throw (AssertionFailedException) t; - } else { - throw new RuntimeException(t); - } - }); - } - - void checkInvocation(String name, Object... args) { - synchronized (lock) { - if (!expectations.hasNext()) { - expectationsViolation.completeExceptionally( - new AssertionFailedException( - "Less invocations were expected: " + name)); - return; - } - InvocationExpectation next = expectations.next(); - if (!next.name.equals(name)) { - expectationsViolation.completeExceptionally( - new AssertionFailedException( - "A different invocation was expected: " + name) - ); - return; - } - if (!next.predicate.apply(args)) { - expectationsViolation.completeExceptionally( - new AssertionFailedException( - "Invocation doesn't match the predicate: " - + name + ", " + Arrays.toString(args)) - ); - } - } - } - } - - static final class InvocationExpectation { - - final String name; - final F<Boolean> predicate; - - InvocationExpectation(String name, F<Boolean> predicate) { - this.name = requireNonNull(name); - this.predicate = requireNonNull(predicate); - } - } - - static void checkExpectations(Mock... mocks) { - checkExpectations(0, TimeUnit.SECONDS, mocks); - } - - static void checkExpectations(long timeout, TimeUnit unit, Mock... mocks) { - CompletableFuture<?>[] completableFutures = Stream.of(mocks) - .map(m -> m.expectations(timeout, unit)) - .collect(Collectors.toList()).toArray(new CompletableFuture<?>[0]); - CompletableFuture<Void> cf = CompletableFuture.allOf(completableFutures); - try { - cf.join(); - } catch (CompletionException e) { - Throwable cause = e.getCause(); - if (cause instanceof AssertionFailedException) { - throw (AssertionFailedException) cause; - } else { - throw e; - } - } - } - public static <T extends Throwable> T assertThrows(Class<? extends T> clazz, ThrowingProcedure code) { @SuppressWarnings("unchecked") @@ -465,6 +298,7 @@ final class TestSupport { caught = t; } if (predicate.test(caught)) { + System.out.println("Got expected exception: " + caught); return caught; } if (caught == null) { @@ -480,46 +314,13 @@ final class TestSupport { CompletionStage<?> stage) { CompletableFuture<?> cf = CompletableFuture.completedFuture(null).thenCompose(x -> stage); - return assertThrows(t -> clazz.isInstance(t.getCause()), cf::get); + return assertThrows(t -> clazz == t.getCause().getClass(), cf::get); } interface ThrowingProcedure { void run() throws Throwable; } - static final class Expectation { - - private final List<Predicate<? super Throwable>> list = new LinkedList<>(); - - static Expectation ifExpect(boolean condition, - Predicate<? super Throwable> predicate) { - return addPredicate(new Expectation(), condition, predicate); - } - - Expectation orExpect(boolean condition, - Predicate<? super Throwable> predicate) { - return addPredicate(this, condition, predicate); - } - - static Expectation addPredicate(Expectation e, boolean condition, - Predicate<? super Throwable> predicate) { - if (condition) { - e.list.add(requireNonNull(predicate)); - } - return e; - } - - public Throwable assertThrows(ThrowingProcedure code) { - Predicate<Throwable> p; - if (list.isEmpty()) { - p = Objects::isNull; - } else { - p = e -> list.stream().anyMatch(x -> x.test(e)); - } - return TestSupport.assertThrows(p, code); - } - } - static final class AssertionFailedException extends RuntimeException { private static final long serialVersionUID = 1L; diff --git a/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java b/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java new file mode 100644 index 00000000000..ebca6852c42 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2017, 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 + * @summary Basic security checks for WebSocket URI from the Builder + * @compile ../DummyWebSocketServer.java ../../ProxyServer.java + * @run testng/othervm/java.security.policy=httpclient.policy WSURLPermissionTest + */ + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URLPermission; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.Permissions; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.util.List; +import java.util.concurrent.ExecutionException; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.WebSocket; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class WSURLPermissionTest { + + static AccessControlContext withPermissions(Permission... perms) { + Permissions p = new Permissions(); + for (Permission perm : perms) { + p.add(perm); + } + ProtectionDomain pd = new ProtectionDomain(null, p); + return new AccessControlContext(new ProtectionDomain[]{ pd }); + } + + static AccessControlContext noPermissions() { + return withPermissions(/*empty*/); + } + + URI wsURI; + DummyWebSocketServer webSocketServer; + InetSocketAddress proxyAddress; + + @BeforeTest + public void setup() throws Exception { + ProxyServer proxyServer = new ProxyServer(0, true); + proxyAddress = new InetSocketAddress("127.0.0.1", proxyServer.getPort()); + webSocketServer = new DummyWebSocketServer(); + webSocketServer.open(); + wsURI = webSocketServer.getURI(); + + System.out.println("Proxy Server: " + proxyAddress); + System.out.println("DummyWebSocketServer: " + wsURI); + } + + @AfterTest + public void teardown() { + webSocketServer.close(); + } + + static class NoOpListener implements WebSocket.Listener {} + static final WebSocket.Listener noOpListener = new NoOpListener(); + + @DataProvider(name = "passingScenarios") + public Object[][] passingScenarios() { + HttpClient noProxyClient = HttpClient.newHttpClient(); + return new Object[][]{ + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // no actions + new URLPermission[] { new URLPermission(wsURI.toString()) }, + "0" /* for log file identification */ }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // scheme wildcard + new URLPermission[] { new URLPermission("ws://*") }, + "0.1" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // port wildcard + new URLPermission[] { new URLPermission("ws://"+wsURI.getHost()+":*") }, + "0.2" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // empty actions + new URLPermission[] { new URLPermission(wsURI.toString(), "") }, + "1" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // colon + new URLPermission[] { new URLPermission(wsURI.toString(), ":") }, + "2" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // wildcard + new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") }, + "3" }, + + // WS permission checking is agnostic of method, any/none will do + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // specific method + new URLPermission[] { new URLPermission(wsURI.toString(), "GET") }, + "3.1" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // specific method + new URLPermission[] { new URLPermission(wsURI.toString(), "POST") }, + "3.2" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // path + new URLPermission[] { new URLPermission(wsURI.resolve("/path/x").toString()) }, + "4" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // same dir wildcard + new URLPermission[] { new URLPermission(wsURI.resolve("/path/*").toString()) }, + "5" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // recursive + new URLPermission[] { new URLPermission(wsURI.resolve("/path/-").toString()) }, + "6" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // recursive top + new URLPermission[] { new URLPermission(wsURI.resolve("/-").toString()) }, + "7" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // header + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") }, + "8" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // header + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // wildcard + new URLPermission[] { new URLPermission(wsURI.toString(), ":*") }, + "9" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // headers + .header("B-Header", "B-Value") // headers + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header,B-Header") }, + "10" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // headers + .header("B-Header", "B-Value") // headers + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // wildcard + new URLPermission[] { new URLPermission(wsURI.toString(), ":*") }, + "11" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // headers + .header("B-Header", "B-Value") // headers + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // wildcards + new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") }, + "12" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // multi-value + .header("A-Header", "B-Value") // headers + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // wildcard + new URLPermission[] { new URLPermission(wsURI.toString(), ":*") }, + "13" }, + + { (PrivilegedExceptionAction<?>)() -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") // multi-value + .header("A-Header", "B-Value") // headers + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // single grant + new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") }, + "14" }, + + // client with a DIRECT proxy + { (PrivilegedExceptionAction<?>)() -> { + ProxySelector ps = ProxySelector.of(null); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { new URLPermission(wsURI.toString()) }, + "15" }, + + // client with a SOCKS proxy! ( expect implementation to ignore SOCKS ) + { (PrivilegedExceptionAction<?>)() -> { + ProxySelector ps = new ProxySelector() { + @Override public List<Proxy> select(URI uri) { + return List.of(new Proxy(Proxy.Type.SOCKS, proxyAddress)); } + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { } + }; + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { new URLPermission(wsURI.toString()) }, + "16" }, + + // client with a HTTP/HTTPS proxy + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), // CONNECT action string + new URLPermission("socket://"+proxyAddress.getHostName() + +":"+proxyAddress.getPort(), "CONNECT")}, + "17" }, + + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), // no action string + new URLPermission("socket://"+proxyAddress.getHostName() + +":"+proxyAddress.getPort())}, + "18" }, + + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), // wildcard headers + new URLPermission("socket://"+proxyAddress.getHostName() + +":"+proxyAddress.getPort(), "CONNECT:*")}, + "19" }, + + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + CountingProxySelector ps = CountingProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + assertEquals(ps.count(), 1); // ps.select only invoked once + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), // empty headers + new URLPermission("socket://"+proxyAddress.getHostName() + +":"+proxyAddress.getPort(), "CONNECT:")}, + "20" }, + + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), + new URLPermission("socket://*")}, // wildcard socket URL + "21" }, + + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission("ws://*"), // wildcard ws URL + new URLPermission("socket://*")}, // wildcard socket URL + "22" }, + + }; + } + + @Test(dataProvider = "passingScenarios") + public void testWithNoSecurityManager(PrivilegedExceptionAction<?> action, + URLPermission[] unused, + String dataProviderId) + throws Exception + { + // sanity ( no security manager ) + System.setSecurityManager(null); + try { + AccessController.doPrivileged(action); + } finally { + System.setSecurityManager(new SecurityManager()); + } + } + + @Test(dataProvider = "passingScenarios") + public void testWithAllPermissions(PrivilegedExceptionAction<?> action, + URLPermission[] unused, + String dataProviderId) + throws Exception + { + // Run with all permissions, i.e. no further restrictions than test's AllPermission + assert System.getSecurityManager() != null; + AccessController.doPrivileged(action); + } + + @Test(dataProvider = "passingScenarios") + public void testWithMinimalPermissions(PrivilegedExceptionAction<?> action, + URLPermission[] perms, + String dataProviderId) + throws Exception + { + // Run with minimal permissions, i.e. just what is required + assert System.getSecurityManager() != null; + AccessControlContext minimalACC = withPermissions(perms); + AccessController.doPrivileged(action, minimalACC); + } + + @Test(dataProvider = "passingScenarios") + public void testWithNoPermissions(PrivilegedExceptionAction<?> action, + URLPermission[] unused, + String dataProviderId) + throws Exception + { + // Run with NO permissions, i.e. expect SecurityException + assert System.getSecurityManager() != null; + try { + AccessController.doPrivileged(action, noPermissions()); + fail("EXPECTED SecurityException"); + } catch (PrivilegedActionException expected) { + Throwable t = expected.getCause(); + if (t instanceof ExecutionException) + t = t.getCause(); + + if (t instanceof SecurityException) + System.out.println("Caught expected SE:" + expected); + else + fail("Expected SecurityException, but got: " + t); + } + } + + // --- Negative tests --- + + @DataProvider(name = "failingScenarios") + public Object[][] failingScenarios() { + HttpClient noProxyClient = HttpClient.newHttpClient(); + return new Object[][]{ + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, + new URLPermission[]{ /* no permissions */ }, + "50" /* for log file identification */}, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // wrong scheme + new URLPermission[]{ new URLPermission("http://*") }, + "51" }, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // wrong scheme + new URLPermission[]{ new URLPermission("socket://*") }, + "52" }, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // wrong host + new URLPermission[]{ new URLPermission("ws://foo.com/") }, + "53" }, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // wrong port + new URLPermission[]{ new URLPermission("ws://"+ wsURI.getHost()+":5") }, + "54" }, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // only perm to set B not A + new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") }, + "55" }, + + { (PrivilegedExceptionAction<?>) () -> { + noProxyClient.newWebSocketBuilder() + .header("A-Header", "A-Value") + .header("B-Header", "B-Value") + .buildAsync(wsURI, noOpListener).get().abort(); + return null; + }, // only perm to set B not A + new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") }, + "56" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // wrong path + new URLPermission[] { new URLPermission(wsURI.resolve("/aDiffPath/").toString()) }, + "57" }, + + { (PrivilegedExceptionAction<?>)() -> { + URI uriWithPath = wsURI.resolve("/path/x"); + noProxyClient.newWebSocketBuilder() + .buildAsync(uriWithPath, noOpListener).get().abort(); + return null; }, // more specific path + new URLPermission[] { new URLPermission(wsURI.resolve("/path/x/y").toString()) }, + "58" }, + + // client with a HTTP/HTTPS proxy + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, // missing proxy perm + new URLPermission[] { new URLPermission(wsURI.toString()) }, + "100" }, + + // client with a HTTP/HTTPS proxy + { (PrivilegedExceptionAction<?>)() -> { + assert proxyAddress != null; + ProxySelector ps = ProxySelector.of(proxyAddress); + HttpClient client = HttpClient.newBuilder().proxy(ps).build(); + client.newWebSocketBuilder() + .buildAsync(wsURI, noOpListener).get().abort(); + return null; }, + new URLPermission[] { + new URLPermission(wsURI.toString()), // missing proxy CONNECT + new URLPermission("socket://*", "GET") }, + "101" }, + }; + } + + @Test(dataProvider = "failingScenarios") + public void testWithoutEnoughPermissions(PrivilegedExceptionAction<?> action, + URLPermission[] perms, + String dataProviderId) + throws Exception + { + // Run without Enough permissions, i.e. expect SecurityException + assert System.getSecurityManager() != null; + AccessControlContext notEnoughPermsACC = withPermissions(perms); + try { + AccessController.doPrivileged(action, notEnoughPermsACC); + fail("EXPECTED SecurityException"); + } catch (PrivilegedActionException expected) { + Throwable t = expected.getCause(); + if (t instanceof ExecutionException) + t = t.getCause(); + + if (t instanceof SecurityException) + System.out.println("Caught expected SE:" + expected); + else + fail("Expected SecurityException, but got: " + t); + } + } + + /** + * A Proxy Selector that wraps a ProxySelector.of(), and counts the number + * of times its select method has been invoked. This can be used to ensure + * that the Proxy Selector is invoked only once per WebSocket.Builder::buildAsync + * invocation. + */ + static class CountingProxySelector extends ProxySelector { + private final ProxySelector proxySelector; + private volatile int count; // 0 + private CountingProxySelector(InetSocketAddress proxyAddress) { + proxySelector = ProxySelector.of(proxyAddress); + } + + public static CountingProxySelector of(InetSocketAddress proxyAddress) { + return new CountingProxySelector(proxyAddress); + } + + int count() { return count; } + + @Override + public List<Proxy> select(URI uri) { + System.out.println("PS: uri"); + Throwable t = new Throwable(); + t.printStackTrace(System.out); + count++; + return proxySelector.select(uri); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + proxySelector.connectFailed(uri, sa, ioe); + } + } +} diff --git a/test/jdk/java/net/httpclient/websocket/security/httpclient.policy b/test/jdk/java/net/httpclient/websocket/security/httpclient.policy new file mode 100644 index 00000000000..8a2c74df9b7 --- /dev/null +++ b/test/jdk/java/net/httpclient/websocket/security/httpclient.policy @@ -0,0 +1,68 @@ +// +// Copyright (c) 2017, 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. +// + +grant codeBase "jrt:/jdk.incubator.httpclient" { + permission java.lang.RuntimePermission "accessClassInPackage.sun.net"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www"; + permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc"; + + // ## why is SP not good enough. Check API @throws signatures and impl + permission java.net.SocketPermission "*","connect,resolve"; + permission java.net.URLPermission "http:*","*:*"; + permission java.net.URLPermission "https:*","*:*"; + permission java.net.URLPermission "ws:*","*:*"; + permission java.net.URLPermission "wss:*","*:*"; + permission java.net.URLPermission "socket:*","CONNECT"; // proxy + + // For request/response body processors, fromFile, asFile + permission java.io.FilePermission "<<ALL FILES>>","read,write,delete"; + + // ## look at the different property names! + permission java.util.PropertyPermission "jdk.httpclient.HttpClient.log","read"; // name! + permission java.util.PropertyPermission "jdk.httpclient.auth.retrylimit","read"; + permission java.util.PropertyPermission "jdk.httpclient.connectionWindowSize","read"; + permission java.util.PropertyPermission "jdk.httpclient.enablepush","read"; + permission java.util.PropertyPermission "jdk.httpclient.hpack.maxheadertablesize","read"; + permission java.util.PropertyPermission "jdk.httpclient.keepalive.timeout","read"; + permission java.util.PropertyPermission "jdk.httpclient.maxframesize","read"; + permission java.util.PropertyPermission "jdk.httpclient.maxstreams","read"; + permission java.util.PropertyPermission "jdk.httpclient.redirects.retrylimit","read"; + permission java.util.PropertyPermission "jdk.httpclient.windowsize","read"; + permission java.util.PropertyPermission "jdk.httpclient.bufsize","read"; + permission java.util.PropertyPermission "jdk.httpclient.internal.selector.timeout","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.debug","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.debug","read"; + permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.log.level","read"; + permission java.util.PropertyPermission "test.src","read"; + + permission java.net.NetPermission "getProxySelector"; + + permission java.security.SecurityPermission "createAccessControlContext"; +}; + +// bootstrap to get the test going, it will do its own restrictions +grant codeBase "file:${test.classes}/*" { + permission java.security.AllPermission; +}; + diff --git a/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java b/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java new file mode 100644 index 00000000000..91fb1e02bac --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017, 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 8187044 8187111 + * @summary Verifies that the ConnectionPool correctly handle + * connection deadlines and purges the right connections + * from the cache. + * @modules jdk.incubator.httpclient java.management + * @run main/othervm --add-reads jdk.incubator.httpclient=java.management jdk.incubator.httpclient/jdk.incubator.http.ConnectionPoolTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java new file mode 100644 index 00000000000..c2749dc6dc9 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * @run testng jdk.incubator.httpclient/jdk.incubator.http.internal.common.DemandTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/Driver.java b/test/jdk/java/net/httpclient/whitebox/Driver.java index 00f5b12bf94..d69f7bf5244 100644 --- a/test/jdk/java/net/httpclient/whitebox/Driver.java +++ b/test/jdk/java/net/httpclient/whitebox/Driver.java @@ -23,10 +23,8 @@ /* * @test - * @bug 8151299 8164704 8187044 - * @modules jdk.incubator.httpclient java.management + * @bug 8151299 8164704 + * @modules jdk.incubator.httpclient * @run testng jdk.incubator.httpclient/jdk.incubator.http.SelectorTest * @run testng jdk.incubator.httpclient/jdk.incubator.http.RawChannelTest - * @run testng jdk.incubator.httpclient/jdk.incubator.http.ResponseHeadersTest - * @run main/othervm --add-reads jdk.incubator.httpclient=java.management jdk.incubator.httpclient/jdk.incubator.http.ConnectionPoolTest */ diff --git a/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java new file mode 100644 index 00000000000..cddf365700c --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @modules jdk.incubator.httpclient + * @run testng jdk.incubator.httpclient/jdk.incubator.http.FlowTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java new file mode 100644 index 00000000000..44844e2ba6b --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient + * @run testng jdk.incubator.httpclient/jdk.incubator.http.Http1HeaderParserTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java new file mode 100644 index 00000000000..7febdff3b9c --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient + * @run testng/othervm -Djdk.internal.httpclient.debug=true jdk.incubator.httpclient/jdk.incubator.http.SSLEchoTubeTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java new file mode 100644 index 00000000000..fbd25f3abfb --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 + * @modules jdk.incubator.httpclient + * @run testng/othervm -Djdk.internal.httpclient.debug=true jdk.incubator.httpclient/jdk.incubator.http.SSLTubeTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java new file mode 100644 index 00000000000..23b8403f680 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016, 2017, 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 + * @modules jdk.incubator.httpclient + * @run testng jdk.incubator.httpclient/jdk.incubator.http.WrapperTest + */ diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java new file mode 100644 index 00000000000..d90847cc07c --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import java.util.Random; + +/** Abstract supertype for tests that need random numbers within a given range. */ +public class AbstractRandomTest { + + private static Long getSystemSeed() { + Long seed = null; + try { + // note that Long.valueOf(null) also throws a NumberFormatException + // so if the property is undefined this will still work correctly + seed = Long.valueOf(System.getProperty("seed")); + } catch (NumberFormatException e) { + // do nothing: seed is still null + } + return seed; + } + + private static long getSeed() { + Long seed = getSystemSeed(); + if (seed == null) { + seed = (new Random()).nextLong(); + } + System.out.println("Seed from AbstractRandomTest.getSeed = "+seed+"L"); + return seed; + } + + private static Random random = new Random(getSeed()); + + protected static int randomRange(int lower, int upper) { + if (lower > upper) + throw new IllegalArgumentException("lower > upper"); + int diff = upper - lower; + int r = lower + random.nextInt(diff); + return r - (r % 8); // round down to multiple of 8 (align for longs) + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java new file mode 100644 index 00000000000..f24e84f8c58 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SSLTube; +import jdk.incubator.http.internal.common.Utils; +import org.testng.annotations.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.List; +import java.util.Random; +import java.util.StringTokenizer; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Flow; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.atomic.AtomicLong; + +public class AbstractSSLTubeTest extends AbstractRandomTest { + + public static final long COUNTER = 600; + public static final int LONGS_PER_BUF = 800; + public static final long TOTAL_LONGS = COUNTER * LONGS_PER_BUF; + public static final ByteBuffer SENTINEL = ByteBuffer.allocate(0); + // This is a hack to work around an issue with SubmissionPublisher. + // SubmissionPublisher will call onComplete immediately without forwarding + // remaining pending data if SubmissionPublisher.close() is called when + // there is no demand. In other words, it doesn't wait for the subscriber + // to pull all the data before calling onComplete. + // We use a CountDownLatch to figure out when it is safe to call close(). + // This may cause the test to hang if data are buffered. + protected final CountDownLatch allBytesReceived = new CountDownLatch(1); + + + protected static ByteBuffer getBuffer(long startingAt) { + ByteBuffer buf = ByteBuffer.allocate(LONGS_PER_BUF * 8); + for (int j = 0; j < LONGS_PER_BUF; j++) { + buf.putLong(startingAt++); + } + buf.flip(); + return buf; + } + + protected void run(FlowTube server, + ExecutorService sslExecutor, + CountDownLatch allBytesReceived) throws IOException { + FlowTube client = new SSLTube(createSSLEngine(true), + sslExecutor, + server); + SubmissionPublisher<List<ByteBuffer>> p = + new SubmissionPublisher<>(ForkJoinPool.commonPool(), + Integer.MAX_VALUE); + FlowTube.TubePublisher begin = p::subscribe; + CompletableFuture<Void> completion = new CompletableFuture<>(); + EndSubscriber end = new EndSubscriber(TOTAL_LONGS, completion, allBytesReceived); + client.connectFlows(begin, end); + /* End of wiring */ + + long count = 0; + System.out.printf("Submitting %d buffer arrays\n", COUNTER); + System.out.printf("LoopCount should be %d\n", TOTAL_LONGS); + for (long i = 0; i < COUNTER; i++) { + ByteBuffer b = getBuffer(count); + count += LONGS_PER_BUF; + p.submit(List.of(b)); + } + System.out.println("Finished submission. Waiting for loopback"); + completion.whenComplete((r,t) -> allBytesReceived.countDown()); + try { + allBytesReceived.await(); + } catch (InterruptedException e) { + throw new IOException(e); + } + p.close(); + System.out.println("All bytes received: calling publisher.close()"); + try { + completion.join(); + System.out.println("OK"); + } finally { + sslExecutor.shutdownNow(); + } + } + + protected static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + + } + } + + /** + * The final subscriber which receives the decrypted looped-back data. Just + * needs to compare the data with what was sent. The given CF is either + * completed exceptionally with an error or normally on success. + */ + protected static class EndSubscriber implements FlowTube.TubeSubscriber { + + private static final int REQUEST_WINDOW = 13; + + private final long nbytes; + private final AtomicLong counter = new AtomicLong(); + private final CompletableFuture<?> completion; + private final CountDownLatch allBytesReceived; + private volatile Flow.Subscription subscription; + private long unfulfilled; + + EndSubscriber(long nbytes, CompletableFuture<?> completion, + CountDownLatch allBytesReceived) { + this.nbytes = nbytes; + this.completion = completion; + this.allBytesReceived = allBytesReceived; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + unfulfilled = REQUEST_WINDOW; + System.out.println("EndSubscriber request " + REQUEST_WINDOW); + subscription.request(REQUEST_WINDOW); + } + + public static String info(List<ByteBuffer> i) { + StringBuilder sb = new StringBuilder(); + sb.append("size: ").append(Integer.toString(i.size())); + int x = 0; + for (ByteBuffer b : i) + x += b.remaining(); + sb.append(" bytes: ").append(x); + return sb.toString(); + } + + @Override + public void onNext(List<ByteBuffer> buffers) { + if (--unfulfilled == (REQUEST_WINDOW / 2)) { + long req = REQUEST_WINDOW - unfulfilled; + System.out.println("EndSubscriber request " + req); + unfulfilled = REQUEST_WINDOW; + subscription.request(req); + } + + long currval = counter.get(); + if (currval % 500 == 0) { + System.out.println("EndSubscriber: " + currval); + } + System.out.println("EndSubscriber onNext " + Utils.remaining(buffers)); + + for (ByteBuffer buf : buffers) { + while (buf.hasRemaining()) { + long n = buf.getLong(); + if (currval > (TOTAL_LONGS - 50)) { + System.out.println("End: " + currval); + } + if (n != currval++) { + System.out.println("ERROR at " + n + " != " + (currval - 1)); + completion.completeExceptionally(new RuntimeException("ERROR")); + subscription.cancel(); + return; + } + } + } + + counter.set(currval); + if (currval >= TOTAL_LONGS) { + allBytesReceived.countDown(); + } + } + + @Override + public void onError(Throwable throwable) { + System.out.println("EndSubscriber onError " + throwable); + completion.completeExceptionally(throwable); + allBytesReceived.countDown(); + } + + @Override + public void onComplete() { + long n = counter.get(); + if (n != nbytes) { + System.out.printf("nbytes=%d n=%d\n", nbytes, n); + completion.completeExceptionally(new RuntimeException("ERROR AT END")); + } else { + System.out.println("DONE OK"); + completion.complete(null); + } + allBytesReceived.countDown(); + } + + @Override + public String toString() { + return "EndSubscriber"; + } + } + + protected static SSLEngine createSSLEngine(boolean client) throws IOException { + SSLContext context = (new SimpleSSLContext()).get(); + SSLEngine engine = context.createSSLEngine(); + SSLParameters params = context.getSupportedSSLParameters(); + params.setProtocols(new String[]{"TLSv1.2"}); // TODO: This is essential. Needs to be protocol impl + if (client) { + params.setApplicationProtocols(new String[]{"proto1", "proto2"}); // server will choose proto2 + } else { + params.setApplicationProtocols(new String[]{"proto2"}); // server will choose proto2 + } + engine.setSSLParameters(params); + engine.setUseClientMode(client); + return engine; + } + + /** + * Creates a simple usable SSLContext for SSLSocketFactory or a HttpsServer + * using either a given keystore or a default one in the test tree. + * + * Using this class with a security manager requires the following + * permissions to be granted: + * + * permission "java.util.PropertyPermission" "test.src.path", "read"; + * permission java.io.FilePermission "${test.src}/../../../../lib/testlibrary/jdk/testlibrary/testkeys", + * "read"; The exact path above depends on the location of the test. + */ + protected static class SimpleSSLContext { + + private final SSLContext ssl; + + /** + * Loads default keystore from SimpleSSLContext source directory + */ + public SimpleSSLContext() throws IOException { + String paths = System.getProperty("test.src.path"); + StringTokenizer st = new StringTokenizer(paths, File.pathSeparator); + boolean securityExceptions = false; + SSLContext sslContext = null; + while (st.hasMoreTokens()) { + String path = st.nextToken(); + try { + File f = new File(path, "../../../../lib/testlibrary/jdk/testlibrary/testkeys"); + if (f.exists()) { + try (FileInputStream fis = new FileInputStream(f)) { + sslContext = init(fis); + break; + } + } + } catch (SecurityException e) { + // catch and ignore because permission only required + // for one entry on path (at most) + securityExceptions = true; + } + } + if (securityExceptions) { + System.err.println("SecurityExceptions thrown on loading testkeys"); + } + ssl = sslContext; + } + + private SSLContext init(InputStream i) throws IOException { + try { + char[] passphrase = "passphrase".toCharArray(); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(i, passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + + SSLContext ssl = SSLContext.getInstance("TLS"); + ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return ssl; + } catch (KeyManagementException | KeyStoreException | + UnrecoverableKeyException | CertificateException | + NoSuchAlgorithmException e) { + throw new RuntimeException(e.getMessage()); + } + } + + public SSLContext get() { + return ssl; + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java index 19e815fa286..0f99fb1cebe 100644 --- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java @@ -25,27 +25,30 @@ package jdk.incubator.http; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; import java.net.Authenticator; -import java.net.CookieManager; +import java.net.CookieHandler; import java.net.InetSocketAddress; import java.net.ProxySelector; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import java.util.List; import java.util.Optional; +import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.Flow; +import java.util.stream.IntStream; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; -import jdk.incubator.http.internal.common.ByteBufferReference; +import jdk.incubator.http.internal.common.FlowTube; /** - * @summary Verifies that the ConnectionPool won't prevent an HttpClient - * from being GC'ed. Verifies that the ConnectionPool has at most - * one CacheCleaner thread running. - * @bug 8187044 + * @summary Verifies that the ConnectionPool correctly handle + * connection deadlines and purges the right connections + * from the cache. + * @bug 8187044 8187111 * @author danielfuchs */ public class ConnectionPoolTest { @@ -65,100 +68,99 @@ public class ConnectionPoolTest { } public static void testCacheCleaners() throws Exception { - ConnectionPool pool = new ConnectionPool(); + ConnectionPool pool = new ConnectionPool(666); HttpClient client = new HttpClientStub(pool); InetSocketAddress proxy = InetSocketAddress.createUnresolved("bar", 80); System.out.println("Adding 10 connections to pool"); - for (int i=0; i<10; i++) { - InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80); - HttpConnection c1 = new HttpConnectionStub(client, addr, proxy, true); - pool.returnToPool(c1); - } - while (getActiveCleaners() == 0) { - System.out.println("Waiting for cleaner to start"); - Thread.sleep(10); - } - System.out.println("Active CacheCleaners: " + getActiveCleaners()); - if (getActiveCleaners() > 1) { - throw new RuntimeException("Too many CacheCleaner active: " - + getActiveCleaners()); - } - System.out.println("Removing 9 connections from pool"); - for (int i=0; i<9; i++) { - InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80); - HttpConnection c2 = pool.getConnection(true, addr, proxy); - if (c2 == null) { - throw new RuntimeException("connection not found for " + addr); - } - } - System.out.println("Active CacheCleaners: " + getActiveCleaners()); - if (getActiveCleaners() != 1) { - throw new RuntimeException("Wrong number of CacheCleaner active: " - + getActiveCleaners()); - } - System.out.println("Removing last connection from pool"); - for (int i=9; i<10; i++) { - InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80); - HttpConnection c2 = pool.getConnection(true, addr, proxy); - if (c2 == null) { - throw new RuntimeException("connection not found for " + addr); - } - } - System.out.println("Active CacheCleaners: " + getActiveCleaners() - + " (may be 0 or may still be 1)"); - if (getActiveCleaners() > 1) { - throw new RuntimeException("Too many CacheCleaner active: " - + getActiveCleaners()); - } - InetSocketAddress addr = InetSocketAddress.createUnresolved("foo", 80); - HttpConnection c = new HttpConnectionStub(client, addr, proxy, true); - System.out.println("Adding/Removing one connection from pool 20 times in a loop"); - for (int i=0; i<20; i++) { - pool.returnToPool(c); - HttpConnection c2 = pool.getConnection(true, addr, proxy); - if (c2 == null) { - throw new RuntimeException("connection not found for " + addr); - } - if (c2 != c) { - throw new RuntimeException("wrong connection found for " + addr); - } - } - if (getActiveCleaners() > 1) { - throw new RuntimeException("Too many CacheCleaner active: " - + getActiveCleaners()); - } - ReferenceQueue<HttpClient> queue = new ReferenceQueue<>(); - WeakReference<HttpClient> weak = new WeakReference<>(client, queue); - System.gc(); - Reference.reachabilityFence(pool); - client = null; pool = null; c = null; - while (true) { - long cleaners = getActiveCleaners(); - System.out.println("Waiting for GC to release stub HttpClient;" - + " active cache cleaners: " + cleaners); - System.gc(); - Reference<?> ref = queue.remove(1000); - if (ref == weak) { - System.out.println("Stub HttpClient GC'ed"); - break; - } - } - while (getActiveCleaners() > 0) { - System.out.println("Waiting for CacheCleaner to stop"); - Thread.sleep(1000); - } - System.out.println("Active CacheCleaners: " - + getActiveCleaners()); + Random random = new Random(); - if (getActiveCleaners() > 0) { - throw new RuntimeException("Too many CacheCleaner active: " - + getActiveCleaners()); + final int count = 20; + Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS); + int[] keepAlives = new int[count]; + HttpConnectionStub[] connections = new HttpConnectionStub[count]; + long purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now); + long expected = 0; + if (purge != expected) { + throw new RuntimeException("Bad purge delay: " + purge + + ", expected " + expected); + } + expected = Long.MAX_VALUE; + for (int i=0; i<count; i++) { + InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80); + keepAlives[i] = random.nextInt(10) * 10 + 10; + connections[i] = new HttpConnectionStub(client, addr, proxy, true); + System.out.println("Adding connection: " + now + + " keepAlive: " + keepAlives[i] + + " /" + connections[i]); + pool.returnToPool(connections[i], now, keepAlives[i]); + expected = Math.min(expected, keepAlives[i] * 1000); + purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now); + if (purge != expected) { + throw new RuntimeException("Bad purge delay: " + purge + + ", expected " + expected); + } + } + int min = IntStream.of(keepAlives).min().getAsInt(); + int max = IntStream.of(keepAlives).max().getAsInt(); + int mean = (min + max)/2; + System.out.println("min=" + min + ", max=" + max + ", mean=" + mean); + purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now); + System.out.println("first purge would be in " + purge + " ms"); + if (Math.abs(purge/1000 - min) > 0) { + throw new RuntimeException("expected " + min + " got " + purge/1000); + } + long opened = java.util.stream.Stream.of(connections) + .filter(HttpConnectionStub::connected).count(); + if (opened != count) { + throw new RuntimeException("Opened: expected " + + count + " got " + opened); + } + purge = mean * 1000; + System.out.println("start purging at " + purge + " ms"); + Instant next = now; + do { + System.out.println("next purge is in " + purge + " ms"); + next = next.plus(purge, ChronoUnit.MILLIS); + purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(next); + long k = now.until(next, ChronoUnit.SECONDS); + System.out.println("now is " + k + "s from start"); + for (int i=0; i<count; i++) { + if (connections[i].connected() != (k < keepAlives[i])) { + throw new RuntimeException("Bad connection state for " + + i + + "\n\t connected=" + connections[i].connected() + + "\n\t keepAlive=" + keepAlives[i] + + "\n\t elapsed=" + k); + } + } + } while (purge > 0); + opened = java.util.stream.Stream.of(connections) + .filter(HttpConnectionStub::connected).count(); + if (opened != 0) { + throw new RuntimeException("Closed: expected " + + count + " got " + + (count-opened)); } } + static <T> T error() { throw new InternalError("Should not reach here: wrong test assumptions!"); } + static class FlowTubeStub implements FlowTube { + final HttpConnectionStub conn; + FlowTubeStub(HttpConnectionStub conn) { this.conn = conn; } + @Override + public void onSubscribe(Flow.Subscription subscription) { } + @Override public void onError(Throwable error) { error(); } + @Override public void onComplete() { error(); } + @Override public void onNext(List<ByteBuffer> item) { error();} + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + } + @Override public boolean isFinished() { return conn.closed; } + } + // Emulates an HttpConnection that has a strong reference to its HttpClient. static class HttpConnectionStub extends HttpConnection { @@ -172,50 +174,44 @@ public class ConnectionPoolTest { this.proxy = proxy; this.secured = secured; this.client = client; + this.flow = new FlowTubeStub(this); } - InetSocketAddress proxy; - InetSocketAddress address; - boolean secured; - ConnectionPool.CacheKey key; - HttpClient client; + final InetSocketAddress proxy; + final InetSocketAddress address; + final boolean secured; + final ConnectionPool.CacheKey key; + final HttpClient client; + final FlowTubeStub flow; + volatile boolean closed; // All these return something - @Override boolean connected() {return true;} + @Override boolean connected() {return !closed;} @Override boolean isSecure() {return secured;} @Override boolean isProxied() {return proxy!=null;} @Override ConnectionPool.CacheKey cacheKey() {return key;} - @Override public void close() {} @Override void shutdownInput() throws IOException {} @Override void shutdownOutput() throws IOException {} + @Override + public void close() { + closed=true; + System.out.println("closed: " + this); + } + @Override public String toString() { return "HttpConnectionStub: " + address + " proxy: " + proxy; } // All these throw errors - @Override - public void connect() throws IOException, InterruptedException {error();} + @Override public HttpPublisher publisher() {return error();} @Override public CompletableFuture<Void> connectAsync() {return error();} @Override SocketChannel channel() {return error();} - @Override void flushAsync() throws IOException {error();} @Override - protected ByteBuffer readImpl() throws IOException {return error();} - @Override CompletableFuture<Void> whenReceivingResponse() {return error();} - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - throw (Error)error(); + HttpConnection.DetachedConnectionChannel detachChannel() { + return error(); } @Override - long write(ByteBuffer buffer) throws IOException {throw (Error)error();} - @Override - void writeAsync(ByteBufferReference[] buffers) throws IOException { - error(); - } - @Override - void writeAsyncUnordered(ByteBufferReference[] buffers) - throws IOException { - error(); - } + FlowTube getConnectionFlow() {return flow;} } // Emulates an HttpClient that has a strong reference to its connection pool. static class HttpClientStub extends HttpClient { @@ -223,14 +219,14 @@ public class ConnectionPoolTest { this.pool = pool; } final ConnectionPool pool; - @Override public Optional<CookieManager> cookieManager() {return error();} + @Override public Optional<CookieHandler> cookieHandler() {return error();} @Override public HttpClient.Redirect followRedirects() {return error();} @Override public Optional<ProxySelector> proxy() {return error();} @Override public SSLContext sslContext() {return error();} - @Override public Optional<SSLParameters> sslParameters() {return error();} + @Override public SSLParameters sslParameters() {return error();} @Override public Optional<Authenticator> authenticator() {return error();} @Override public HttpClient.Version version() {return HttpClient.Version.HTTP_1_1;} - @Override public Executor executor() {return error();} + @Override public Optional<Executor> executor() {return error();} @Override public <T> HttpResponse<T> send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler) @@ -244,7 +240,7 @@ public class ConnectionPoolTest { } @Override public <U, T> CompletableFuture<U> sendAsync(HttpRequest req, - HttpResponse.MultiProcessor<U, T> multiProcessor) { + HttpResponse.MultiSubscriber<U, T> multiSubscriber) { return error(); } } diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java new file mode 100644 index 00000000000..5f4cc7ef147 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.List; +import java.util.Random; +import java.util.StringTokenizer; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.*; +import javax.net.ssl.TrustManagerFactory; +import jdk.incubator.http.internal.common.Utils; +import org.testng.annotations.Test; +import jdk.incubator.http.internal.common.SSLFlowDelegate; + +@Test +public class FlowTest extends AbstractRandomTest { + + private final SubmissionPublisher<List<ByteBuffer>> srcPublisher; + private final ExecutorService executor; + private static final long COUNTER = 3000; + private static final int LONGS_PER_BUF = 800; + static final long TOTAL_LONGS = COUNTER * LONGS_PER_BUF; + public static final ByteBuffer SENTINEL = ByteBuffer.allocate(0); + static volatile String alpn; + + // This is a hack to work around an issue with SubmissionPublisher. + // SubmissionPublisher will call onComplete immediately without forwarding + // remaining pending data if SubmissionPublisher.close() is called when + // there is no demand. In other words, it doesn't wait for the subscriber + // to pull all the data before calling onComplete. + // We use a CountDownLatch to figure out when it is safe to call close(). + // This may cause the test to hang if data are buffered. + final CountDownLatch allBytesReceived = new CountDownLatch(1); + + private final CompletableFuture<Void> completion; + + public FlowTest() throws IOException { + executor = Executors.newCachedThreadPool(); + srcPublisher = new SubmissionPublisher<>(executor, 20, + this::handlePublisherException); + SSLContext ctx = (new SimpleSSLContext()).get(); + SSLEngine engineClient = ctx.createSSLEngine(); + SSLParameters params = ctx.getSupportedSSLParameters(); + params.setApplicationProtocols(new String[]{"proto1", "proto2"}); // server will choose proto2 + params.setProtocols(new String[]{"TLSv1.2"}); // TODO: This is essential. Needs to be protocol impl + engineClient.setSSLParameters(params); + engineClient.setUseClientMode(true); + completion = new CompletableFuture<>(); + SSLLoopbackSubscriber looper = new SSLLoopbackSubscriber(ctx, executor, allBytesReceived); + looper.start(); + EndSubscriber end = new EndSubscriber(TOTAL_LONGS, completion, allBytesReceived); + SSLFlowDelegate sslClient = new SSLFlowDelegate(engineClient, executor, end, looper); + // going to measure how long handshake takes + final long start = System.currentTimeMillis(); + sslClient.alpn().whenComplete((String s, Throwable t) -> { + if (t != null) + t.printStackTrace(); + long endTime = System.currentTimeMillis(); + alpn = s; + System.out.println("ALPN: " + alpn); + long period = (endTime - start); + System.out.printf("Handshake took %d ms\n", period); + }); + Subscriber<List<ByteBuffer>> reader = sslClient.upstreamReader(); + Subscriber<List<ByteBuffer>> writer = sslClient.upstreamWriter(); + looper.setReturnSubscriber(reader); + // now connect all the pieces + srcPublisher.subscribe(writer); + String aa = sslClient.alpn().join(); + System.out.println("AAALPN = " + aa); + } + + private void handlePublisherException(Object o, Throwable t) { + System.out.println("Src Publisher exception"); + t.printStackTrace(System.out); + } + + private static ByteBuffer getBuffer(long startingAt) { + ByteBuffer buf = ByteBuffer.allocate(LONGS_PER_BUF * 8); + for (int j = 0; j < LONGS_PER_BUF; j++) { + buf.putLong(startingAt++); + } + buf.flip(); + return buf; + } + + @Test + public void run() { + long count = 0; + System.out.printf("Submitting %d buffer arrays\n", COUNTER); + System.out.printf("LoopCount should be %d\n", TOTAL_LONGS); + for (long i = 0; i < COUNTER; i++) { + ByteBuffer b = getBuffer(count); + count += LONGS_PER_BUF; + srcPublisher.submit(List.of(b)); + } + System.out.println("Finished submission. Waiting for loopback"); + // make sure we don't wait for allBytesReceived in case of error. + completion.whenComplete((r,t) -> allBytesReceived.countDown()); + try { + allBytesReceived.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + System.out.println("All bytes received: "); + srcPublisher.close(); + try { + completion.join(); + if (!alpn.equals("proto2")) { + throw new RuntimeException("wrong alpn received"); + } + System.out.println("OK"); + } finally { + executor.shutdownNow(); + } + } + +/* + public static void main(String[]args) throws Exception { + FlowTest test = new FlowTest(); + test.run(); + } +*/ + + /** + * This Subscriber simulates an SSL loopback network. The object itself + * accepts outgoing SSL encrypted data which is looped back via two sockets + * (one of which is an SSLSocket emulating a server). The method + * {@link #setReturnSubscriber(java.util.concurrent.Flow.Subscriber) } + * is used to provide the Subscriber which feeds the incoming side + * of SSLFlowDelegate. Three threads are used to implement this behavior + * and a SubmissionPublisher drives the incoming read side. + * <p> + * A thread reads from the buffer, writes + * to the client j.n.Socket which is connected to a SSLSocket operating + * in server mode. A second thread loops back data read from the SSLSocket back to the + * client again. A third thread reads the client socket and pushes the data to + * a SubmissionPublisher that drives the reader side of the SSLFlowDelegate + */ + static class SSLLoopbackSubscriber implements Subscriber<List<ByteBuffer>> { + private final BlockingQueue<ByteBuffer> buffer; + private final Socket clientSock; + private final SSLSocket serverSock; + private final Thread thread1, thread2, thread3; + private volatile Flow.Subscription clientSubscription; + private final SubmissionPublisher<List<ByteBuffer>> publisher; + private final CountDownLatch allBytesReceived; + + SSLLoopbackSubscriber(SSLContext ctx, + ExecutorService exec, + CountDownLatch allBytesReceived) throws IOException { + SSLServerSocketFactory fac = ctx.getServerSocketFactory(); + SSLServerSocket serv = (SSLServerSocket) fac.createServerSocket(0); + SSLParameters params = serv.getSSLParameters(); + params.setApplicationProtocols(new String[]{"proto2"}); + serv.setSSLParameters(params); + + + int serverPort = serv.getLocalPort(); + clientSock = new Socket("127.0.0.1", serverPort); + serverSock = (SSLSocket) serv.accept(); + this.buffer = new LinkedBlockingQueue<>(); + this.allBytesReceived = allBytesReceived; + thread1 = new Thread(this::clientWriter, "clientWriter"); + thread2 = new Thread(this::serverLoopback, "serverLoopback"); + thread3 = new Thread(this::clientReader, "clientReader"); + publisher = new SubmissionPublisher<>(exec, Flow.defaultBufferSize(), + this::handlePublisherException); + SSLFlowDelegate.Monitor.add(this::monitor); + } + + public void start() { + thread1.start(); + thread2.start(); + thread3.start(); + } + + private void handlePublisherException(Object o, Throwable t) { + System.out.println("Loopback Publisher exception"); + t.printStackTrace(System.out); + } + + private final AtomicInteger readCount = new AtomicInteger(); + + // reads off the SSLSocket the data from the "server" + private void clientReader() { + try { + InputStream is = clientSock.getInputStream(); + final int bufsize = FlowTest.randomRange(512, 16 * 1024); + System.out.println("clientReader: bufsize = " + bufsize); + while (true) { + byte[] buf = new byte[bufsize]; + int n = is.read(buf); + if (n == -1) { + System.out.println("clientReader close: read " + + readCount.get() + " bytes"); + System.out.println("clientReader: got EOF. " + + "Waiting signal to close publisher."); + allBytesReceived.await(); + System.out.println("clientReader: closing publisher"); + publisher.close(); + sleep(2000); + Utils.close(is, clientSock); + return; + } + ByteBuffer bb = ByteBuffer.wrap(buf, 0, n); + readCount.addAndGet(n); + publisher.submit(List.of(bb)); + } + } catch (Throwable e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + + // writes the encrypted data from SSLFLowDelegate to the j.n.Socket + // which is connected to the SSLSocket emulating a server. + private void clientWriter() { + long nbytes = 0; + try { + OutputStream os = + new BufferedOutputStream(clientSock.getOutputStream()); + + while (true) { + ByteBuffer buf = buffer.take(); + if (buf == FlowTest.SENTINEL) { + // finished + //Utils.sleep(2000); + System.out.println("clientWriter close: " + nbytes + " written"); + clientSock.shutdownOutput(); + System.out.println("clientWriter close return"); + return; + } + int len = buf.remaining(); + int written = writeToStream(os, buf); + assert len == written; + nbytes += len; + assert !buf.hasRemaining() + : "buffer has " + buf.remaining() + " bytes left"; + clientSubscription.request(1); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private int writeToStream(OutputStream os, ByteBuffer buf) throws IOException { + byte[] b = buf.array(); + int offset = buf.arrayOffset() + buf.position(); + int n = buf.limit() - buf.position(); + os.write(b, offset, n); + buf.position(buf.limit()); + os.flush(); + return n; + } + + private final AtomicInteger loopCount = new AtomicInteger(); + + public String monitor() { + return "serverLoopback: loopcount = " + loopCount.toString() + + " clientRead: count = " + readCount.toString(); + } + + // thread2 + private void serverLoopback() { + try { + InputStream is = serverSock.getInputStream(); + OutputStream os = serverSock.getOutputStream(); + final int bufsize = FlowTest.randomRange(512, 16 * 1024); + System.out.println("serverLoopback: bufsize = " + bufsize); + byte[] bb = new byte[bufsize]; + while (true) { + int n = is.read(bb); + if (n == -1) { + sleep(2000); + is.close(); + serverSock.close(); + return; + } + os.write(bb, 0, n); + os.flush(); + loopCount.addAndGet(n); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + + /** + * This needs to be called before the chain is subscribed. It can't be + * supplied in the constructor. + */ + public void setReturnSubscriber(Subscriber<List<ByteBuffer>> returnSubscriber) { + publisher.subscribe(returnSubscriber); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + clientSubscription = subscription; + clientSubscription.request(5); + } + + @Override + public void onNext(List<ByteBuffer> item) { + try { + for (ByteBuffer b : item) + buffer.put(b); + } catch (InterruptedException e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + Utils.close(clientSock); + } + + @Override + public void onComplete() { + try { + buffer.put(FlowTest.SENTINEL); + } catch (InterruptedException e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + } + + /** + * The final subscriber which receives the decrypted looped-back data. + * Just needs to compare the data with what was sent. The given CF is + * either completed exceptionally with an error or normally on success. + */ + static class EndSubscriber implements Subscriber<List<ByteBuffer>> { + + private final long nbytes; + + private final AtomicLong counter; + private volatile Flow.Subscription subscription; + private final CompletableFuture<Void> completion; + private final CountDownLatch allBytesReceived; + + EndSubscriber(long nbytes, + CompletableFuture<Void> completion, + CountDownLatch allBytesReceived) { + counter = new AtomicLong(0); + this.nbytes = nbytes; + this.completion = completion; + this.allBytesReceived = allBytesReceived; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(5); + } + + public static String info(List<ByteBuffer> i) { + StringBuilder sb = new StringBuilder(); + sb.append("size: ").append(Integer.toString(i.size())); + int x = 0; + for (ByteBuffer b : i) + x += b.remaining(); + sb.append(" bytes: " + Integer.toString(x)); + return sb.toString(); + } + + @Override + public void onNext(List<ByteBuffer> buffers) { + long currval = counter.get(); + //if (currval % 500 == 0) { + //System.out.println("End: " + currval); + //} + + for (ByteBuffer buf : buffers) { + while (buf.hasRemaining()) { + long n = buf.getLong(); + //if (currval > (FlowTest.TOTAL_LONGS - 50)) { + //System.out.println("End: " + currval); + //} + if (n != currval++) { + System.out.println("ERROR at " + n + " != " + (currval - 1)); + completion.completeExceptionally(new RuntimeException("ERROR")); + subscription.cancel(); + return; + } + } + } + + counter.set(currval); + subscription.request(1); + if (currval >= TOTAL_LONGS) { + allBytesReceived.countDown(); + } + } + + @Override + public void onError(Throwable throwable) { + allBytesReceived.countDown(); + completion.completeExceptionally(throwable); + } + + @Override + public void onComplete() { + long n = counter.get(); + if (n != nbytes) { + System.out.printf("nbytes=%d n=%d\n", nbytes, n); + completion.completeExceptionally(new RuntimeException("ERROR AT END")); + } else { + System.out.println("DONE OK: counter = " + n); + allBytesReceived.countDown(); + completion.complete(null); + } + } + } + + /** + * Creates a simple usable SSLContext for SSLSocketFactory + * or a HttpsServer using either a given keystore or a default + * one in the test tree. + * <p> + * Using this class with a security manager requires the following + * permissions to be granted: + * <p> + * permission "java.util.PropertyPermission" "test.src.path", "read"; + * permission java.io.FilePermission + * "${test.src}/../../../../lib/testlibrary/jdk/testlibrary/testkeys", "read"; + * The exact path above depends on the location of the test. + */ + static class SimpleSSLContext { + + private final SSLContext ssl; + + /** + * Loads default keystore from SimpleSSLContext source directory + */ + public SimpleSSLContext() throws IOException { + String paths = System.getProperty("test.src.path"); + StringTokenizer st = new StringTokenizer(paths, File.pathSeparator); + boolean securityExceptions = false; + SSLContext sslContext = null; + while (st.hasMoreTokens()) { + String path = st.nextToken(); + try { + File f = new File(path, "../../../../lib/testlibrary/jdk/testlibrary/testkeys"); + if (f.exists()) { + try (FileInputStream fis = new FileInputStream(f)) { + sslContext = init(fis); + break; + } + } + } catch (SecurityException e) { + // catch and ignore because permission only required + // for one entry on path (at most) + securityExceptions = true; + } + } + if (securityExceptions) { + System.out.println("SecurityExceptions thrown on loading testkeys"); + } + ssl = sslContext; + } + + private SSLContext init(InputStream i) throws IOException { + try { + char[] passphrase = "passphrase".toCharArray(); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(i, passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ks); + + SSLContext ssl = SSLContext.getInstance("TLS"); + ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return ssl; + } catch (KeyManagementException | KeyStoreException | + UnrecoverableKeyException | CertificateException | + NoSuchAlgorithmException e) { + throw new RuntimeException(e.getMessage()); + } + } + + public SSLContext get() { + return ssl; + } + } + + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java new file mode 100644 index 00000000000..69d63d245d9 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import java.io.ByteArrayInputStream; +import java.net.ProtocolException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import sun.net.www.MessageHeader; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; +import static java.lang.System.out; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.util.stream.Collectors.toList; +import static org.testng.Assert.*; + +// Mostly verifies the "new" Http1HeaderParser returns the same results as the +// tried and tested sun.net.www.MessageHeader. + +public class Http1HeaderParserTest { + + @DataProvider(name = "responses") + public Object[][] responses() { + List<String> responses = new ArrayList<>(); + + String[] basic = + { "HTTP/1.1 200 OK\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + "Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" + + "Server: Apache/1.3.14 (Unix)\r\n" + + "Connection: close\r\n" + + "Content-Type: text/html; charset=iso-8859-1\r\n" + + "Content-Length: 10\r\n\r\n" + + "123456789", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 9\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 9\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + // more than one SP after ':' + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length:\t10\r\n" + + "Content-Type:\ttext/html; charset=UTF-8\r\n\r\n" + // HT separator + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length:\t\t10\r\n" + + "Content-Type:\t\ttext/html; charset=UTF-8\r\n\r\n" + // more than one HT after ':' + "XXXXX", + + "HTTP/1.1 407 Proxy Authorization Required\r\n" + + "Proxy-Authenticate: Basic realm=\"a fake realm\"\r\n\r\n", + + "HTTP/1.1 401 Unauthorized\r\n" + + "WWW-Authenticate: Digest realm=\"wally land\" domain=/ " + + "nonce=\"2B7F3A2B\" qop=\"auth\"\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + "X-Foo:\r\n\r\n", // no value + + "HTTP/1.1 200 OK\r\n" + + "X-Foo:\r\n\r\n" + // no value, with response body + "Some Response Body", + + "HTTP/1.1 200 OK\r\n" + + "X-Foo:\r\n" + // no value, followed by another header + "Content-Length: 10\r\n\r\n" + + "Some Response Body", + + "HTTP/1.1 200 OK\r\n" + + "X-Foo:\r\n" + // no value, followed by another header, with response body + "Content-Length: 10\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + "X-Foo: chegar\r\n" + + "X-Foo: dfuchs\r\n" + // same header appears multiple times + "Content-Length: 0\r\n" + + "X-Foo: michaelm\r\n" + + "X-Foo: prappo\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + "X-Foo:\r\n" + // no value, same header appears multiple times + "X-Foo: dfuchs\r\n" + + "Content-Length: 0\r\n" + + "X-Foo: michaelm\r\n" + + "X-Foo: prappo\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + "Accept-Ranges: bytes\r\n" + + "Cache-control: max-age=0, no-cache=\"set-cookie\"\r\n" + + "Content-Length: 132868\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n" + + "Date: Sun, 05 Nov 2017 22:24:03 GMT\r\n" + + "Server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips Communique/4.2.2\r\n" + + "Set-Cookie: AWSELB=AF7927F5100F4202119876ED2436B5005EE;PATH=/;MAX-AGE=900\r\n" + + "Vary: Host,Accept-Encoding,User-Agent\r\n" + + "X-Mod-Pagespeed: 1.12.34.2-0\r\n" + + "Connection: keep-alive\r\n\r\n" + }; + Arrays.stream(basic).forEach(responses::add); + + String[] foldingTemplate = + { "HTTP/1.1 200 OK\r\n" + + "Content-Length: 9\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r' + " charset=UTF-8\r\n" + // one preceding SP + "Connection: close\r\n\r\n" + + "XXYYZZAABBCCDDEE", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 19\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r + " charset=UTF-8\r\n" + // more than one preceding SP + "Connection: keep-alive\r\n\r\n" + + "XXYYZZAABBCCDDEEFFGG", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 999\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r + "\tcharset=UTF-8\r\n" + // one preceding HT + "Connection: close\r\n\r\n" + + "XXYYZZAABBCCDDEE", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 54\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r + "\t\t\tcharset=UTF-8\r\n" + // more than one preceding HT + "Connection: keep-alive\r\n\r\n" + + "XXYYZZAABBCCDDEEFFGG", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: -1\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r + "\t \t \tcharset=UTF-8\r\n" + // mix of preceding HT and SP + "Connection: keep-alive\r\n\r\n" + + "XXYYZZAABBCCDDEEFFGGHH", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 65\r\n" + + "Content-Type: text/html;$NEWLINE" + // folding field-value with '\n'|'\r + " \t \t charset=UTF-8\r\n" + // mix of preceding SP and HT + "Connection: keep-alive\r\n\r\n" + + "XXYYZZAABBCCDDEEFFGGHHII", + }; + for (String newLineChar : new String[] { "\n", "\r" }) { + for (String template : foldingTemplate) + responses.add(template.replace("$NEWLINE", newLineChar)); + } + + String[] bad = // much of this is to retain parity with legacy MessageHeaders + { "HTTP/1.1 200 OK\r\n" + + "Connection:\r\n\r\n", // empty value, no body + + "HTTP/1.1 200 OK\r\n" + + "Connection:\r\n\r\n" + // empty value, with body + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + ": no header\r\n\r\n", // no/empty header-name, no body, no following header + + "HTTP/1.1 200 OK\r\n" + + ": no; header\r\n" + // no/empty header-name, no body, following header + "Content-Length: 65\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" + + ": no header\r\n" + // no/empty header-name + "Content-Length: 65\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + ": no header\r\n\r\n" + // no/empty header-name, followed by header + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Conte\r" + + " nt-Length: 9\r\n" + // fold/bad header name ??? + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Conte\r" + + "nt-Length: 9\r\n" + // fold/bad header name ??? without preceding space + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXXYYZZ", + + "HTTP/1.0 404 Not Found\r\n" + + "header-without-colon\r\n\r\n", + + "HTTP/1.0 404 Not Found\r\n" + + "header-without-colon\r\n\r\n" + + "SOMEBODY", + + }; + Arrays.stream(bad).forEach(responses::add); + + return responses.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); + } + + @Test(dataProvider = "responses") + public void verifyHeaders(String respString) throws Exception { + byte[] bytes = respString.getBytes(US_ASCII); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + MessageHeader m = new MessageHeader(bais); + Map<String,List<String>> messageHeaderMap = m.getHeaders(); + int available = bais.available(); + + Http1HeaderParser decoder = new Http1HeaderParser(); + ByteBuffer b = ByteBuffer.wrap(bytes); + decoder.parse(b); + Map<String,List<String>> decoderMap1 = decoder.headers().map(); + assertEquals(available, b.remaining(), + "stream available not equal to remaining"); + + // assert status-line + String statusLine1 = messageHeaderMap.get(null).get(0); + String statusLine2 = decoder.statusLine(); + if (statusLine1.startsWith("HTTP")) {// skip the case where MH's messes up the status-line + assertEquals(statusLine1, statusLine2, "Status-line not equal"); + } else { + assertTrue(statusLine2.startsWith("HTTP/1."), "Status-line not HTTP/1."); + } + + // remove the null'th entry with is the status-line + Map<String,List<String>> map = new HashMap<>(); + for (Map.Entry<String,List<String>> e : messageHeaderMap.entrySet()) { + if (e.getKey() != null) { + map.put(e.getKey(), e.getValue()); + } + } + messageHeaderMap = map; + + assertHeadersEqual(messageHeaderMap, decoderMap1, + "messageHeaderMap not equal to decoderMap1"); + + // byte at a time + decoder = new Http1HeaderParser(); + List<ByteBuffer> buffers = IntStream.range(0, bytes.length) + .mapToObj(i -> ByteBuffer.wrap(bytes, i, 1)) + .collect(toList()); + while (decoder.parse(buffers.remove(0)) != true); + Map<String,List<String>> decoderMap2 = decoder.headers().map(); + assertEquals(available, buffers.size(), + "stream available not equals to remaining buffers"); + assertEquals(decoderMap1, decoderMap2, "decoder maps not equal"); + } + + @DataProvider(name = "errors") + public Object[][] errors() { + List<String> responses = new ArrayList<>(); + + // These responses are parsed, somewhat, by MessageHeaders but give + // nonsensible results. They, correctly, fail with the Http1HeaderParser. + String[] bad = + {// "HTTP/1.1 402 Payment Required\r\n" + + // "Content-Length: 65\r\n\r", // missing trailing LF //TODO: incomplete + + "HTTP/1.1 402 Payment Required\r\n" + + "Content-Length: 65\r\n\rT\r\n\r\nGGGGGG", + + "HTTP/1.1 200OK\r\n\rT", + + "HTTP/1.1 200OK\rT", + }; + Arrays.stream(bad).forEach(responses::add); + + return responses.stream().map(p -> new Object[] { p }).toArray(Object[][]::new); + } + + @Test(dataProvider = "errors", expectedExceptions = ProtocolException.class) + public void errors(String respString) throws ProtocolException { + byte[] bytes = respString.getBytes(US_ASCII); + Http1HeaderParser decoder = new Http1HeaderParser(); + ByteBuffer b = ByteBuffer.wrap(bytes); + decoder.parse(b); + } + + void assertHeadersEqual(Map<String,List<String>> expected, + Map<String,List<String>> actual, + String msg) { + + if (expected.equals(actual)) + return; + + assertEquals(expected.size(), actual.size(), + format("%s. Expected size %d, actual size %s. %nexpected= %s,%n actual=%s.", + msg, expected.size(), actual.size(), mapToString(expected), mapToString(actual))); + + for (Map.Entry<String,List<String>> e : expected.entrySet()) { + String key = e.getKey(); + List<String> values = e.getValue(); + + boolean found = false; + for (Map.Entry<String,List<String>> other: actual.entrySet()) { + if (key.equalsIgnoreCase(other.getKey())) { + found = true; + List<String> otherValues = other.getValue(); + assertEquals(values.size(), otherValues.size(), + format("%s. Expected list size %d, actual size %s", + msg, values.size(), otherValues.size())); + if (!values.containsAll(otherValues) && otherValues.containsAll(values)) + assertTrue(false, format("Lists are unequal [%s] [%s]", values, otherValues)); + break; + } + } + assertTrue(found, format("header name, %s, not found in %s", key, actual)); + } + } + + static String mapToString(Map<String,List<String>> map) { + StringBuilder sb = new StringBuilder(); + List<String> sortedKeys = new ArrayList(map.keySet()); + Collections.sort(sortedKeys); + for (String key : sortedKeys) { + List<String> values = map.get(key); + sb.append("\n\t" + key + " | " + values); + } + return sb.toString(); + } + + // --- + + /* Main entry point for standalone testing of the main functional test. */ + public static void main(String... args) throws Exception { + Http1HeaderParserTest test = new Http1HeaderParserTest(); + int count = 0; + for (Object[] objs : test.responses()) { + out.println("Testing " + count++ + ", " + objs[0]); + test.verifyHeaders((String) objs[0]); + } + for (Object[] objs : test.errors()) { + out.println("Testing " + count++ + ", " + objs[0]); + try { + test.errors((String) objs[0]); + throw new RuntimeException("Expected ProtocolException for " + objs[0]); + } catch (ProtocolException expected) { /* Ok */ } + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java index 7e9ca255795..9cb59d59d11 100644 --- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java @@ -24,6 +24,7 @@ package jdk.incubator.http; import jdk.incubator.http.internal.websocket.RawChannel; +import jdk.incubator.http.internal.websocket.WebSocketRequest; import org.testng.annotations.Test; import java.io.IOException; @@ -83,6 +84,7 @@ public class RawChannelTest { new TestServer(server).start(); final RawChannel chan = channelOf(port); + print("RawChannel is %s", String.valueOf(chan)); initialWriteStall.await(); // It's very important not to forget the initial bytes, possibly @@ -185,9 +187,21 @@ public class RawChannelTest { URI uri = URI.create("http://127.0.0.1:" + port + "/"); print("raw channel to %s", uri.toString()); HttpRequest req = HttpRequest.newBuilder(uri).build(); - HttpResponse<?> r = HttpClient.newHttpClient().send(req, discard(null)); - r.body(); - return ((HttpResponseImpl) r).rawChannel(); + // Switch on isWebSocket flag to prevent the connection from + // being returned to the pool. + ((WebSocketRequest)req).isWebSocket(true); + HttpClient client = HttpClient.newHttpClient(); + try { + HttpResponse<?> r = client.send(req, discard(null)); + r.body(); + return ((HttpResponseImpl) r).rawChannel(); + } finally { + // Need to hold onto the client until the RawChannel is + // created. This would not be needed if we had created + // a WebSocket, but here we are fiddling directly + // with the internals of HttpResponseImpl! + java.lang.ref.Reference.reachabilityFence(client); + } } private class TestServer extends Thread { // Powered by Slowpokes diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java deleted file mode 100644 index bf2c6d751e6..00000000000 --- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2017, 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. - */ -package jdk.incubator.http; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.channels.SocketChannel; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import org.testng.annotations.Test; -import jdk.incubator.http.internal.common.ByteBufferReference; - -@Test -public class ResponseHeadersTest { - - static final String BODY = - "This is the body dude,\r\n" - + "not a header!\r\n"; - - static final String MESSAGE_OK = - "HTTP/1.1 200 OK\r\n" - + "Content-Length: " + BODY.length() + "\r\n" - + "MY-Folding-Header: YES\r\n" - + " OR\r\n" - + " NO\r\n" - + "\r\n" - + BODY; - - static final String MESSAGE_NOK = - "HTTP/1.1 101 Switching Protocols\r\n" - + "\r\n"; - - //public static void main(String[] args) throws IOException { - // new ResponseHeadersTest().test(); - //} - - @Test - public void test() throws IOException { - testResponseHeaders(MESSAGE_OK); - testResponseHeaders(MESSAGE_NOK); - } - - /** - * Verifies that ResponseHeaders behave as we expect. - * @param msg The response string. - * @throws IOException should not happen. - */ - static void testResponseHeaders(String msg) throws IOException { - byte[] bytes = msg.getBytes("US-ASCII"); - ByteBuffer buffer = ByteBuffer.wrap(bytes); - - // Read status line - String statusLine = readStatusLine(buffer); - System.out.println("StatusLine: " + statusLine); - if (!statusLine.startsWith("HTTP/1.1")) { - throw new AssertionError("bad status line: " + statusLine); - } - - // We have two cases: - // - MESSAGE_OK: there will be some headers to read, - // - MESSAGE_NOK: there will be no headers to read. - HttpHeaders headers = createResponseHeaders(buffer); - - // Now get the expected length of the body - Optional<String> contentLengthValue = headers.firstValue("Content-length"); - int contentLength = contentLengthValue.map(Integer::parseInt).orElse(0); - - // We again have two cases: - // - MESSAGE_OK: there should be a Content-length: header - // - MESSAGE_NOK: there should be no Content-length: header - if (contentLengthValue.isPresent()) { - // MESSAGE_NOK has no headers and no body and therefore - // no Content-length: header. - if (MESSAGE_NOK.equals(msg)) { - throw new AssertionError("Content-length: header found in" - + " error 101 message"); - } - } else { - if (!MESSAGE_NOK.equals(msg)) { - throw new AssertionError("Content-length: header not found"); - } - } - - // Now read the remaining bytes. It should either be - // the empty string (MESSAGE_NOK) or BODY (MESSAGE_OK), - // and it should not contains any leading CR or LF" - String remaining = readRemainingBytes(buffer); - System.out.println("Body: <<<" + remaining + ">>>"); - if (remaining.length() != contentLength) { - throw new AssertionError("Unexpected body length: " + remaining.length() - + " expected " + contentLengthValue); - } - if (contentLengthValue.isPresent()) { - if (!BODY.equals(remaining)) { - throw new AssertionError("Body does not match!"); - } - } - } - - static String readRemainingBytes(ByteBuffer buffer) throws UnsupportedEncodingException { - byte[] res = new byte[buffer.limit() - buffer.position()]; - System.arraycopy(buffer.array(), buffer.position(), res, 0, res.length); - buffer.position(buffer.limit()); - return new String(res, "US-ASCII"); - } - - static String readStatusLine(ByteBuffer buffer) throws IOException { - buffer.mark(); - int p = buffer.position(); - while(buffer.hasRemaining()) { - char c = (char)buffer.get(); - if (c == '\r') { - c = (char)buffer.get(); - if (c == '\n') { - byte[] res = new byte[buffer.position() - p -2]; - System.arraycopy(buffer.array(), p, res, 0, res.length); - return new String(res, "US-ASCII"); - } - } - } - throw new IOException("Status line not found"); - } - - private static final class HttpConnectionStub extends HttpConnection { - public HttpConnectionStub() { - super(null, null); - } - @Override - public void connect() throws IOException, InterruptedException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - public CompletableFuture<Void> connectAsync() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - boolean connected() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - boolean isSecure() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - boolean isProxied() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - CompletableFuture<Void> whenReceivingResponse() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - SocketChannel channel() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - ConnectionPool.CacheKey cacheKey() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - long write(ByteBuffer[] buffers, int start, int number) throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - long write(ByteBuffer buffer) throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - void writeAsync(ByteBufferReference[] buffers) throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - void flushAsync() throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - public void close() { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - void shutdownInput() throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - void shutdownOutput() throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - @Override - protected ByteBuffer readImpl() throws IOException { - throw new AssertionError("Bad test assumption: should not have reached here!"); - } - } - - public static HttpHeaders createResponseHeaders(ByteBuffer buffer) - throws IOException{ - return new ResponseHeaders(new HttpConnectionStub(), buffer); - } -} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java new file mode 100644 index 00000000000..b818bcc71be --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import jdk.incubator.http.internal.common.Demand; +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SSLTube; +import jdk.incubator.http.internal.common.SequentialScheduler; +import jdk.incubator.http.internal.common.Utils; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +@Test +public class SSLEchoTubeTest extends AbstractSSLTubeTest { + + @Test + public void runWithEchoServer() throws IOException { + ExecutorService sslExecutor = Executors.newCachedThreadPool(); + + /* Start of wiring */ + /* Emulates an echo server */ + FlowTube server = crossOverEchoServer(sslExecutor); + + run(server, sslExecutor, allBytesReceived); + } + + /** + * Creates a cross-over FlowTube than can be plugged into a client-side + * SSLTube (in place of the SSLLoopbackSubscriber). + * Note that the only method that can be called on the return tube + * is connectFlows(). Calling any other method will trigger an + * InternalError. + * @param sslExecutor an executor + * @return a cross-over FlowTube connected to an EchoTube. + * @throws IOException + */ + private FlowTube crossOverEchoServer(Executor sslExecutor) throws IOException { + LateBindingTube crossOver = new LateBindingTube(); + FlowTube server = new SSLTube(createSSLEngine(false), + sslExecutor, + crossOver); + EchoTube echo = new EchoTube(6); + server.connectFlows(FlowTube.asTubePublisher(echo), FlowTube.asTubeSubscriber(echo)); + + return new CrossOverTube(crossOver); + } + + /** + * A cross-over FlowTube that makes it possible to reverse the direction + * of flows. The typical usage is to connect an two opposite SSLTube, + * one encrypting, one decrypting, to e.g. an EchoTube, with the help + * of a LateBindingTube: + * {@code + * client app => SSLTube => CrossOverTube <= LateBindingTube <= SSLTube <= EchoTube + * } + * <p> + * Note that the only method that can be called on the CrossOverTube is + * connectFlows(). Calling any other method will cause an InternalError to + * be thrown. + * Also connectFlows() can be called only once. + */ + private static final class CrossOverTube implements FlowTube { + final LateBindingTube tube; + CrossOverTube(LateBindingTube tube) { + this.tube = tube; + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + throw newInternalError(); + } + + @Override + public void connectFlows(TubePublisher writePublisher, TubeSubscriber readSubscriber) { + tube.start(writePublisher, readSubscriber); + } + + @Override + public boolean isFinished() { + return tube.isFinished(); + } + + Error newInternalError() { + InternalError error = new InternalError(); + error.printStackTrace(System.out); + return error; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + throw newInternalError(); + } + + @Override + public void onError(Throwable throwable) { + throw newInternalError(); + } + + @Override + public void onComplete() { + throw newInternalError(); + } + + @Override + public void onNext(List<ByteBuffer> item) { + throw newInternalError(); + } + } + + /** + * A late binding tube that makes it possible to create an + * SSLTube before the right-hand-side tube has been created. + * The typical usage is to make it possible to connect two + * opposite SSLTube (one encrypting, one decrypting) through a + * CrossOverTube: + * {@code + * client app => SSLTube => CrossOverTube <= LateBindingTube <= SSLTube <= EchoTube + * } + * <p> + * Note that this class only supports a single call to start(): it cannot be + * subscribed more than once from its left-hand-side (the cross over tube side). + */ + private static class LateBindingTube implements FlowTube { + + final CompletableFuture<Flow.Publisher<List<ByteBuffer>>> futurePublisher + = new CompletableFuture<>(); + final ConcurrentLinkedQueue<Consumer<Flow.Subscriber<? super List<ByteBuffer>>>> queue + = new ConcurrentLinkedQueue<>(); + AtomicReference<Flow.Subscriber<? super List<ByteBuffer>>> subscriberRef = new AtomicReference<>(); + SequentialScheduler scheduler = SequentialScheduler.synchronizedScheduler(this::loop); + AtomicReference<Throwable> errorRef = new AtomicReference<>(); + private volatile boolean finished; + private volatile boolean completed; + + + public void start(Flow.Publisher<List<ByteBuffer>> publisher, + Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + subscriberRef.set(subscriber); + futurePublisher.complete(publisher); + scheduler.runOrSchedule(); + } + + @Override + public boolean isFinished() { + return finished; + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + futurePublisher.thenAccept((p) -> p.subscribe(subscriber)); + scheduler.runOrSchedule(); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + queue.add((s) -> s.onSubscribe(subscription)); + scheduler.runOrSchedule(); + } + + @Override + public void onNext(List<ByteBuffer> item) { + queue.add((s) -> s.onNext(item)); + scheduler.runOrSchedule(); + } + + @Override + public void onError(Throwable throwable) { + System.out.println("LateBindingTube onError"); + throwable.printStackTrace(System.out); + queue.add((s) -> { + errorRef.compareAndSet(null, throwable); + try { + System.out.println("LateBindingTube subscriber onError: " + throwable); + s.onError(errorRef.get()); + } finally { + finished = true; + System.out.println("LateBindingTube finished"); + } + }); + scheduler.runOrSchedule(); + } + + @Override + public void onComplete() { + System.out.println("LateBindingTube completing"); + queue.add((s) -> { + completed = true; + try { + System.out.println("LateBindingTube complete subscriber"); + s.onComplete(); + } finally { + finished = true; + System.out.println("LateBindingTube finished"); + } + }); + scheduler.runOrSchedule(); + } + + private void loop() { + if (finished) { + scheduler.stop(); + return; + } + Flow.Subscriber<? super List<ByteBuffer>> subscriber = subscriberRef.get(); + if (subscriber == null) return; + try { + Consumer<Flow.Subscriber<? super List<ByteBuffer>>> s; + while ((s = queue.poll()) != null) { + s.accept(subscriber); + } + } catch (Throwable t) { + if (errorRef.compareAndSet(null, t)) { + onError(t); + } + } + } + } + + /** + * An echo tube that just echoes back whatever bytes it receives. + * This cannot be plugged to the right-hand-side of an SSLTube + * since handshake data cannot be simply echoed back, and + * application data most likely also need to be decrypted and + * re-encrypted. + */ + private static final class EchoTube implements FlowTube { + + private final static Object EOF = new Object(); + private final Executor executor = Executors.newSingleThreadExecutor(); + + private final Queue<Object> queue = new ConcurrentLinkedQueue<>(); + private final int maxQueueSize; + private final SequentialScheduler processingScheduler = + new SequentialScheduler(createProcessingTask()); + + /* Writing into this tube */ + private volatile long requested; + private Flow.Subscription subscription; + + /* Reading from this tube */ + private final Demand demand = new Demand(); + private final AtomicBoolean cancelled = new AtomicBoolean(); + private Flow.Subscriber<? super List<ByteBuffer>> subscriber; + + private EchoTube(int maxBufferSize) { + if (maxBufferSize < 1) + throw new IllegalArgumentException(); + this.maxQueueSize = maxBufferSize; + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + this.subscriber = subscriber; + System.out.println("EchoTube got subscriber: " + subscriber); + this.subscriber.onSubscribe(new InternalSubscription()); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + System.out.println("EchoTube request: " + maxQueueSize); + (this.subscription = subscription).request(requested = maxQueueSize); + } + + private void requestMore() { + Flow.Subscription s = subscription; + if (s == null || cancelled.get()) return; + long unfulfilled = queue.size() + --requested; + if (unfulfilled <= maxQueueSize/2) { + long req = maxQueueSize - unfulfilled; + requested += req; + s.request(req); + System.out.printf("EchoTube request: %s [requested:%s, queue:%s, unfulfilled:%s]%n", + req, requested-req, queue.size(), unfulfilled ); + } + } + + @Override + public void onNext(List<ByteBuffer> item) { + System.out.printf("EchoTube add %s [requested:%s, queue:%s]%n", + Utils.remaining(item), requested, queue.size()); + queue.add(item); + processingScheduler.deferOrSchedule(executor); + } + + @Override + public void onError(Throwable throwable) { + System.out.println("EchoTube add " + throwable); + queue.add(throwable); + processingScheduler.deferOrSchedule(executor); + } + + @Override + public void onComplete() { + System.out.println("EchoTube add EOF"); + queue.add(EOF); + processingScheduler.deferOrSchedule(executor); + } + + @Override + public boolean isFinished() { + return cancelled.get(); + } + + private class InternalSubscription implements Flow.Subscription { + + @Override + public void request(long n) { + System.out.println("EchoTube got request: " + n); + if (n <= 0) { + throw new InternalError(); + } + if (demand.increase(n)) { + processingScheduler.deferOrSchedule(executor); + } + } + + @Override + public void cancel() { + cancelled.set(true); + } + } + + @Override + public String toString() { + return "EchoTube"; + } + + int transmitted = 0; + private SequentialScheduler.RestartableTask createProcessingTask() { + return new SequentialScheduler.CompleteRestartableTask() { + + @Override + protected void run() { + try { + while (!cancelled.get()) { + Object item = queue.peek(); + if (item == null) { + System.out.printf("EchoTube: queue empty, requested=%s, demand=%s, transmitted=%s%n", + requested, demand.get(), transmitted); + requestMore(); + return; + } + try { + System.out.printf("EchoTube processing item, requested=%s, demand=%s, transmitted=%s%n", + requested, demand.get(), transmitted); + if (item instanceof List) { + if (!demand.tryDecrement()) { + System.out.println("EchoTube no demand"); + return; + } + @SuppressWarnings("unchecked") + List<ByteBuffer> bytes = (List<ByteBuffer>) item; + Object removed = queue.remove(); + assert removed == item; + System.out.println("EchoTube processing " + + Utils.remaining(bytes)); + transmitted++; + subscriber.onNext(bytes); + requestMore(); + } else if (item instanceof Throwable) { + cancelled.set(true); + Object removed = queue.remove(); + assert removed == item; + System.out.println("EchoTube processing " + item); + subscriber.onError((Throwable) item); + } else if (item == EOF) { + cancelled.set(true); + Object removed = queue.remove(); + assert removed == item; + System.out.println("EchoTube processing EOF"); + subscriber.onComplete(); + } else { + throw new InternalError(String.valueOf(item)); + } + } finally { + } + } + } catch(Throwable t) { + t.printStackTrace(); + throw t; + } + } + }; + } + } + } diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java new file mode 100644 index 00000000000..d9897a38ec0 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import jdk.incubator.http.internal.common.FlowTube; +import jdk.incubator.http.internal.common.SSLFlowDelegate; +import jdk.incubator.http.internal.common.Utils; +import org.testng.annotations.Test; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.SubmissionPublisher; +import java.util.concurrent.atomic.AtomicInteger; + +@Test +public class SSLTubeTest extends AbstractSSLTubeTest { + + @Test + public void runWithSSLLoopackServer() throws IOException { + ExecutorService sslExecutor = Executors.newCachedThreadPool(); + + /* Start of wiring */ + /* Emulates an echo server */ + SSLLoopbackSubscriber server = + new SSLLoopbackSubscriber((new SimpleSSLContext()).get(), + sslExecutor, + allBytesReceived); + server.start(); + + run(server, sslExecutor, allBytesReceived); + } + + /** + * This is a copy of the SSLLoopbackSubscriber used in FlowTest + */ + private static class SSLLoopbackSubscriber implements FlowTube { + private final BlockingQueue<ByteBuffer> buffer; + private final Socket clientSock; + private final SSLSocket serverSock; + private final Thread thread1, thread2, thread3; + private volatile Flow.Subscription clientSubscription; + private final SubmissionPublisher<List<ByteBuffer>> publisher; + private final CountDownLatch allBytesReceived; + + SSLLoopbackSubscriber(SSLContext ctx, + ExecutorService exec, + CountDownLatch allBytesReceived) throws IOException { + SSLServerSocketFactory fac = ctx.getServerSocketFactory(); + SSLServerSocket serv = (SSLServerSocket) fac.createServerSocket(0); + SSLParameters params = serv.getSSLParameters(); + params.setApplicationProtocols(new String[]{"proto2"}); + serv.setSSLParameters(params); + + + int serverPort = serv.getLocalPort(); + clientSock = new Socket("127.0.0.1", serverPort); + serverSock = (SSLSocket) serv.accept(); + this.buffer = new LinkedBlockingQueue<>(); + this.allBytesReceived = allBytesReceived; + thread1 = new Thread(this::clientWriter, "clientWriter"); + thread2 = new Thread(this::serverLoopback, "serverLoopback"); + thread3 = new Thread(this::clientReader, "clientReader"); + publisher = new SubmissionPublisher<>(exec, Flow.defaultBufferSize(), + this::handlePublisherException); + SSLFlowDelegate.Monitor.add(this::monitor); + } + + public void start() { + thread1.start(); + thread2.start(); + thread3.start(); + } + + private void handlePublisherException(Object o, Throwable t) { + System.out.println("Loopback Publisher exception"); + t.printStackTrace(System.out); + } + + private final AtomicInteger readCount = new AtomicInteger(); + + // reads off the SSLSocket the data from the "server" + private void clientReader() { + try { + InputStream is = clientSock.getInputStream(); + final int bufsize = randomRange(512, 16 * 1024); + System.out.println("clientReader: bufsize = " + bufsize); + while (true) { + byte[] buf = new byte[bufsize]; + int n = is.read(buf); + if (n == -1) { + System.out.println("clientReader close: read " + + readCount.get() + " bytes"); + System.out.println("clientReader: waiting signal to close publisher"); + allBytesReceived.await(); + System.out.println("clientReader: closing publisher"); + publisher.close(); + sleep(2000); + Utils.close(is, clientSock); + return; + } + ByteBuffer bb = ByteBuffer.wrap(buf, 0, n); + readCount.addAndGet(n); + publisher.submit(List.of(bb)); + } + } catch (Throwable e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + + // writes the encrypted data from SSLFLowDelegate to the j.n.Socket + // which is connected to the SSLSocket emulating a server. + private void clientWriter() { + long nbytes = 0; + try { + OutputStream os = + new BufferedOutputStream(clientSock.getOutputStream()); + + while (true) { + ByteBuffer buf = buffer.take(); + if (buf == SENTINEL) { + // finished + //Utils.sleep(2000); + System.out.println("clientWriter close: " + nbytes + " written"); + clientSock.shutdownOutput(); + System.out.println("clientWriter close return"); + return; + } + int len = buf.remaining(); + int written = writeToStream(os, buf); + assert len == written; + nbytes += len; + assert !buf.hasRemaining() + : "buffer has " + buf.remaining() + " bytes left"; + clientSubscription.request(1); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private int writeToStream(OutputStream os, ByteBuffer buf) throws IOException { + byte[] b = buf.array(); + int offset = buf.arrayOffset() + buf.position(); + int n = buf.limit() - buf.position(); + os.write(b, offset, n); + buf.position(buf.limit()); + os.flush(); + return n; + } + + private final AtomicInteger loopCount = new AtomicInteger(); + + public String monitor() { + return "serverLoopback: loopcount = " + loopCount.toString() + + " clientRead: count = " + readCount.toString(); + } + + // thread2 + private void serverLoopback() { + try { + InputStream is = serverSock.getInputStream(); + OutputStream os = serverSock.getOutputStream(); + final int bufsize = randomRange(512, 16 * 1024); + System.out.println("serverLoopback: bufsize = " + bufsize); + byte[] bb = new byte[bufsize]; + while (true) { + int n = is.read(bb); + if (n == -1) { + sleep(2000); + is.close(); + os.close(); + serverSock.close(); + return; + } + os.write(bb, 0, n); + os.flush(); + loopCount.addAndGet(n); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + + /** + * This needs to be called before the chain is subscribed. It can't be + * supplied in the constructor. + */ + public void setReturnSubscriber(Flow.Subscriber<List<ByteBuffer>> returnSubscriber) { + publisher.subscribe(returnSubscriber); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + clientSubscription = subscription; + clientSubscription.request(5); + } + + @Override + public void onNext(List<ByteBuffer> item) { + try { + for (ByteBuffer b : item) + buffer.put(b); + } catch (InterruptedException e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + Utils.close(clientSock); + } + + @Override + public void onComplete() { + try { + buffer.put(SENTINEL); + } catch (InterruptedException e) { + e.printStackTrace(); + Utils.close(clientSock); + } + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) { + publisher.subscribe(subscriber); + } + } + +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java index 9b091c1ce51..21ffc4d18e8 100644 --- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -76,7 +76,7 @@ public class SelectorTest { } } - @Test(timeOut = 10000) + @Test public void test() throws Exception { try (ServerSocket server = new ServerSocket(0)) { @@ -88,57 +88,58 @@ public class SelectorTest { t.start(); out.println("Started server thread"); - final RawChannel chan = getARawChannel(port); + try (RawChannel chan = getARawChannel(port)) { - chan.registerEvent(new RawChannel.RawEvent() { - @Override - public int interestOps() { - return SelectionKey.OP_READ; - } - - @Override - public void handle() { - readSomeBytes(chan); - out.printf("OP_READ\n"); - final int count = counter.get(); - if (count != 1) { - out.printf("OP_READ error counter = %d\n", count); - error = true; + chan.registerEvent(new RawChannel.RawEvent() { + @Override + public int interestOps() { + return SelectionKey.OP_READ; } - } - }); - chan.registerEvent(new RawChannel.RawEvent() { - @Override - public int interestOps() { - return SelectionKey.OP_WRITE; - } - - @Override - public void handle() { - out.printf("OP_WRITE\n"); - final int count = counter.get(); - if (count != 0) { - out.printf("OP_WRITE error counter = %d\n", count); - error = true; - } else { - ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT); - counter.incrementAndGet(); - try { - chan.write(new ByteBuffer[]{bb}, 0, 1); - } catch (IOException e) { - throw new UncheckedIOException(e); + @Override + public void handle() { + readSomeBytes(chan); + out.printf("OP_READ\n"); + final int count = counter.get(); + if (count != 1) { + out.printf("OP_READ error counter = %d\n", count); + error = true; } } - } + }); - }); - out.println("Events registered. Waiting"); - finishingGate.await(30, SECONDS); - if (error) - throw new RuntimeException("Error"); - else - out.println("No error"); + chan.registerEvent(new RawChannel.RawEvent() { + @Override + public int interestOps() { + return SelectionKey.OP_WRITE; + } + + @Override + public void handle() { + out.printf("OP_WRITE\n"); + final int count = counter.get(); + if (count != 0) { + out.printf("OP_WRITE error counter = %d\n", count); + error = true; + } else { + ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT); + counter.incrementAndGet(); + try { + chan.write(new ByteBuffer[]{bb}, 0, 1); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + }); + out.println("Events registered. Waiting"); + finishingGate.await(30, SECONDS); + if (error) + throw new RuntimeException("Error"); + else + out.println("No error"); + } } } @@ -146,6 +147,10 @@ public class SelectorTest { URI uri = URI.create("http://127.0.0.1:" + port + "/"); out.println("client connecting to " + uri.toString()); HttpRequest req = HttpRequest.newBuilder(uri).build(); + // Otherwise HttpClient will think this is an ordinary connection and + // thus all ordinary procedures apply to it, e.g. it must be put into + // the cache + ((HttpRequestImpl) req).isWebSocket(true); HttpResponse<?> r = defaultClient().send(req, discard(null)); r.body(); return ((HttpResponseImpl) r).rawChannel(); diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java new file mode 100644 index 00000000000..6cfcdf3f53e --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import org.testng.annotations.Test; +import jdk.incubator.http.internal.common.SubscriberWrapper; + +@Test +public class WrapperTest { + static final int LO_PRI = 1; + static final int HI_PRI = 2; + static final int NUM_HI_PRI = 240; + static final int BUFSIZE = 1016; + static final int BUFSIZE_INT = BUFSIZE/4; + static final int HI_PRI_FREQ = 40; + + static final int TOTAL = 10000; + //static final int TOTAL = 500; + + final SubmissionPublisher<List<ByteBuffer>> publisher; + final SubscriberWrapper sub1, sub2, sub3; + final ExecutorService executor = Executors.newCachedThreadPool(); + volatile int hipricount = 0; + + void errorHandler(Flow.Subscriber<? super List<ByteBuffer>> sub, Throwable t) { + System.err.printf("Exception from %s : %s\n", sub.toString(), t.toString()); + } + + public WrapperTest() { + publisher = new SubmissionPublisher<>(executor, 600, + (a, b) -> { + errorHandler(a, b); + }); + + CompletableFuture<Void> notif = new CompletableFuture<>(); + LastSubscriber ls = new LastSubscriber(notif); + sub1 = new Filter1(ls); + sub2 = new Filter2(sub1); + sub3 = new Filter2(sub2); + } + + public class Filter2 extends SubscriberWrapper { + Filter2(SubscriberWrapper wrapper) { + super(wrapper); + } + + // reverse the order of the bytes in each buffer + public void incoming(List<ByteBuffer> list, boolean complete) { + List<ByteBuffer> out = new LinkedList<>(); + for (ByteBuffer inbuf : list) { + int size = inbuf.remaining(); + ByteBuffer outbuf = ByteBuffer.allocate(size); + for (int i=size; i>0; i--) { + byte b = inbuf.get(i-1); + outbuf.put(b); + } + outbuf.flip(); + out.add(outbuf); + } + if (complete) System.out.println("Filter2.complete"); + outgoing(out, complete); + } + + protected long windowUpdate(long currval) { + return currval == 0 ? 1 : 0; + } + } + + volatile int filter1Calls = 0; // every third call we insert hi pri data + + ByteBuffer getHiPri(int val) { + ByteBuffer buf = ByteBuffer.allocate(8); + buf.putInt(HI_PRI); + buf.putInt(val); + buf.flip(); + return buf; + } + + volatile int hiPriAdded = 0; + + public class Filter1 extends SubscriberWrapper { + Filter1(Flow.Subscriber<List<ByteBuffer>> downstreamSubscriber) + { + super(); + subscribe(downstreamSubscriber); + } + + // Inserts up to NUM_HI_PRI hi priority buffers into flow + protected void incoming(List<ByteBuffer> in, boolean complete) { + if ((++filter1Calls % HI_PRI_FREQ) == 0 && (hiPriAdded++ < NUM_HI_PRI)) { + sub1.outgoing(getHiPri(hipricount++), false); + } + // pass data thru + if (complete) System.out.println("Filter1.complete"); + outgoing(in, complete); + } + + protected long windowUpdate(long currval) { + return currval == 0 ? 1 : 0; + } + } + + /** + * Final subscriber in the chain. Compares the data sent by the original + * publisher. + */ + static public class LastSubscriber implements Flow.Subscriber<List<ByteBuffer>> { + volatile Flow.Subscription subscription; + volatile int hipriCounter=0; + volatile int lopriCounter=0; + final CompletableFuture<Void> cf; + + LastSubscriber(CompletableFuture<Void> cf) { + this.cf = cf; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; + subscription.request(50); // say + } + + private void error(String...args) { + StringBuilder sb = new StringBuilder(); + for (String s : args) { + sb.append(s); + sb.append(' '); + } + String msg = sb.toString(); + System.out.println("Error: " + msg); + RuntimeException e = new RuntimeException(msg); + cf.completeExceptionally(e); + subscription.cancel(); // This is where we need a variant that include exception + } + + private void check(ByteBuffer buf) { + int type = buf.getInt(); + if (type == HI_PRI) { + // check next int is hi pri counter + int c = buf.getInt(); + if (c != hipriCounter) + error("hi pri counter", Integer.toString(c), Integer.toString(hipriCounter)); + hipriCounter++; + } else { + while (buf.hasRemaining()) { + if (buf.getInt() != lopriCounter) + error("lo pri counter", Integer.toString(lopriCounter)); + lopriCounter++; + } + } + } + + @Override + public void onNext(List<ByteBuffer> items) { + for (ByteBuffer item : items) + check(item); + subscription.request(1); + } + + @Override + public void onError(Throwable throwable) { + error(throwable.getMessage()); + } + + @Override + public void onComplete() { + if (hipriCounter != NUM_HI_PRI) + error("hi pri at end wrong", Integer.toString(hipriCounter), Integer.toString(NUM_HI_PRI)); + else { + System.out.println("LastSubscriber.complete"); + cf.complete(null); // success + } + } + } + + List<ByteBuffer> getBuffer(int c) { + ByteBuffer buf = ByteBuffer.allocate(BUFSIZE+4); + buf.putInt(LO_PRI); + for (int i=0; i<BUFSIZE_INT; i++) { + buf.putInt(c++); + } + buf.flip(); + return List.of(buf); + } + + boolean errorTest = false; + + @Test + public void run() throws InterruptedException { + try { + CompletableFuture<Void> completion = sub3.completion(); + publisher.subscribe(sub3); + // now submit a load of data + int counter = 0; + for (int i = 0; i < TOTAL; i++) { + List<ByteBuffer> bufs = getBuffer(counter); + //if (i==2) + //bufs.get(0).putInt(41, 1234); // error + counter += BUFSIZE_INT; + publisher.submit(bufs); + //if (i % 1000 == 0) + //Thread.sleep(1000); + //if (i == 99) { + //publisher.closeExceptionally(new RuntimeException("Test error")); + //errorTest = true; + //break; + //} + } + if (!errorTest) { + publisher.close(); + } + System.out.println("Publisher completed"); + completion.join(); + System.out.println("Subscribers completed ok"); + } finally { + executor.shutdownNow(); + } + } + + static void display(CompletableFuture<?> cf) { + System.out.print (cf); + if (!cf.isDone()) + return; + try { + cf.join(); // wont block + } catch (Exception e) { + System.out.println(" " + e); + } + } + +/* + public static void main(String[] args) throws InterruptedException { + WrapperTest test = new WrapperTest(); + test.run(); + } +*/ +} diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java new file mode 100644 index 00000000000..ab291da4a65 --- /dev/null +++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2017, 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. + */ + +package jdk.incubator.http.internal.common; + +import org.testng.annotations.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicReference; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class DemandTest { + + @Test + public void test01() { + assertTrue(new Demand().isFulfilled()); + } + + @Test + public void test011() { + Demand d = new Demand(); + d.increase(3); + d.decreaseAndGet(3); + assertTrue(d.isFulfilled()); + } + + @Test + public void test02() { + Demand d = new Demand(); + d.increase(1); + assertFalse(d.isFulfilled()); + } + + @Test + public void test03() { + Demand d = new Demand(); + d.increase(3); + assertEquals(d.decreaseAndGet(3), 3); + } + + @Test + public void test04() { + Demand d = new Demand(); + d.increase(3); + assertEquals(d.decreaseAndGet(5), 3); + } + + @Test + public void test05() { + Demand d = new Demand(); + d.increase(7); + assertEquals(d.decreaseAndGet(4), 4); + } + + @Test + public void test06() { + Demand d = new Demand(); + assertEquals(d.decreaseAndGet(3), 0); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test07() { + Demand d = new Demand(); + d.increase(0); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test08() { + Demand d = new Demand(); + d.increase(-1); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test09() { + Demand d = new Demand(); + d.increase(10); + d.decreaseAndGet(0); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test10() { + Demand d = new Demand(); + d.increase(13); + d.decreaseAndGet(-3); + } + + @Test + public void test11() { + Demand d = new Demand(); + d.increase(1); + assertTrue(d.tryDecrement()); + } + + @Test + public void test12() { + Demand d = new Demand(); + d.increase(2); + assertTrue(d.tryDecrement()); + } + + @Test + public void test14() { + Demand d = new Demand(); + assertFalse(d.tryDecrement()); + } + + @Test + public void test141() { + Demand d = new Demand(); + d.increase(Long.MAX_VALUE); + assertFalse(d.isFulfilled()); + } + + @Test + public void test142() { + Demand d = new Demand(); + d.increase(Long.MAX_VALUE); + d.increase(1); + assertFalse(d.isFulfilled()); + } + + @Test + public void test143() { + Demand d = new Demand(); + d.increase(Long.MAX_VALUE); + d.increase(1); + assertFalse(d.isFulfilled()); + } + + @Test + public void test144() { + Demand d = new Demand(); + d.increase(Long.MAX_VALUE); + d.increase(Long.MAX_VALUE); + d.decreaseAndGet(3); + d.decreaseAndGet(5); + assertFalse(d.isFulfilled()); + } + + @Test + public void test145() { + Demand d = new Demand(); + d.increase(Long.MAX_VALUE); + d.decreaseAndGet(Long.MAX_VALUE); + assertTrue(d.isFulfilled()); + } + + @Test(invocationCount = 32) + public void test15() throws InterruptedException { + int N = Math.max(2, Runtime.getRuntime().availableProcessors() + 1); + int M = ((N + 1) * N) / 2; // 1 + 2 + 3 + ... N + Demand d = new Demand(); + d.increase(M); + CyclicBarrier start = new CyclicBarrier(N); + CountDownLatch stop = new CountDownLatch(N); + AtomicReference<Throwable> error = new AtomicReference<>(); + for (int i = 0; i < N; i++) { + int j = i + 1; + new Thread(() -> { + try { + start.await(); + } catch (Exception e) { + error.compareAndSet(null, e); + } + try { + assertEquals(d.decreaseAndGet(j), j); + } catch (Throwable t) { + error.compareAndSet(null, t); + } finally { + stop.countDown(); + } + }).start(); + } + stop.await(); + assertTrue(d.isFulfilled()); + assertEquals(error.get(), null); + } +} From a53a1b78449e55470b5543488e0416dbb263f5b3 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Wed, 6 Dec 2017 11:43:50 -0800 Subject: [PATCH 122/165] 8191078: Wrong "Package not found" warning Reviewed-by: jjg, jlahoda --- .../sun/tools/javac/model/JavacElements.java | 15 +++- .../tool/testPackages/TestPackages.java | 77 +++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java index d8d4687a1db..324b0b13240 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java @@ -197,9 +197,18 @@ public class JavacElements implements Elements { for (ModuleSymbol msym : modules.allModules()) { S sym = nameToSymbol(msym, nameStr, clazz); - if (sym != null) { - if (!allowModules || clazz == ClassSymbol.class || !sym.members().isEmpty()) { - //do not add packages without members: + if (sym == null) + continue; + + if (clazz == ClassSymbol.class) { + // Always include classes + found.add(sym); + } else if (clazz == PackageSymbol.class) { + // In module mode, ignore the "spurious" empty packages that "enclose" module-specific packages. + // For example, if a module contains classes or package info in package p.q.r, it will also appear + // to have additional packages p.q and p, even though these packages have no content other + // than the subpackage. We don't want those empty packages showing up in searches for p or p.q. + if (!sym.members().isEmpty() || ((PackageSymbol) sym).package_info != null) { found.add(sym); } } diff --git a/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java b/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java new file mode 100644 index 00000000000..5f20a2d26c5 --- /dev/null +++ b/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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 8191078 + * @summary ensure that javadoc considers packages correctly + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.javadoc/jdk.javadoc.internal.api + * jdk.javadoc/jdk.javadoc.internal.tool + * @library /tools/lib + * @build toolbox.JavadocTask toolbox.TestRunner toolbox.ToolBox + * @run main TestPackages + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import toolbox.*; +import toolbox.Task.Expect; +import toolbox.Task.OutputKind; +import toolbox.Task.Result; + +public class TestPackages extends TestRunner { + + final ToolBox tb = new ToolBox(); + + public TestPackages() { + super(System.err); + } + + public static void main(String[] args) throws Exception { + TestPackages t = new TestPackages(); + t.runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testEmptyPackage(Path base) throws Exception { + Files.createDirectories(base); + tb.writeFile(base.resolve("p1/package-info.java"), "package p1;\n"); + tb.writeFile(base.resolve("p2/A.java"), "package p2;\npublic class A {}\n"); + + Path outDir = base.resolve("out"); + Files.createDirectory(outDir); + JavadocTask task = new JavadocTask(tb); + task = task.outdir(outDir).sourcepath(base).options("p1", "p2"); + Result r = task.run(Expect.SUCCESS); + List<String> list = tb.grep(".*warning.*not found.*", r.getOutputLines(OutputKind.DIRECT)); + if (!list.isEmpty()) { + throw new Exception("Found warning: " + list.get(0)); + } + } +} From 5eafe731b056ce2eccec3b29e90f578441c757db Mon Sep 17 00:00:00 2001 From: Patrick Reinhart <patrick@reini.net> Date: Wed, 6 Dec 2017 14:39:15 -0800 Subject: [PATCH 123/165] 8191706: Add Reader::transferTo(Writer) Reviewed-by: alanb, bpb, briangoetz --- .../share/classes/java/io/Reader.java | 43 ++- test/jdk/java/io/Reader/TransferTo.java | 353 ++++++++++++++++++ 2 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 test/jdk/java/io/Reader/TransferTo.java diff --git a/src/java.base/share/classes/java/io/Reader.java b/src/java.base/share/classes/java/io/Reader.java index 13e90d76ea6..a1fada4952e 100644 --- a/src/java.base/share/classes/java/io/Reader.java +++ b/src/java.base/share/classes/java/io/Reader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2017, 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,6 +26,8 @@ package java.io; +import java.util.Objects; + /** * Abstract class for reading character streams. The only methods that a * subclass must implement are read(char[], int, int) and close(). Most @@ -50,6 +52,8 @@ package java.io; public abstract class Reader implements Readable, Closeable { + private static final int TRANSFER_BUFFER_SIZE = 8192; + /** * The object used to synchronize operations on this stream. For * efficiency, a character-stream object may use an object other than @@ -262,4 +266,41 @@ public abstract class Reader implements Readable, Closeable { */ public abstract void close() throws IOException; + /** + * Reads all characters from this reader and writes the characters to the + * given writer in the order that they are read. On return, this reader + * will be at end of the stream. This method does not close either reader + * or writer. + * <p> + * This method may block indefinitely reading from the reader, or + * writing to the writer. The behavior for the case where the reader + * and/or writer is <i>asynchronously closed</i>, or the thread + * interrupted during the transfer, is highly reader and writer + * specific, and therefore not specified. + * <p> + * If an I/O error occurs reading from the reader or writing to the + * writer, then it may do so after some characters have been read or + * written. Consequently the reader may not be at end of the stream and + * one, or both, streams may be in an inconsistent state. It is strongly + * recommended that both streams be promptly closed if an I/O error occurs. + * + * @param out the writer, non-null + * @return the number of characters transferred + * @throws IOException if an I/O error occurs when reading or writing + * @throws NullPointerException if {@code out} is {@code null} + * + * @since 10 + */ + public long transferTo(Writer out) throws IOException { + Objects.requireNonNull(out, "out"); + long transferred = 0; + char[] buffer = new char[TRANSFER_BUFFER_SIZE]; + int nRead; + while ((nRead = read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) { + out.write(buffer, 0, nRead); + transferred += nRead; + } + return transferred; + } + } diff --git a/test/jdk/java/io/Reader/TransferTo.java b/test/jdk/java/io/Reader/TransferTo.java new file mode 100644 index 00000000000..9ed409443b6 --- /dev/null +++ b/test/jdk/java/io/Reader/TransferTo.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2017, 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 source 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 source 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 Franklsource 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. + */ + +import java.io.*; +import java.util.Arrays; +import java.util.Random; + +import jdk.test.lib.RandomFactory; + +import static java.lang.String.format; + +/* + * @test + * @bug 8191706 + * @summary tests whether java.io.Reader.transferTo conforms to its + * contract defined source the javadoc + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run main TransferTo + * @key randomness + * @author Patrick Reinhart + */ +public class TransferTo { + + private static Random generator = RandomFactory.getRandom(); + + public static void main(String[] args) throws IOException { + ifOutIsNullThenNpeIsThrown(); + ifExceptionInInputNeitherStreamIsClosed(); + ifExceptionInOutputNeitherStreamIsClosed(); + onReturnNeitherStreamIsClosed(); + onReturnInputIsAtEnd(); + contents(); + } + + private static void ifOutIsNullThenNpeIsThrown() throws IOException { + try (Reader in = input()) { + assertThrowsNPE(() -> in.transferTo(null), "out"); + } + + try (Reader in = input((char) 1)) { + assertThrowsNPE(() -> in.transferTo(null), "out"); + } + + try (Reader in = input((char) 1, (char) 2)) { + assertThrowsNPE(() -> in.transferTo(null), "out"); + } + + Reader in = null; + try { + Reader fin = in = new ThrowingReader(); + // null check should precede everything else: + // Reader shouldn't be touched if Writer is null + assertThrowsNPE(() -> fin.transferTo(null), "out"); + } finally { + if (in != null) + try { + in.close(); + } catch (IOException ignored) { } + } + } + + private static void ifExceptionInInputNeitherStreamIsClosed() + throws IOException { + transferToThenCheckIfAnyClosed(input(0, new char[]{1, 2, 3}), output()); + transferToThenCheckIfAnyClosed(input(1, new char[]{1, 2, 3}), output()); + transferToThenCheckIfAnyClosed(input(2, new char[]{1, 2, 3}), output()); + } + + private static void ifExceptionInOutputNeitherStreamIsClosed() + throws IOException { + transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(0)); + transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(1)); + transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(2)); + } + + private static void transferToThenCheckIfAnyClosed(Reader input, + Writer output) + throws IOException { + try (CloseLoggingReader in = new CloseLoggingReader(input); + CloseLoggingWriter out = + new CloseLoggingWriter(output)) { + boolean thrown = false; + try { + in.transferTo(out); + } catch (IOException ignored) { + thrown = true; + } + if (!thrown) + throw new AssertionError(); + + if (in.wasClosed() || out.wasClosed()) { + throw new AssertionError(); + } + } + } + + private static void onReturnNeitherStreamIsClosed() + throws IOException { + try (CloseLoggingReader in = + new CloseLoggingReader(input(new char[]{1, 2, 3})); + CloseLoggingWriter out = + new CloseLoggingWriter(output())) { + + in.transferTo(out); + + if (in.wasClosed() || out.wasClosed()) { + throw new AssertionError(); + } + } + } + + private static void onReturnInputIsAtEnd() throws IOException { + try (Reader in = input(new char[]{1, 2, 3}); + Writer out = output()) { + + in.transferTo(out); + + if (in.read() != -1) { + throw new AssertionError(); + } + } + } + + private static void contents() throws IOException { + checkTransferredContents(new char[0]); + checkTransferredContents(createRandomChars(1024, 4096)); + // to span through several batches + checkTransferredContents(createRandomChars(16384, 16384)); + } + + private static void checkTransferredContents(char[] chars) + throws IOException { + try (Reader in = input(chars); + StringWriter out = new StringWriter()) { + in.transferTo(out); + + char[] outChars = out.toString().toCharArray(); + if (!Arrays.equals(chars, outChars)) { + throw new AssertionError( + format("chars.length=%s, outChars.length=%s", + chars.length, outChars.length)); + } + } + } + + private static char[] createRandomChars(int min, int maxRandomAdditive) { + char[] chars = new char[min + generator.nextInt(maxRandomAdditive)]; + for (int index=0; index<chars.length; index++) { + chars[index] = (char)generator.nextInt(); + } + return chars; + } + + private static Writer output() { + return output(-1); + } + + private static Writer output(int exceptionPosition) { + return new Writer() { + + int pos; + + @Override + public void write(int b) throws IOException { + if (pos++ == exceptionPosition) + throw new IOException(); + } + + @Override + public void write(char[] chars, int off, int len) throws IOException { + for (int i=0; i<len; i++) { + write(chars[off + i]); + } + } + + @Override + public Writer append(CharSequence csq, int start, int end) throws IOException { + for (int i = start; i < end; i++) { + write(csq.charAt(i)); + } + return this; + } + + @Override + public void flush() throws IOException { + } + + @Override + public void close() throws IOException { + } + }; + } + + private static Reader input(char... chars) { + return input(-1, chars); + } + + private static Reader input(int exceptionPosition, char... chars) { + return new Reader() { + + int pos; + + @Override + public int read() throws IOException { + if (pos == exceptionPosition) { + throw new IOException(); + } + + if (pos >= chars.length) + return -1; + return chars[pos++]; + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + int c = read(); + if (c == -1) { + return -1; + } + cbuf[off] = (char)c; + + int i = 1; + for (; i < len ; i++) { + c = read(); + if (c == -1) { + break; + } + cbuf[off + i] = (char)c; + } + return i; + } + + @Override + public void close() throws IOException { + } + }; + } + + private static class ThrowingReader extends Reader { + + boolean closed; + + @Override + public int read(char[] b, int off, int len) throws IOException { + throw new IOException(); + } + + @Override + public void close() throws IOException { + if (!closed) { + closed = true; + throw new IOException(); + } + } + @Override + public int read() throws IOException { + throw new IOException(); + } + } + + private static class CloseLoggingReader extends FilterReader { + + boolean closed; + + CloseLoggingReader(Reader in) { + super(in); + } + + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + + boolean wasClosed() { + return closed; + } + } + + private static class CloseLoggingWriter extends FilterWriter { + + boolean closed; + + CloseLoggingWriter(Writer out) { + super(out); + } + + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + + boolean wasClosed() { + return closed; + } + } + + public interface Thrower { + public void run() throws Throwable; + } + + public static void assertThrowsNPE(Thrower thrower, String message) { + assertThrows(thrower, NullPointerException.class, message); + } + + public static <T extends Throwable> void assertThrows(Thrower thrower, + Class<T> throwable, + String message) { + Throwable thrown; + try { + thrower.run(); + thrown = null; + } catch (Throwable caught) { + thrown = caught; + } + + if (!throwable.isInstance(thrown)) { + String caught = thrown == null ? + "nothing" : thrown.getClass().getCanonicalName(); + throw new AssertionError( + format("Expected to catch %s, but caught %s", + throwable, caught), thrown); + } + + if (thrown != null && !message.equals(thrown.getMessage())) { + throw new AssertionError( + format("Expected exception message to be '%s', but it's '%s'", + message, thrown.getMessage())); + } + } +} From 7892014147e59305ddb25f6335e5563e51b417e7 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Wed, 6 Dec 2017 15:14:13 -0800 Subject: [PATCH 124/165] 8192933: Wrong generic type parameter in serialized form javadoc Reviewed-by: jjg --- .../formats/html/HtmlSerialFieldWriter.java | 27 +++++++----- .../doclets/toolkit/SerializedFormWriter.java | 16 +++++-- .../builders/SerializedFormBuilder.java | 3 +- .../TestSerializedForm.java | 21 +++++++++- .../testSerializedForm/pkg2/Fields.java | 42 +++++++++++++++++++ 5 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java index 82bad7e9e92..e3d2aa42658 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java @@ -29,6 +29,7 @@ import java.util.*; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; import com.sun.source.doctree.DocTree; @@ -115,18 +116,10 @@ public class HtmlSerialFieldWriter extends FieldWriterImpl return li; } - /** - * Add the member header. - * - * @param fieldType the class document to be listed - * @param fieldTypeStr the string for the field type to be documented - * @param fieldDimensions the dimensions of the field string to be added - * @param fieldName name of the field to be added - * @param contentTree the content tree to which the member header will be added - */ + @Override public void addMemberHeader(TypeElement fieldType, String fieldTypeStr, String fieldDimensions, String fieldName, Content contentTree) { - Content nameContent = new RawHtml(fieldName); + Content nameContent = new StringContent(fieldName); Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, nameContent); contentTree.addContent(heading); Content pre = new HtmlTree(HtmlTag.PRE); @@ -142,6 +135,20 @@ public class HtmlSerialFieldWriter extends FieldWriterImpl contentTree.addContent(pre); } + @Override + public void addMemberHeader(TypeMirror fieldType, String fieldName, Content contentTree) { + Content nameContent = new StringContent(fieldName); + Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, nameContent); + contentTree.addContent(heading); + Content pre = new HtmlTree(HtmlTag.PRE); + Content fieldContent = writer.getLink(new LinkInfoImpl( + configuration, LinkInfoImpl.Kind.SERIAL_MEMBER, fieldType)); + pre.addContent(fieldContent); + pre.addContent(" "); + pre.addContent(fieldName); + contentTree.addContent(pre); + } + /** * Add the deprecated information for this member. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java index 7fd1c581186..af9293973f7 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -28,6 +28,7 @@ package jdk.javadoc.internal.doclets.toolkit; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; import com.sun.source.doctree.DocTree; import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException; @@ -227,8 +228,8 @@ public interface SerializedFormWriter { * Adds the member header. * * @param fieldType the type of the field - * @param fieldTypeStr the type of the field in string format. We will - * print this out if we can't link to the type + * @param fieldTypeStr the type of the field in string format, used + * only if the type cannot be linked * @param fieldDimensions the dimensions of the field * @param fieldName the name of the field * @param contentTree content tree to which the member header will be added @@ -236,6 +237,15 @@ public interface SerializedFormWriter { public void addMemberHeader(TypeElement fieldType, String fieldTypeStr, String fieldDimensions, String fieldName, Content contentTree); + /** + * Adds the member header. + * + * @param fieldType the type of the field + * @param fieldName the name of the field + * @param contentTree content tree to which the member header will be added + */ + public void addMemberHeader(TypeMirror fieldType, String fieldName, Content contentTree); + /** * Check to see if overview details should be printed. If * nocomment option set or if there is no text to be printed diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java index 042ac2bd52f..57a707a1e09 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java @@ -450,8 +450,7 @@ public class SerializedFormBuilder extends AbstractBuilder { protected void buildFieldSubHeader(Content fieldsContentTree) { if (!utils.definesSerializableFields(currentTypeElement)) { VariableElement field = (VariableElement) currentMember; - fieldWriter.addMemberHeader(utils.asTypeElement(field.asType()), - utils.getTypeName(field.asType(), false), utils.getDimension(field.asType()), + fieldWriter.addMemberHeader(field.asType(), utils.getSimpleName(field), fieldsContentTree); } diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java index ba8bec7c0fd..496eec4ed38 100644 --- a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java +++ b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2017, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 4341304 4485668 4966728 8032066 8071982 + * @bug 4341304 4485668 4966728 8032066 8071982 8192933 * @summary Test that methods readResolve and writeReplace show * up in serialized-form.html the same way that readObject and writeObject do. * If the doclet includes readResolve and writeReplace in the serialized-form @@ -114,4 +114,21 @@ public class TestSerializedForm extends JavadocTester { + "title=\"class in pkg1\">pkg1.PublicExcludeInnerClass.PubInnerClass</a> " + "extends java.lang.Object implements Serializable</h3>"); } + + @Test + void test2() { + javadoc("-private", + "-d", "out-2", + "-sourcepath", testSrc, + "pkg2"); + checkExit(Exit.OK); + + checkOrder("serialized-form.html", + "int[] a1", + "int[][] a2", + "<a href=\"pkg2/Fields.html\" title=\"class in pkg2\">Fields</a>[][] doubleArray", + "<a href=\"pkg2/Fields.html\" title=\"class in pkg2\">Fields</a>[] singleArray", + "java.lang.Class<<a href=\"pkg2/Fields.html\" " + + "title=\"type parameter in Fields\">E</a>> someClass"); + } } diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java b/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java new file mode 100644 index 00000000000..6d8fad00aee --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, 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. + */ + +package pkg2; + +public class Fields<E> implements java.io.Serializable { + /** Some doc. */ + public Class<E> someClass; + + /** a primitive array */ + private int[] a1; + + /** a two dimensional primitive array */ + private int[][] a2; + + /** a single object array */ + private Fields[] singleArray; + + /** a double object array */ + private Fields[][] doubleArray; + +} From c042b7af4cc12186f0765c632762e05b584f2cfa Mon Sep 17 00:00:00 2001 From: Roland Westrelin <roland@openjdk.org> Date: Sat, 2 Dec 2017 13:50:04 +0100 Subject: [PATCH 125/165] 8191950: assertion failed: no insertions allowed Check for dead loops before incremental inlining. Reviewed-by: thartmann --- src/hotspot/share/opto/callGenerator.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index a5aaa552fee..3a0dd830ac1 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -362,6 +362,20 @@ void LateInlineCallGenerator::do_late_inline() { return; } + // check for unreachable loop + CallProjections callprojs; + call->extract_projections(&callprojs, true); + if (callprojs.fallthrough_catchproj == call->in(0) || + callprojs.catchall_catchproj == call->in(0) || + callprojs.fallthrough_memproj == call->in(TypeFunc::Memory) || + callprojs.catchall_memproj == call->in(TypeFunc::Memory) || + callprojs.fallthrough_ioproj == call->in(TypeFunc::I_O) || + callprojs.catchall_ioproj == call->in(TypeFunc::I_O) || + (callprojs.resproj != NULL && call->find_edge(callprojs.resproj) != -1) || + (callprojs.exobj != NULL && call->find_edge(callprojs.exobj) != -1)) { + return; + } + Compile* C = Compile::current(); // Remove inlined methods from Compiler's lists. if (call->is_macro()) { From 3af68346dc52060883d9c396bb1edb638cd3be85 Mon Sep 17 00:00:00 2001 From: Harsha Wardhana B <hb@openjdk.org> Date: Tue, 5 Dec 2017 21:26:11 +0530 Subject: [PATCH 126/165] 8192909: Invalid username or password in HashedPasswordFileTest.java Reviewed-by: clanger, dfuchs --- .../security/HashedPasswordFileTest.java | 87 +++++++++---------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/test/jdk/javax/management/security/HashedPasswordFileTest.java b/test/jdk/javax/management/security/HashedPasswordFileTest.java index cae574c36a6..4ae4981c2e5 100644 --- a/test/jdk/javax/management/security/HashedPasswordFileTest.java +++ b/test/jdk/javax/management/security/HashedPasswordFileTest.java @@ -31,43 +31,26 @@ * */ -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import jdk.test.lib.Utils; +import jdk.test.lib.process.ProcessTools; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; + +import javax.management.MBeanServer; +import javax.management.remote.*; +import java.io.*; import java.lang.management.ManagementFactory; -import java.net.MalformedURLException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermission; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.HashSet; +import java.util.*; import java.util.List; -import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.concurrent.*; -import javax.management.MBeanServer; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXConnectorServer; -import javax.management.remote.JMXConnectorServerFactory; -import javax.management.remote.JMXServiceURL; - -import org.testng.Assert; -import org.testng.annotations.Test; -import org.testng.annotations.AfterClass; - -import jdk.test.lib.Utils; -import jdk.test.lib.process.ProcessTools; @Test public class HashedPasswordFileTest { @@ -96,13 +79,12 @@ public class HashedPasswordFileTest { "SHA3-512" }; - private final Random rnd = new Random(); private final Random random = Utils.getRandomInstance(); private JMXConnectorServer cs; private String randomWord() { - int idx = rnd.nextInt(randomWords.length); + int idx = random.nextInt(randomWords.length); return randomWords[idx]; } @@ -146,9 +128,12 @@ public class HashedPasswordFileTest { BufferedWriter br; try (FileWriter fw = new FileWriter(file)) { br = new BufferedWriter(fw); - int numentries = rnd.nextInt(5) + 3; + int numentries = random.nextInt(5) + 3; for (int i = 0; i < numentries; i++) { - String username = randomWord(); + String username; + do { + username = randomWord(); + } while (props.get(username) != null); String password = randomWord(); props.put(username, password); br.write(username + " " + password + "\n"); @@ -182,11 +167,14 @@ public class HashedPasswordFileTest { BufferedWriter br; try (FileWriter fw = new FileWriter(file)) { br = new BufferedWriter(fw); - int numentries = rnd.nextInt(5) + 3; + int numentries = random.nextInt(5) + 3; for (int i = 0; i < numentries; i++) { - String username = randomWord(); + String username; + do { + username = randomWord(); + } while (props.get(username) != null); String password = randomWord(); - String alg = hashAlgs[rnd.nextInt(hashAlgs.length)]; + String alg = hashAlgs[random.nextInt(hashAlgs.length)]; String[] b64str = getHash(alg, password); br.write(username + " " + b64str[0] + " " + b64str[1] + " " + alg + "\n"); props.put(username, password); @@ -307,7 +295,7 @@ public class HashedPasswordFileTest { JMXServiceURL serverUrl = createServerSide(true); Assert.assertEquals(isPasswordFileHashed(), false); // create random number of clients - int numClients = rnd.nextInt(20) + 10; + int numClients = random.nextInt(20) + 10; List<Future> futures = new ArrayList<>(); ExecutorService executor = Executors.newFixedThreadPool(numClients); for (int i = 0; i < numClients; i++) { @@ -355,32 +343,41 @@ public class HashedPasswordFileTest { sbuild.append(line).append("\n"); continue; } - String[] tokens = line.split("\\s+"); + // Change password for random entries - if ((tokens.length == 4 || tokens.length == 3) && rnd.nextBoolean()) { - String password = randomWord(); - credentials.put(tokens[0], password); - sbuild.append(tokens[0]).append(" ").append(password).append("\n"); + if (random.nextBoolean()) { + String[] tokens = line.split("\\s+"); + if ((tokens.length == 4 || tokens.length == 3)) { + String password = randomWord(); + credentials.put(tokens[0], password); + sbuild.append(tokens[0]).append(" ").append(password).append("\n"); + } } else { sbuild.append(line).append("\n"); } } // Add new entries in clear - int newentries = rnd.nextInt(2) + 3; + int newentries = random.nextInt(2) + 3; for (int i = 0; i < newentries; i++) { - String username = randomWord(); + String username; + do { + username = randomWord(); + } while (credentials.get(username) != null); String password = randomWord(); credentials.put(username, password); sbuild.append(username).append(" ").append(password).append("\n"); } // Add new entries as a hash - int numentries = rnd.nextInt(2) + 3; + int numentries = random.nextInt(2) + 3; for (int i = 0; i < numentries; i++) { - String username = randomWord(); + String username; + do { + username = randomWord(); + } while (credentials.get(username) != null); String password = randomWord(); - String alg = hashAlgs[rnd.nextInt(hashAlgs.length)]; + String alg = hashAlgs[random.nextInt(hashAlgs.length)]; String[] b64str = getHash(alg, password); credentials.put(username, password); sbuild.append(username).append(" ").append(b64str[0]) From b4f7ce6c8ffd12a35bc660f355bb101f9b46ff64 Mon Sep 17 00:00:00 2001 From: Stefan Johansson <sjohanss@openjdk.org> Date: Tue, 5 Dec 2017 13:58:55 +0100 Subject: [PATCH 127/165] 8192983: gc/g1/TestVerifyGCType.java might fail on loaded machines Reviewed-by: tschatzl, jwilhelm --- .../hotspot/jtreg/gc/g1/TestVerifyGCType.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java index 35cf30374f9..033aed25492 100644 --- a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java +++ b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java @@ -142,8 +142,9 @@ public class TestVerifyGCType { "-XX:+UnlockDiagnosticVMOptions", "-XX:+UseG1GC", "-XX:+WhiteBoxAPI", - "-XX:+ExplicitGCInvokesConcurrent", "-Xlog:gc,gc+start,gc+verify=info", + "-Xms16m", + "-Xmx16m", "-XX:+VerifyBeforeGC", "-XX:+VerifyAfterGC", "-XX:+VerifyDuringGC"}); @@ -173,7 +174,7 @@ public class TestVerifyGCType { private static void verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data) { CollectionInfo ci = CollectionInfo.parseFirst(name, data); - Asserts.assertTrue(ci != null, "Expected GC not found: " + name); + Asserts.assertTrue(ci != null, "Expected GC not found: " + name + "\n" + data); // Verify Before verifyType(ci, expectBefore, VERIFY_BEFORE); @@ -243,14 +244,41 @@ public class TestVerifyGCType { public static class TriggerGCs { public static void main(String args[]) throws Exception { WhiteBox wb = WhiteBox.getWhiteBox(); - // Trigger the different GCs using the WhiteBox API and System.gc() - // to start a concurrent cycle with -XX:+ExplicitGCInvokesConcurrent. + // Allocate some memory that can be turned into garbage. + Object[] used = alloc1M(); + + // Trigger the different GCs using the WhiteBox API. wb.fullGC(); // full - System.gc(); // initial-mark, remark and cleanup + + // Memory have been promoted to old by full GC. Free + // some memory to be reclaimed by concurrent cycle. + partialFree(used); + wb.g1StartConcMarkCycle(); // initial-mark, remark and cleanup + // Sleep to make sure concurrent cycle is done - Thread.sleep(1000); + while (wb.g1InConcurrentMark()) { + Thread.sleep(1000); + } + + // Trigger two young GCs, first will be young-only, second will be mixed. wb.youngGC(); // young-only wb.youngGC(); // mixed } + + private static Object[] alloc1M() { + Object[] ret = new Object[1024]; + // Alloc 1024 1k byte arrays (~1M) + for (int i = 0; i < ret.length; i++) { + ret[i] = new byte[1024]; + } + return ret; + } + + private static void partialFree(Object[] array) { + // Free every other element + for (int i = 0; i < array.length; i+=2) { + array[i] = null; + } + } } } From 29aabe9c05895067cdebe9eacbc985e22870004e Mon Sep 17 00:00:00 2001 From: Dean Long <dlong@openjdk.org> Date: Wed, 6 Dec 2017 18:37:57 -0800 Subject: [PATCH 128/165] 8193009: compiler/c2/Test7029152.java crashes with SIGILL in java.lang.StringLatin1.indexOf with -XX:+UseJVMCICompiler Reviewed-by: iveresov, kvn --- src/hotspot/cpu/x86/nativeInst_x86.hpp | 11 ++--- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 42 ++++++++++++++++++-- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp index 436a48caf34..de4b448396b 100644 --- a/src/hotspot/cpu/x86/nativeInst_x86.hpp +++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp @@ -706,14 +706,11 @@ inline bool NativeInstruction::is_cond_jump() { return (int_at(0) & 0xF0FF) = inline bool NativeInstruction::is_safepoint_poll() { #ifdef AMD64 if (SafepointMechanism::uses_thread_local_poll()) { - // We know that the poll must have a REX_B prefix since we enforce its source to be - // a rex-register and the destination to be rax. const bool has_rex_prefix = ubyte_at(0) == NativeTstRegMem::instruction_rex_b_prefix; - const bool is_test_opcode = ubyte_at(1) == NativeTstRegMem::instruction_code_memXregl; - const bool is_rax_target = (ubyte_at(2) & NativeTstRegMem::modrm_mask) == NativeTstRegMem::modrm_reg; - if (has_rex_prefix && is_test_opcode && is_rax_target) { - return true; - } + const int test_offset = has_rex_prefix ? 1 : 0; + const bool is_test_opcode = ubyte_at(test_offset) == NativeTstRegMem::instruction_code_memXregl; + const bool is_rax_target = (ubyte_at(test_offset + 1) & NativeTstRegMem::modrm_mask) == NativeTstRegMem::modrm_reg; + return is_test_opcode && is_rax_target; } // Try decoding a near safepoint first: if (ubyte_at(0) == NativeTstRegMem::instruction_code_memXregl && diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 3f0d57bcce0..b02015a08c2 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -3388,23 +3388,57 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // No exception case __ bind(noException); - Label no_adjust, bail, no_prefix; + Label no_adjust, bail, no_prefix, not_special; if (SafepointMechanism::uses_thread_local_poll() && !cause_return) { // If our stashed return pc was modified by the runtime we avoid touching it __ cmpptr(rbx, Address(rbp, wordSize)); __ jccb(Assembler::notEqual, no_adjust); - // Verify the correct encoding of the poll we're about to skip. + // Skip over the poll instruction. // See NativeInstruction::is_safepoint_poll() + // Possible encodings: + // 85 00 test %eax,(%rax) + // 85 01 test %eax,(%rcx) + // 85 02 test %eax,(%rdx) + // 85 03 test %eax,(%rbx) + // 85 06 test %eax,(%rsi) + // 85 07 test %eax,(%rdi) + // + // 41 85 00 test %eax,(%r8) + // 41 85 01 test %eax,(%r9) + // 41 85 02 test %eax,(%r10) + // 41 85 03 test %eax,(%r11) + // 41 85 06 test %eax,(%r14) + // 41 85 07 test %eax,(%r15) + // + // 85 04 24 test %eax,(%rsp) + // 41 85 04 24 test %eax,(%r12) + // 85 45 00 test %eax,0x0(%rbp) + // 41 85 45 00 test %eax,0x0(%r13) + __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_rex_b_prefix); __ jcc(Assembler::notEqual, no_prefix); __ addptr(rbx, 1); __ bind(no_prefix); #ifdef ASSERT - __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_code_memXregl); + __ movptr(rax, rbx); // remember where 0x85 should be, for verification below +#endif + // r12/r13/rsp/rbp base encoding takes 3 bytes with the following register values: + // r12/rsp 0x04 + // r13/rbp 0x05 + __ movzbq(rcx, Address(rbx, 1)); + __ andptr(rcx, 0x07); // looking for 0x04 .. 0x05 + __ subptr(rcx, 4); // looking for 0x00 .. 0x01 + __ cmpptr(rcx, 1); + __ jcc(Assembler::above, not_special); + __ addptr(rbx, 1); + __ bind(not_special); +#ifdef ASSERT + // Verify the correct encoding of the poll we're about to skip. + __ cmpb(Address(rax, 0), NativeTstRegMem::instruction_code_memXregl); __ jcc(Assembler::notEqual, bail); // Mask out the modrm bits - __ testb(Address(rbx, 1), NativeTstRegMem::modrm_mask); + __ testb(Address(rax, 1), NativeTstRegMem::modrm_mask); // rax encodes to 0, so if the bits are nonzero it's incorrect __ jcc(Assembler::notZero, bail); #endif From 1cfa67d1d5248022e7b49057e3dce0bcecf230fc Mon Sep 17 00:00:00 2001 From: Jiangli Zhou <jiangli@openjdk.org> Date: Wed, 6 Dec 2017 20:06:43 -0500 Subject: [PATCH 129/165] 8193065: [TESTBUG] [TESTBUG]GCSharedStringsDuringDump.java: Exception in thread "main" java.lang.RuntimeException: String is not shared Check if 'oa' region is missing. Reviewed-by: hseigel, ccheung --- .../jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java index 38e23c31a30..8faffaaf14e 100644 --- a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java +++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java @@ -101,6 +101,7 @@ public class GCSharedStringsDuringDump { if (output.getStdout().contains("Too many string space regions") || output.getStderr().contains("Unable to write archive heap memory regions") || output.getStdout().contains("Try increasing NewSize") || + !output.getStdout().contains("oa0 space:") || output.getExitValue() != 0) { // Try again with larger heap and NewSize, this should increase the // G1 heap region size to 2M From e2d38d5f6a12c2163f7e7eca4e9c97e2167390e1 Mon Sep 17 00:00:00 2001 From: Stefan Johansson <sjohanss@openjdk.org> Date: Tue, 5 Dec 2017 16:34:03 +0100 Subject: [PATCH 130/165] 8193068: Add gc/g1/TestVerifyGCType.java to problem list Reviewed-by: tschatzl --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index d1c9d42e0a4..a07b9c9e792 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -65,6 +65,7 @@ gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java 8156755 gener gc/survivorAlignment/TestPromotionToSurvivor.java 8129886 generic-all gc/g1/logging/TestG1LoggingFailure.java 8169634 generic-all gc/g1/humongousObjects/TestHeapCounters.java 8178918 generic-all +gc/g1/TestVerifyGCType.java 8193067 generic-all gc/stress/gclocker/TestGCLockerWithG1.java 8179226 generic-all gc/survivorAlignment/TestPromotionFromSurvivorToTenuredAfterMinorGC.java 8177765 generic-all From 8c50b3e660b97bd395f8de86cc2cfa88ed1266f0 Mon Sep 17 00:00:00 2001 From: Vicente Romero <vromero@openjdk.org> Date: Wed, 6 Dec 2017 13:36:34 -0500 Subject: [PATCH 131/165] 8192885: Compiler in JDK 10-ea+33 misses to include entry in LineNumberTable for goto instruction of foreach loop Reviewed-by: mcimadamore --- .../com/sun/tools/javac/comp/Lower.java | 27 ++-- .../AddGotoAfterForLoopToLNTTest.java | 116 ++++++++++++++++++ .../javac/flow/tests/TestCaseForEach.java | 2 +- 3 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index f95f6640506..fe9f0945797 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -3236,12 +3236,10 @@ public class Lower extends TreeTranslator { make.Ident(index)).setType(elemtype); JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods, tree.var.name, - tree.var.vartype, null).setType(tree.var.type); + tree.var.vartype, loopvarinit).setType(tree.var.type); loopvardef.sym = tree.var.sym; - JCStatement loopVarAssign = make.Assignment(tree.var.sym, loopvarinit); - JCBlock body = make. - Block(0, List.of(loopVarAssign, tree.body)); + JCBlock body = make.Block(0, List.of(loopvardef, tree.body)); arraycachedef = translate(arraycachedef); result = translate(make. @@ -3251,12 +3249,7 @@ public class Lower extends TreeTranslator { body)); patchTargets(body, tree, result); JCStatement nullAssignToArr = make.Assignment(arraycache, make.Literal(BOT, null).setType(syms.botType)); - JCStatement nullAssignToLoopVar = tree.var.type.isPrimitive() ? - null : - make.Assignment(tree.var.sym, make.Literal(BOT, null).setType(syms.botType)); - result = nullAssignToLoopVar == null ? - make.Block(0, List.of(arraycachedef, loopvardef, (JCStatement)result, nullAssignToArr)): - make.Block(0, List.of(arraycachedef, loopvardef, (JCStatement)result, nullAssignToArr, nullAssignToLoopVar)); + result = make.Block(0, List.of(arraycachedef, (JCStatement)result, nullAssignToArr)); } /** Patch up break and continue targets. */ private void patchTargets(JCTree body, final JCTree src, final JCTree dest) { @@ -3310,7 +3303,7 @@ public class Lower extends TreeTranslator { types.erasure(types.asSuper(iterator.type.getReturnType(), syms.iteratorType.tsym)), currentMethodSym); - JCStatement init = make. + JCStatement init = make. VarDef(itvar, make.App(make.Select(tree.expr, iterator) .setType(types.erasure(iterator.type)))); @@ -3331,10 +3324,9 @@ public class Lower extends TreeTranslator { JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods, tree.var.name, tree.var.vartype, - null).setType(tree.var.type); + vardefinit).setType(tree.var.type); indexDef.sym = tree.var.sym; - JCStatement loopVarAssign = make.Assignment(tree.var.sym, vardefinit); - JCBlock body = make.Block(0, List.of(loopVarAssign, tree.body)); + JCBlock body = make.Block(0, List.of(indexDef, tree.body)); body.endpos = TreeInfo.endPos(tree.body); init = translate(init); result = translate(make. @@ -3344,12 +3336,7 @@ public class Lower extends TreeTranslator { body)); patchTargets(body, tree, result); JCStatement nullAssignToIterator = make.Assignment(itvar, make.Literal(BOT, null).setType(syms.botType)); - JCStatement nullAssignToLoopVar = tree.var.type.isPrimitive() ? - null : - make.Assignment(tree.var.sym, make.Literal(BOT, null).setType(syms.botType)); - result = nullAssignToLoopVar == null ? - make.Block(0, List.of(init, indexDef, (JCStatement)result, nullAssignToIterator)): - make.Block(0, List.of(init, indexDef, (JCStatement)result, nullAssignToIterator, nullAssignToLoopVar)); + result = make.Block(0, List.of(init, (JCStatement)result, nullAssignToIterator)); } public void visitVarDef(JCVariableDecl tree) { diff --git a/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java b/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java new file mode 100644 index 00000000000..5d5df2c4ff4 --- /dev/null +++ b/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 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 8192885 + * @summary Compiler in JDK 10-ea+33 misses to include entry in LineNumberTable for goto instruction of foreach loop + * @library /tools/lib + * @modules jdk.jdeps/com.sun.tools.classfile + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * jdk.jdeps/com.sun.tools.javap + * @build toolbox.ToolBox toolbox.JavacTask + * @run main AddGotoAfterForLoopToLNTTest + */ + +import java.io.File; +import java.nio.file.Paths; + +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.LineNumberTable_attribute; +import com.sun.tools.classfile.Method; +import com.sun.tools.javac.util.Assert; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class AddGotoAfterForLoopToLNTTest { + + static final String testSource = + /* 01 */ "class GotoAtEnhancedForTest {\n" + + /* 02 */ " void lookForThisMethod() {\n" + + /* 03 */ " for (Object o : java.util.Collections.emptyList()) {\n" + + /* 04 */ " }\n" + + /* 05 */ " }\n" + + /* 06 */ "}"; + + static final int[][] expectedLNT = { + // {line-number, start-pc}, + {3, 0}, + {4, 25}, + {3, 28}, + {5, 30}, + }; + + static final String methodToLookFor = "lookForThisMethod"; + + public static void main(String[] args) throws Exception { + new AddGotoAfterForLoopToLNTTest().run(); + } + + ToolBox tb = new ToolBox(); + + void run() throws Exception { + compileTestClass(); + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), + "GotoAtEnhancedForTest.class").toUri()), methodToLookFor); + } + + void compileTestClass() throws Exception { + new JavacTask(tb) + .sources(testSource) + .run(); + } + + void checkClassFile(final File cfile, String methodToFind) throws Exception { + ClassFile classFile = ClassFile.read(cfile); + boolean methodFound = false; + for (Method method : classFile.methods) { + if (method.getName(classFile.constant_pool).equals(methodToFind)) { + methodFound = true; + Code_attribute code = (Code_attribute) method.attributes.get("Code"); + LineNumberTable_attribute lnt = + (LineNumberTable_attribute) code.attributes.get("LineNumberTable"); + Assert.check(lnt.line_number_table_length == expectedLNT.length, + "The LineNumberTable found has a length different to the expected one"); + int i = 0; + for (LineNumberTable_attribute.Entry entry: lnt.line_number_table) { + Assert.check(entry.line_number == expectedLNT[i][0] && + entry.start_pc == expectedLNT[i][1], + "LNT entry at pos " + i + " differ from expected." + + "Found " + entry.line_number + ":" + entry.start_pc + + ". Expected " + expectedLNT[i][0] + ":" + expectedLNT[i][1]); + i++; + } + } + } + Assert.check(methodFound, "The seek method was not found"); + } + + void error(String msg) { + throw new AssertionError(msg); + } +} diff --git a/test/langtools/tools/javac/flow/tests/TestCaseForEach.java b/test/langtools/tools/javac/flow/tests/TestCaseForEach.java index e97070bc0fb..978ae3fbb7e 100644 --- a/test/langtools/tools/javac/flow/tests/TestCaseForEach.java +++ b/test/langtools/tools/javac/flow/tests/TestCaseForEach.java @@ -3,7 +3,7 @@ public class TestCaseForEach { @AliveRange(varName="o", bytecodeStart=25, bytecodeLength=11) - @AliveRange(varName="o", bytecodeStart=44, bytecodeLength=1) + @AliveRange(varName="o", bytecodeStart=41, bytecodeLength=1) void m(String[] args) { Object o; for (String s : args) { From 800f9ab58aee17d0e9a5741c0772a53f5f7fa1ed Mon Sep 17 00:00:00 2001 From: Alan Bateman <alanb@openjdk.org> Date: Thu, 7 Dec 2017 16:45:19 +0000 Subject: [PATCH 132/165] 8191867: Module attribute in 54.0+ class file cannot contains a requires java.base with ACC_TRANSITIVE or ACC_STATIC_PHASE Reviewed-by: psandoz, mchung --- .../jdk/internal/module/ModuleInfo.java | 28 ++++- .../jdk/internal/module/ModuleInfoWriter.java | 2 +- .../lang/module/ClassFileVersionsTest.java | 104 ++++++++++++++++++ 3 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 test/jdk/java/lang/module/ClassFileVersionsTest.java diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java index f6f1bb07e16..942af76b69d 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java @@ -63,6 +63,9 @@ import static jdk.internal.module.ClassFileConstants.*; public final class ModuleInfo { + private final int JAVA_MIN_SUPPORTED_VERSION = 53; + private final int JAVA_MAX_SUPPORTED_VERSION = 54; + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); @@ -188,8 +191,10 @@ public final class ModuleInfo { int minor_version = in.readUnsignedShort(); int major_version = in.readUnsignedShort(); - if (major_version < 53) { - throw invalidModuleDescriptor("Must be >= 53.0"); + if (major_version < JAVA_MIN_SUPPORTED_VERSION || + major_version > JAVA_MAX_SUPPORTED_VERSION) { + throw invalidModuleDescriptor("Unsupported major.minor version " + + major_version + "." + minor_version); } ConstantPool cpool = new ConstantPool(in); @@ -245,7 +250,7 @@ public final class ModuleInfo { switch (attribute_name) { case MODULE : - builder = readModuleAttribute(in, cpool); + builder = readModuleAttribute(in, cpool, major_version); break; case MODULE_PACKAGES : @@ -334,7 +339,7 @@ public final class ModuleInfo { * Reads the Module attribute, returning the ModuleDescriptor.Builder to * build the corresponding ModuleDescriptor. */ - private Builder readModuleAttribute(DataInput in, ConstantPool cpool) + private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major) throws IOException { // module_name @@ -390,8 +395,21 @@ public final class ModuleInfo { JLMA.requires(builder, mods, dn, vs); } - if (dn.equals("java.base")) + if (dn.equals("java.base")) { + if (major >= 54 + && (mods.contains(Requires.Modifier.TRANSITIVE) + || mods.contains(Requires.Modifier.STATIC))) { + String flagName; + if (mods.contains(Requires.Modifier.TRANSITIVE)) { + flagName = "ACC_TRANSITIVE"; + } else { + flagName = "ACC_STATIC_PHASE"; + } + throw invalidModuleDescriptor("The requires entry for java.base" + + " has " + flagName + " set"); + } requiresJavaBase = true; + } } if (mn.equals("java.base")) { if (requires_count > 0) { diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java index 2649e3e5b42..d1da3356ac7 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java @@ -80,7 +80,7 @@ public final class ModuleInfoWriter { */ private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) { ClassWriter cw = new ClassWriter(0); - cw.visit(Opcodes.V9, ACC_MODULE, "module-info", null, null, null); + cw.visit(Opcodes.V10, ACC_MODULE, "module-info", null, null, null); int moduleFlags = md.modifiers().stream() .map(MODULE_MODS_TO_FLAGS::get) diff --git a/test/jdk/java/lang/module/ClassFileVersionsTest.java b/test/jdk/java/lang/module/ClassFileVersionsTest.java new file mode 100644 index 00000000000..c7808adb223 --- /dev/null +++ b/test/jdk/java/lang/module/ClassFileVersionsTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 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 + * @modules java.base/jdk.internal.module + * @run testng ClassFileVersionsTest + * @summary Test parsing of module-info.class with different class file versions + */ + +import java.lang.module.InvalidModuleDescriptorException; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Requires.Modifier; +import java.nio.ByteBuffer; +import java.util.Set; + +import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; + +import jdk.internal.module.ModuleInfoWriter; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class ClassFileVersionsTest { + + // major, minor, modifiers for requires java.base + @DataProvider(name = "supported") + public Object[][] supported() { + return new Object[][]{ + { 53, 0, Set.of() }, // JDK 9 + { 53, 0, Set.of(STATIC) }, + { 53, 0, Set.of(TRANSITIVE) }, + { 53, 0, Set.of(STATIC, TRANSITIVE) }, + + { 54, 0, Set.of() }, // JDK 10 + }; + } + + // major, minor, modifiers for requires java.base + @DataProvider(name = "unsupported") + public Object[][] unsupported() { + return new Object[][]{ + { 50, 0, Set.of()}, // JDK 6 + { 51, 0, Set.of()}, // JDK 7 + { 52, 0, Set.of()}, // JDK 8 + + { 54, 0, Set.of(STATIC) }, // JDK 10 + { 54, 0, Set.of(TRANSITIVE) }, + { 54, 0, Set.of(STATIC, TRANSITIVE) }, + + { 55, 0, Set.of()}, // JDK 11 + }; + } + + @Test(dataProvider = "supported") + public void testSupported(int major, int minor, Set<Modifier> ms) { + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") + .requires(ms, "java.base") + .build(); + ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor); + classFileVersion(bb, major, minor); + descriptor = ModuleDescriptor.read(bb); + assertEquals(descriptor.name(), "foo"); + } + + @Test(dataProvider = "unsupported", + expectedExceptions = InvalidModuleDescriptorException.class) + public void testUnsupported(int major, int minor, Set<Modifier> ms) { + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") + .requires(ms, "java.base") + .build(); + ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor); + classFileVersion(bb, major, minor); + + // throws InvalidModuleDescriptorException + ModuleDescriptor.read(bb); + } + + private void classFileVersion(ByteBuffer bb, int major, int minor) { + bb.putShort(4, (short) minor); + bb.putShort(6, (short) major); + } +} From dd07ad17036c71a5e11bf34cd80d0918431e0c3c Mon Sep 17 00:00:00 2001 From: Brian Burkhalter <bpb@openjdk.org> Date: Thu, 7 Dec 2017 08:56:04 -0800 Subject: [PATCH 133/165] 8191872: (fs) UnixNativeDispatcher conditionally compiles in support for high precision timestamps Remove POSIX conditional compilation and correct stat64 times for macOS Reviewed-by: alanb, simonis --- .../native/libnio/fs/UnixNativeDispatcher.c | 8 ++++++-- .../nio/file/Files/SetLastModifiedTime.java | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 0e7e2563994..53bb1c1009f 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2017, 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 @@ -476,10 +476,14 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime); #endif -#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__) +#ifndef MACOSX (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec); (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec); (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctim.tv_nsec); +#else + (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atimespec.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtimespec.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctimespec.tv_nsec); #endif } diff --git a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java index ef8cfaa0cec..4377242f0a0 100644 --- a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java +++ b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017 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 @@ -21,6 +21,7 @@ * questions. */ +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,7 +37,7 @@ import static org.testng.Assert.assertFalse; /** * @test - * @bug 4313887 8062949 + * @bug 4313887 8062949 8191872 * @library .. * @run testng SetLastModifiedTime * @summary Unit test for Files.setLastModifiedTime @@ -114,5 +115,20 @@ public class SetLastModifiedTime { assertTrue(false); } catch (NullPointerException expected) { } } + + @Test + public void testCompare() throws Exception { + Path path = Files.createFile(testDir.resolve("path")); + long timeMillis = 1512520600195L; + FileTime fileTime = FileTime.fromMillis(timeMillis); + Files.setLastModifiedTime(path, fileTime); + File file = path.toFile(); + long ioTime = file.lastModified(); + long nioTime = Files.getLastModifiedTime(path).toMillis(); + assertTrue(ioTime == timeMillis || ioTime == 1000*(timeMillis/1000), + "File.lastModified() not in {time, 1000*(time/1000)}"); + assertEquals(nioTime, ioTime, + "File.lastModified() != Files.getLastModifiedTime().toMillis()"); + } } From 4bff43558f17341b16b32417d53d69b0ed9341c4 Mon Sep 17 00:00:00 2001 From: Mandy Chung <mchung@openjdk.org> Date: Thu, 7 Dec 2017 09:22:35 -0800 Subject: [PATCH 134/165] 8193159: Reduce the number of classes loaded due to NativeLibrary Reviewed-by: alanb, redestad, martin --- src/java.base/share/classes/java/lang/ClassLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index c36b54d31f0..c18a9ad9774 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -38,6 +38,7 @@ import java.security.CodeSource; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collections; import java.util.Deque; @@ -45,7 +46,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; -import java.util.LinkedList; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; @@ -2496,7 +2496,7 @@ public abstract class ClassLoader { } // native libraries being loaded - static Deque<NativeLibrary> nativeLibraryContext = new LinkedList<>(); + static Deque<NativeLibrary> nativeLibraryContext = new ArrayDeque<>(8); /* * The run() method will be invoked when this class loader becomes From f296f0cfbcecb74b3260e4c8f29657abd6970987 Mon Sep 17 00:00:00 2001 From: Mandy Chung <mchung@openjdk.org> Date: Thu, 7 Dec 2017 09:23:15 -0800 Subject: [PATCH 135/165] 8192945: Need stable sort for MODULES entry in the release file Reviewed-by: alanb, redestad --- .../tools/jlink/internal/ModuleSorter.java | 61 ++++++++++-------- .../jdk/tools/jlink/ModuleNamesOrderTest.java | 62 +++++++++++++++---- 2 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java index 42cbe9f0255..d01364a914b 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -30,13 +30,16 @@ import jdk.tools.jlink.plugin.ResourcePoolModule; import jdk.tools.jlink.plugin.ResourcePoolModuleView; import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Requires.Modifier; import java.nio.ByteBuffer; -import java.util.Deque; +import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; @@ -45,9 +48,8 @@ import java.util.stream.Stream; * Helper class to sort modules in topological order */ public final class ModuleSorter { - private final Deque<ResourcePoolModule> nodes = new LinkedList<>(); - private final Map<String, Set<ResourcePoolModule>> edges = new HashMap<>(); - private final Deque<ResourcePoolModule> result = new LinkedList<>(); + private final Map<ResourcePoolModule, Set<ResourcePoolModule>> graph = new HashMap<>(); + private final List<ResourcePoolModule> result = new ArrayList<>(); private final ResourcePoolModuleView moduleView; @@ -69,11 +71,17 @@ public final class ModuleSorter { private ModuleSorter addModule(ResourcePoolModule module) { addNode(module); - readModuleDescriptor(module).requires().forEach(req -> { + // the module graph will be traversed in a stable order for + // the topological sort. So add the dependences in the module name order + readModuleDescriptor(module).requires() + .stream() + .sorted(Comparator.comparing(Requires::name)) + .forEach(req -> + { ResourcePoolModule dep = moduleView.findModule(req.name()).orElse(null); if (dep != null) { addNode(dep); - edges.get(module.name()).add(dep); + graph.get(module).add(dep); } else if (!req.modifiers().contains(Modifier.STATIC)) { throw new PluginException(req.name() + " not found"); } @@ -82,22 +90,23 @@ public final class ModuleSorter { } private void addNode(ResourcePoolModule module) { - nodes.add(module); - edges.computeIfAbsent(module.name(), _n -> new HashSet<>()); + graph.computeIfAbsent(module, _n -> new LinkedHashSet<>()); } + /* + * The module graph will be traversed in a stable order + * (traversing the modules and their dependences in alphabetical order) + * so that it will produce the same result of a given module graph. + */ private synchronized void build() { - if (!result.isEmpty() || nodes.isEmpty()) + if (!result.isEmpty() || graph.isEmpty()) return; - Deque<ResourcePoolModule> visited = new LinkedList<>(); - Deque<ResourcePoolModule> done = new LinkedList<>(); - ResourcePoolModule node; - while ((node = nodes.poll()) != null) { - if (!visited.contains(node)) { - visit(node, visited, done); - } - } + Set<ResourcePoolModule> visited = new HashSet<>(); + Set<ResourcePoolModule> done = new HashSet<>(); + graph.keySet().stream() + .sorted(Comparator.comparing(ResourcePoolModule::name)) + .forEach(node -> visit(node, visited, done)); } public Stream<ResourcePoolModule> sorted() { @@ -106,19 +115,21 @@ public final class ModuleSorter { } private void visit(ResourcePoolModule node, - Deque<ResourcePoolModule> visited, - Deque<ResourcePoolModule> done) { + Set<ResourcePoolModule> visited, + Set<ResourcePoolModule> done) { if (visited.contains(node)) { if (!done.contains(node)) { throw new IllegalArgumentException("Cyclic detected: " + - node + " " + edges.get(node.name())); + node + " " + graph.get(node)); } return; } + + // traverse the dependences of the given module which are + // also sorted in alphabetical order visited.add(node); - edges.get(node.name()) - .forEach(x -> visit(x, visited, done)); + graph.get(node).forEach(x -> visit(x, visited, done)); done.add(node); - result.addLast(node); + result.add(node); } } diff --git a/test/jdk/tools/jlink/ModuleNamesOrderTest.java b/test/jdk/tools/jlink/ModuleNamesOrderTest.java index 20b642d1ef3..e7c8af4ecdc 100644 --- a/test/jdk/tools/jlink/ModuleNamesOrderTest.java +++ b/test/jdk/tools/jlink/ModuleNamesOrderTest.java @@ -24,14 +24,17 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; import java.util.Properties; import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; import tests.Helper; import tests.JImageGenerator; +import tests.JImageGenerator.JLinkTask; /* * @test @@ -44,6 +47,9 @@ import tests.JImageGenerator; * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler + * jdk.scripting.nashorn + * jdk.scripting.nashorn.shell + * * @build tests.* * @run main ModuleNamesOrderTest */ @@ -60,12 +66,18 @@ public class ModuleNamesOrderTest { return; } - Path outputDir = helper.createNewImageDir("image8168925"); - JImageGenerator.getJLinkTask() - .modulePath(helper.defaultModulePath()) - .output(outputDir) - .addMods("jdk.scripting.nashorn") - .call().assertSuccess(); + testDependences(helper); + testModulesOrder(helper); + } + + private static List<String> modulesProperty(Path outputDir, String modulePath, String... roots) + throws IOException + { + JLinkTask jlinkTask = JImageGenerator.getJLinkTask() + .modulePath(modulePath) + .output(outputDir); + Stream.of(roots).forEach(jlinkTask::addMods); + jlinkTask.call().assertSuccess(); File release = new File(outputDir.toString(), "release"); if (!release.exists()) { @@ -81,9 +93,21 @@ public class ModuleNamesOrderTest { if (!modules.startsWith("\"java.base ")) { throw new AssertionError("MODULES should start with 'java.base'"); } + if (modules.charAt(0) != '"' || modules.charAt(modules.length()-1) != '"') { + throw new AssertionError("MODULES value should be double quoted"); + } - if (!modules.endsWith(" jdk.scripting.nashorn\"")) { - throw new AssertionError("MODULES end with 'jdk.scripting.nashorn'"); + return Stream.of(modules.substring(1, modules.length()-1).split("\\s+")) + .collect(Collectors.toList()); + } + + private static void testDependences(Helper helper) throws IOException { + Path outputDir = helper.createNewImageDir("test"); + List<String> modules = modulesProperty(outputDir, helper.defaultModulePath(), + "jdk.scripting.nashorn"); + String last = modules.get(modules.size()-1); + if (!last.equals("jdk.scripting.nashorn")) { + throw new AssertionError("Unexpected MODULES value: " + modules); } checkDependency(modules, "java.logging", "java.base"); @@ -94,7 +118,23 @@ public class ModuleNamesOrderTest { checkDependency(modules, "jdk.scripting.nashorn", "java.scripting"); } - private static void checkDependency(String modules, String fromMod, String toMod) { + /* + * Verify the MODULES list must be the same for the same module graph + */ + private static void testModulesOrder(Helper helper) throws IOException { + Path image1 = helper.createNewImageDir("test1"); + List<String> modules1 = modulesProperty(image1, helper.defaultModulePath(), + "jdk.scripting.nashorn", "jdk.scripting.nashorn.shell"); + Path image2 = helper.createNewImageDir("test2"); + List<String> modules2 = modulesProperty(image2, helper.defaultModulePath(), + "jdk.scripting.nashorn.shell", "jdk.scripting.nashorn"); + if (!modules1.equals(modules2)) { + throw new AssertionError("MODULES should be a stable order: " + + modules1 + " vs " + modules2); + } + } + + private static void checkDependency(List<String> modules, String fromMod, String toMod) { int fromModIdx = modules.indexOf(fromMod); if (fromModIdx == -1) { throw new AssertionError(fromMod + " is missing in MODULES"); From 77b4045e0f7a91936f229fd4fd72c16fb458d6cb Mon Sep 17 00:00:00 2001 From: Ivan Gerasimov <igerasim@openjdk.org> Date: Thu, 7 Dec 2017 10:18:22 -0800 Subject: [PATCH 136/165] 8193156: Need to backout fixes for JDK-8058547, JDK-8055753, JDK-8085903 Reviewed-by: mullan --- .../java/security/ProtectionDomain.java | 153 +++--------------- 1 file changed, 24 insertions(+), 129 deletions(-) diff --git a/src/java.base/share/classes/java/security/ProtectionDomain.java b/src/java.base/share/classes/java/security/ProtectionDomain.java index 365096f619a..7830ee4d8f9 100644 --- a/src/java.base/share/classes/java/security/ProtectionDomain.java +++ b/src/java.base/share/classes/java/security/ProtectionDomain.java @@ -25,15 +25,13 @@ package java.security; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; +import java.util.WeakHashMap; import jdk.internal.misc.JavaSecurityAccess; import jdk.internal.misc.JavaSecurityProtectionDomainAccess; import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; @@ -115,23 +113,10 @@ public class ProtectionDomain { } static { - // setup SharedSecrets to allow access to doIntersectionPrivilege - // methods and ProtectionDomain cache + // Set up JavaSecurityAccess in SharedSecrets SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl()); - SharedSecrets.setJavaSecurityProtectionDomainAccess( - new JavaSecurityProtectionDomainAccess() { - @Override - public ProtectionDomainCache getProtectionDomainCache() { - return new PDCache(); - } - }); } - /** - * Used for storing ProtectionDomains as keys in a Map. - */ - static final class Key {} - /* CodeSource */ private CodeSource codesource ; @@ -571,117 +556,27 @@ public class ProtectionDomain { } /** - * A cache of ProtectionDomains and their Permissions. - * - * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap - * with additional support for checking and removing weak keys that are no - * longer in use. There can be cases where the permission collection may - * have a chain of strong references back to the ProtectionDomain, which - * ordinarily would prevent the entry from being removed from the map. To - * address that, we wrap the permission collection in a SoftReference so - * that it can be reclaimed by the garbage collector due to memory demand. + * Used for storing ProtectionDomains as keys in a Map. */ - private static class PDCache implements ProtectionDomainCache { - private final ConcurrentHashMap<WeakProtectionDomainKey, - SoftReference<PermissionCollection>> - pdMap = new ConcurrentHashMap<>(); - private final ReferenceQueue<Key> queue = new ReferenceQueue<>(); + final class Key {} - @Override - public void put(ProtectionDomain pd, PermissionCollection pc) { - processQueue(queue, pdMap); - WeakProtectionDomainKey weakPd = - new WeakProtectionDomainKey(pd, queue); - pdMap.put(weakPd, new SoftReference<>(pc)); - } - - @Override - public PermissionCollection get(ProtectionDomain pd) { - processQueue(queue, pdMap); - WeakProtectionDomainKey weakPd = new WeakProtectionDomainKey(pd); - SoftReference<PermissionCollection> sr = pdMap.get(weakPd); - return (sr == null) ? null : sr.get(); - } - - /** - * Removes weak keys from the map that have been enqueued - * on the reference queue and are no longer in use. - */ - private static void processQueue(ReferenceQueue<Key> queue, - ConcurrentHashMap<? extends - WeakReference<Key>, ?> pdMap) { - Reference<? extends Key> ref; - while ((ref = queue.poll()) != null) { - pdMap.remove(ref); - } - } - } - - /** - * A weak key for a ProtectionDomain. - */ - private static class WeakProtectionDomainKey extends WeakReference<Key> { - /** - * Saved value of the referent's identity hash code, to maintain - * a consistent hash code after the referent has been cleared - */ - private final int hash; - - /** - * A key representing a null ProtectionDomain. - */ - private static final Key NULL_KEY = new Key(); - - /** - * Create a new WeakProtectionDomain with the specified domain and - * registered with a queue. - */ - WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue<Key> rq) { - this((pd == null ? NULL_KEY : pd.key), rq); - } - - WeakProtectionDomainKey(ProtectionDomain pd) { - this(pd == null ? NULL_KEY : pd.key); - } - - private WeakProtectionDomainKey(Key key, ReferenceQueue<Key> rq) { - super(key, rq); - hash = key.hashCode(); - } - - private WeakProtectionDomainKey(Key key) { - super(key); - hash = key.hashCode(); - } - - /** - * Returns the identity hash code of the original referent. - */ - @Override - public int hashCode() { - return hash; - } - - /** - * Returns true if the given object is an identical - * WeakProtectionDomainKey instance, or, if this object's referent - * has not been cleared and the given object is another - * WeakProtectionDomainKey instance with an identical non-null - * referent as this one. - */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof WeakProtectionDomainKey) { - Object referent = get(); - return (referent != null) && - (referent == ((WeakProtectionDomainKey)obj).get()); - } else { - return false; - } - } + static { + SharedSecrets.setJavaSecurityProtectionDomainAccess( + new JavaSecurityProtectionDomainAccess() { + public ProtectionDomainCache getProtectionDomainCache() { + return new ProtectionDomainCache() { + private final Map<Key, PermissionCollection> map = + Collections.synchronizedMap + (new WeakHashMap<Key, PermissionCollection>()); + public void put(ProtectionDomain pd, + PermissionCollection pc) { + map.put((pd == null ? null : pd.key), pc); + } + public PermissionCollection get(ProtectionDomain pd) { + return pd == null ? map.get(null) : map.get(pd.key); + } + }; + } + }); } } From 9f07305ab6f008b03b8d4f17adc4ce59e5895e06 Mon Sep 17 00:00:00 2001 From: Jan Lahoda <jlahoda@openjdk.org> Date: Thu, 7 Dec 2017 19:24:27 +0100 Subject: [PATCH 137/165] 8189248: Jshell: error with mutually dependent snippets, when one must be replaced Ensuring proper imports are generated for mutually dependent snippets if one of them is replaced. Reviewed-by: rfield --- .../share/classes/jdk/jshell/Eval.java | 2 ++ test/langtools/jdk/jshell/ClassesTest.java | 23 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java index 1e176ed4bee..30f0db98501 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java @@ -834,6 +834,8 @@ class Eval { if (!toReplace.isEmpty()) { replaced.addAll(toReplace); replaced.stream().forEach(Unit::markForReplacement); + //ensure correct classnames are set in the snippets: + replaced.stream().forEach(u -> u.setWrap(ins, legit)); } return toReplace.isEmpty() ? Result.SUCESS : Result.FAILURE; diff --git a/test/langtools/jdk/jshell/ClassesTest.java b/test/langtools/jdk/jshell/ClassesTest.java index bcdd597da62..121cafdc441 100644 --- a/test/langtools/jdk/jshell/ClassesTest.java +++ b/test/langtools/jdk/jshell/ClassesTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8145239 8129559 8080354 + * @bug 8145239 8129559 8080354 8189248 * @summary Tests for EvaluationState.classes * @build KullaTesting TestingInputStream ExpectedDiagnostic * @run testng ClassesTest @@ -41,6 +41,7 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import jdk.jshell.Diag; +import jdk.jshell.Snippet.Status; import static java.util.stream.Collectors.toList; import static jdk.jshell.Snippet.Status.VALID; import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED; @@ -327,4 +328,24 @@ public class ClassesTest extends KullaTesting { VarSnippet variableKey = varKey(assertEval("a.x;")); assertEquals(variableKey.typeName(), "A.I1"); } + + public void testCircular() { + assertEval("import java.util.function.Supplier;"); + TypeDeclSnippet aClass = + classKey(assertEval("public class A<T> {\n" + + " private class SomeClass {}\n" + + " public Supplier<T> m() {\n" + + " return new B<>(this);\n" + + " }\n" + + "}", + added(RECOVERABLE_DEFINED))); + assertEval("public class B<T> implements Supplier<T> {\n" + + " public B(A<T> a) {}\n" + + " public T get() {return null;}\n" + + "}", + added(VALID), + ste(aClass, Status.RECOVERABLE_DEFINED, Status.VALID, true, null)); + assertEval("new A()"); + } + } From 62d672208a9c5757c3711ff9dd191fb50437c667 Mon Sep 17 00:00:00 2001 From: Joe Darcy <darcy@openjdk.org> Date: Thu, 7 Dec 2017 11:00:32 -0800 Subject: [PATCH 138/165] 8193191: Update JavacTestingAbstractProcessor for JDK 10 Reviewed-by: jjg --- .../lib/JavacTestingAbstractProcessor.java | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java index 6509542c1ba..72c758d20d6 100644 --- a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java +++ b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2017, 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 @@ -31,13 +31,7 @@ import static javax.lang.model.SourceVersion.*; * An abstract annotation processor tailored to {@code javac} regression testing. */ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { - private static final Set<String> allAnnotations; - - static { - Set<String> tmp = new HashSet<>(); - tmp.add("*"); - allAnnotations = Collections.unmodifiableSet(tmp); - } + private static final Set<String> allAnnotations = Set.of("*"); protected Elements eltUtils; protected Elements elements; @@ -116,7 +110,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { * corresponding platform visitor type. */ - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static abstract class AbstractAnnotationValueVisitor<R, P> extends AbstractAnnotationValueVisitor9<R, P> { /** @@ -127,7 +121,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static abstract class AbstractElementVisitor<R, P> extends AbstractElementVisitor9<R, P> { /** * Constructor for concrete subclasses to call. @@ -137,7 +131,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static abstract class AbstractTypeVisitor<R, P> extends AbstractTypeVisitor9<R, P> { /** * Constructor for concrete subclasses to call. @@ -147,7 +141,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class ElementKindVisitor<R, P> extends ElementKindVisitor9<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the @@ -168,7 +162,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class ElementScanner<R, P> extends ElementScanner9<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the @@ -187,7 +181,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class SimpleAnnotationValueVisitor<R, P> extends SimpleAnnotationValueVisitor9<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the @@ -208,7 +202,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class SimpleElementVisitor<R, P> extends SimpleElementVisitor9<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the @@ -229,7 +223,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class SimpleTypeVisitor<R, P> extends SimpleTypeVisitor9<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the @@ -250,7 +244,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor { } } - @SupportedSourceVersion(RELEASE_9) + @SupportedSourceVersion(RELEASE_10) public static class TypeKindVisitor<R, P> extends TypeKindVisitor9<R, P> { /** * Constructor for concrete subclasses to call; uses {@code null} From 653400a4bbd72e3087bbd72ed2580c46cf06c910 Mon Sep 17 00:00:00 2001 From: Lana Steuck <lana@openjdk.org> Date: Thu, 7 Dec 2017 21:04:24 +0000 Subject: [PATCH 139/165] Added tag jdk-10+35 for changeset d8c634b016c6 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 93926ee960b..0a1b78991e9 100644 --- a/.hgtags +++ b/.hgtags @@ -459,3 +459,4 @@ e6278add9ff28fab70fe1cc4c1d65f7363dc9445 jdk-10+31 a2008587c13fa05fa2dbfcb09fe987576fbedfd1 jdk-10+32 bbd692ad4fa300ecca7939ffbe3b1d5e52a28cc6 jdk-10+33 89deac44e51517841491ba86ff44aa82a5ca96b3 jdk-10+34 +d8c634b016c628622c9abbdc6bf50509e5dedbec jdk-10+35 From 9edbda6c17c222ab4ba6e1c6708de1c329f0ff9a Mon Sep 17 00:00:00 2001 From: Erik Joelsson <erikj@openjdk.org> Date: Thu, 7 Dec 2017 22:37:45 +0100 Subject: [PATCH 140/165] 8188789: Update JDK 9.0.1 and Future OpenJDK bundle names Reviewed-by: tbell --- make/conf/jib-profiles.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 840966c742f..172aabb7fe4 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -662,6 +662,16 @@ var getJibProfilesProfiles = function (input, common, data) { } }); + // For open profiles, the non-debug jdk bundles, need an "open" prefix on the + // remote bundle names, forming the word "openjdk". See JDK-8188789. + common.main_profile_names.forEach(function (name) { + var openName = name + common.open_suffix; + profiles[openName].artifacts["jdk"].remote = replaceAll( + "\/jdk-", "/openjdk-", + replaceAll("\/\\1", "/open\\1", + profiles[openName].artifacts["jdk"].remote)); + }); + // Profiles used to run tests. Used in JPRT and Mach 5. var testOnlyProfiles = { "run-test-jprt": { From 900f2c016deaeef18d4a4cd593773cf72df6a86f Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Thu, 7 Dec 2017 14:15:14 -0800 Subject: [PATCH 141/165] 8191030: @value Tags are not resolved by javadoc 9 Reviewed-by: jjg --- .../doclets/toolkit/taglets/ValueTaglet.java | 61 ++++--------------- .../internal/doclets/toolkit/util/Utils.java | 3 - .../TestDeprecatedDocs.java | 15 +++-- .../doclet/testValueTag/TestValueTag.java | 37 +++++++---- .../javadoc/doclet/testValueTag/pkg3/RT.java | 56 +++++++++++++++++ 5 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java index 9fd8d415269..bfbdc124eec 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java @@ -25,11 +25,8 @@ package jdk.javadoc.internal.doclets.toolkit.taglets; - import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.util.Elements; import com.sun.source.doctree.DocTree; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; @@ -121,60 +118,26 @@ public class ValueTaglet extends BaseInlineTaglet { } /** - * Given the name of the field, return the corresponding VariableElement. Return null - * due to invalid use of value tag if the name is null or empty string and if - * the value tag is not used on a field. + * Returns the referenced field or a null if the value tag + * is empty or the reference is invalid. * - * @param holder the element holding the tag - * @param config the current configuration of the doclet. + * @param holder the tag holder. + * @param config the configuration of the doclet. * @param tag the value tag. * - * @return the corresponding VariableElement. If the name is null or empty string, - * return field that the value tag was used in. Return null if the name is null - * or empty string and if the value tag is not used on a field. + * @return the referenced field or null. */ private VariableElement getVariableElement(Element holder, BaseConfiguration config, DocTree tag) { - Utils utils = config.utils; - CommentHelper ch = utils.getCommentHelper(holder); + CommentHelper ch = config.utils.getCommentHelper(holder); String signature = ch.getReferencedSignature(tag); - if (signature == null) { // no reference - //Base case: no label. - if (utils.isVariableElement(holder)) { - return (VariableElement)(holder); - } else { - // If the value tag does not specify a parameter which is a valid field and - // it is not used within the comments of a valid field, return null. - return null; - } - } + Element e = signature == null + ? holder + : ch.getReferencedMember(config, tag); - String[] sigValues = signature.split("#"); - String memberName = null; - TypeElement te = null; - if (sigValues.length == 1) { - //Case 2: @value in same class. - if (utils.isExecutableElement(holder) || utils.isVariableElement(holder)) { - te = utils.getEnclosingTypeElement(holder); - } else if (utils.isTypeElement(holder)) { - te = utils.getTopMostContainingTypeElement(holder); - } - memberName = sigValues[0]; - } else { - //Case 3: @value in different class. - Elements elements = config.docEnv.getElementUtils(); - te = elements.getTypeElement(sigValues[0]); - memberName = sigValues[1]; - } - if (te == null) { - return null; - } - for (Element field : utils.getFields(te)) { - if (utils.getSimpleName(field).equals(memberName)) { - return (VariableElement)field; - } - } - return null; + return (e != null && config.utils.isVariableElement(e)) + ? (VariableElement) e + : null; } /** diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index b6097ad81e6..8b3d4593259 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -2365,9 +2365,6 @@ public class Utils { List<Element> getItems(Element e, boolean filter, ElementKind select) { List<Element> elements = new ArrayList<>(); - // maintain backward compatibility by returning a null list, see AnnotationDocType.methods(). - if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE) - return elements; return new SimpleElementVisitor9<List<Element>, Void>() { @Override diff --git a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java index c8f7c7ecb9a..0d75410a240 100644 --- a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java +++ b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java @@ -23,7 +23,8 @@ /* * @test - * @bug 4927552 8026567 8071982 8162674 8175200 8175218 8183511 8186332 8169819 8074407 + * @bug 4927552 8026567 8071982 8162674 8175200 8175218 8183511 8186332 + * 8169819 8074407 8191030 * @summary test generated docs for deprecated items * @author jamieh * @library ../lib @@ -257,24 +258,30 @@ public class TestDeprecatedDocs extends JavadocTester { + "<td class=\"colLast\"></td>\n" + "</tr>\n" + "<tr class=\"rowColor\">\n" + + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestAnnotationType.html#field\">pkg.TestAnnotationType.field</a></th>\n" + + "<td class=\"colLast\">\n" + + "<div class=\"deprecationComment\">annotation_test4 passes.</div>\n" + + "</td>\n" + + "</tr>\n" + + "<tr class=\"altColor\">\n" + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestClass.html#field\">pkg.TestClass.field</a></th>\n" + "<td class=\"colLast\">\n" + "<div class=\"deprecationComment\">class_test2 passes. This is the second sentence of deprecated description for a field.</div>\n" + "</td>\n" + "</tr>\n" - + "<tr class=\"altColor\">\n" + + "<tr class=\"rowColor\">\n" + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestError.html#field\">pkg.TestError.field</a></th>\n" + "<td class=\"colLast\">\n" + "<div class=\"deprecationComment\">error_test2 passes.</div>\n" + "</td>\n" + "</tr>\n" - + "<tr class=\"rowColor\">\n" + + "<tr class=\"altColor\">\n" + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestException.html#field\">pkg.TestException.field</a></th>\n" + "<td class=\"colLast\">\n" + "<div class=\"deprecationComment\">exception_test2 passes.</div>\n" + "</td>\n" + "</tr>\n" - + "<tr class=\"altColor\">\n" + + "<tr class=\"rowColor\">\n" + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestInterface.html#field\">pkg.TestInterface.field</a></th>\n" + "<td class=\"colLast\">\n" + "<div class=\"deprecationComment\">interface_test2 passes.</div>\n" diff --git a/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java index da9c775b0e8..78dff7b72ec 100644 --- a/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java +++ b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -23,9 +23,9 @@ /* * @test - * @bug 4764045 8004825 8026567 + * @bug 4764045 8004825 8026567 8191030 * @summary This test ensures that the value tag works in all - * use cases. The explainations for each test case are written below. + * use cases, the tests are explained below. * @author jamieh * @library ../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -103,7 +103,7 @@ public class TestValueTag extends JavadocTester { ); checkOutput("pkg1/Class1.html", false, - //Base case: using @value on a constant. + // Base case: using @value on a constant. "Result: <a href=\"../pkg1/Class1.html#TEST_12_ERROR\">\"Test 12 " + "generates an error message\"</a>"); @@ -119,23 +119,36 @@ public class TestValueTag extends JavadocTester { "pkg1", "pkg2"); checkExit(Exit.OK); checkOutput(Output.OUT, true, - //Test @value warning printed when used with non-constant. + // Test @value warning printed when used with non-constant. "warning - @value tag (which references nonConstant) " + "can only be used in constants.", "warning - @value tag (which references NULL) " + "can only be used in constants.", "warning - @value tag (which references TEST_12_ERROR) " - + "can only be used in constants." -// TODO: re-examine these. -// //Test warning printed for bad reference. -// "warning - UnknownClass#unknownConstant (referenced by " -// + "@value tag) is an unknown reference.", -// //Test warning printed for invalid use of @value. -// "warning - @value tag cannot be used here." + + "can only be used in constants.", + // Test warning printed for bad reference. + "warning - {@value UnknownClass#unknownConstant}" + + " (referenced by @value tag) is an unknown reference." ); checkForException(); } + @Test() + void test3() { + javadoc("-d", "out3", + "-sourcepath", testSrc, + "pkg2", "pkg3"); + checkExit(Exit.OK); + + checkOrder("pkg3/RT.html", + "The value is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.", + "The value1 is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.", + "The value2 is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.", + "The value3 is <a href=\"../pkg2/Class3.html#TEST_12_PASSES\">" + + "\"Test 12 passes\"</a>."); + } + + void checkForException() { checkOutput(Output.STDERR, false, "DocletAbortException"); } diff --git a/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java b/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java new file mode 100644 index 00000000000..f6917390aa1 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +package pkg3; + +public @interface RT { + + /** The CONSTANT */ + static String CONSTANT = "constant"; + /** + * The value is {@value CONSTANT}. + * @return a value + */ + int value(); + + /** + * The value1 is {@value #CONSTANT}. + * @return a value + */ + int value1(); + + /** + * The value2 is {@value pkg3.RT#CONSTANT}. + * @return a value + */ + int value2(); + + /** + * The value3 is {@value pkg2.Class3#TEST_12_PASSES}. + * @return a value + */ + int value3(); + +} From d57db3b793108dd32b58d7a0eafd7d7b0ecec2b7 Mon Sep 17 00:00:00 2001 From: Stuart Marks <smarks@openjdk.org> Date: Thu, 7 Dec 2017 14:23:52 -0800 Subject: [PATCH 142/165] 8177681: Remove methods Runtime.getLocalized{Input,Output}Stream Reviewed-by: rriggs, bpb, mr --- .../share/classes/java/lang/Runtime.java | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java index dc36b769a07..b3f5c08b659 100644 --- a/src/java.base/share/classes/java/lang/Runtime.java +++ b/src/java.base/share/classes/java/lang/Runtime.java @@ -876,62 +876,6 @@ public class Runtime { ClassLoader.loadLibrary(fromClass, libname, false); } - /** - * Creates a localized version of an input stream. This method takes - * an {@code InputStream} and returns an {@code InputStream} - * equivalent to the argument in all respects except that it is - * localized: as characters in the local character set are read from - * the stream, they are automatically converted from the local - * character set to Unicode. - * <p> - * If the argument is already a localized stream, it may be returned - * as the result. - * - * @param in InputStream to localize - * @return a localized input stream - * @see java.io.InputStream - * @see java.io.BufferedReader#BufferedReader(java.io.Reader) - * @see java.io.InputStreamReader#InputStreamReader(java.io.InputStream) - * @deprecated As of JDK 1.1, the preferred way to translate a byte - * stream in the local encoding into a character stream in Unicode is via - * the {@code InputStreamReader} and {@code BufferedReader} - * classes. - * This method is subject to removal in a future version of Java SE. - */ - @Deprecated(since="1.1", forRemoval=true) - public InputStream getLocalizedInputStream(InputStream in) { - return in; - } - - /** - * Creates a localized version of an output stream. This method - * takes an {@code OutputStream} and returns an - * {@code OutputStream} equivalent to the argument in all respects - * except that it is localized: as Unicode characters are written to - * the stream, they are automatically converted to the local - * character set. - * <p> - * If the argument is already a localized stream, it may be returned - * as the result. - * - * @deprecated As of JDK 1.1, the preferred way to translate a - * Unicode character stream into a byte stream in the local encoding is via - * the {@code OutputStreamWriter}, {@code BufferedWriter}, and - * {@code PrintWriter} classes. - * This method is subject to removal in a future version of Java SE. - * - * @param out OutputStream to localize - * @return a localized output stream - * @see java.io.OutputStream - * @see java.io.BufferedWriter#BufferedWriter(java.io.Writer) - * @see java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream) - * @see java.io.PrintWriter#PrintWriter(java.io.OutputStream) - */ - @Deprecated(since="1.1", forRemoval=true) - public OutputStream getLocalizedOutputStream(OutputStream out) { - return out; - } - /** * Returns the version of the Java Runtime Environment as a {@link Version}. * From 7604d1d515d7e5a90c917d97a311840d28e923da Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Thu, 7 Dec 2017 14:21:25 -0800 Subject: [PATCH 143/165] 8149402: "-group" option issue for classes from default package Reviewed-by: jjg --- .../internal/doclets/toolkit/util/Group.java | 2 +- .../{C.java => InUnnamedPackage.java} | 4 ++-- .../doclet/testGroupOption/TestGroupOption.java | 13 ++++++------- 3 files changed, 9 insertions(+), 10 deletions(-) rename test/langtools/jdk/javadoc/doclet/testGroupOption/{C.java => InUnnamedPackage.java} (89%) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java index 2e80a742e22..710fffb6d2b 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java @@ -290,7 +290,7 @@ public class Group { groupList.add(defaultGroupName); } for (PackageElement pkg : packages) { - String pkgName = pkg.isUnnamed() ? null : configuration.utils.getPackageName(pkg); + String pkgName = configuration.utils.getPackageName(pkg); String groupName = pkg.isUnnamed() ? null : elementNameGroupMap.get(pkgName); // if this package is not explicitly assigned to a group, // try matching it to group specified by regular expression diff --git a/test/langtools/jdk/javadoc/doclet/testGroupOption/C.java b/test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java similarity index 89% rename from test/langtools/jdk/javadoc/doclet/testGroupOption/C.java rename to test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java index 38238973f8d..df1ca5b3085 100644 --- a/test/langtools/jdk/javadoc/doclet/testGroupOption/C.java +++ b/test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, 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 @@ -21,4 +21,4 @@ * questions. */ -public class C {} +public class InUnnamedPackage {} diff --git a/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java b/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java index 54f11eb9881..246aa79be84 100644 --- a/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java +++ b/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -23,9 +23,9 @@ /* * @test - * @bug 4924383 - * @summary Test to make sure the -group option does not cause a bad warning - * to be printed. Test for the group defined using patterns. + * @bug 4924383 8149402 + * @summary Test to make sure the -group option works correctly + * with the given pattern usages. * @author jamieh * @library ../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -55,8 +55,7 @@ public class TestGroupOption extends JavadocTester { "-group"); } - // @Test - // @ignore 8149402 + @Test // Make sure the "Other packages" section is printed and the header for empty section is not. // Make sure that the headers of group that is defined using patterns are printed. void test2() { @@ -66,7 +65,7 @@ public class TestGroupOption extends JavadocTester { "-group", "Group abc*", "abc*", "-group", "Empty group", "qwerty*", "-group", "Group a*", "a*", - "pkg1", "pkg2", "pkg3", "abc1", "abc2", "abc3", "other", testSrc("C.java")); + "pkg1", "pkg2", "pkg3", "abc1", "abc2", "abc3", "other", testSrc("InUnnamedPackage.java")); checkExit(Exit.OK); checkOutput("overview-summary.html", true, "Group pkg*", "Group abc*", "Other Packages"); From 67205081ecf7b43bee128ce0633af4a1950f1843 Mon Sep 17 00:00:00 2001 From: Weijun Wang <weijun@openjdk.org> Date: Fri, 8 Dec 2017 09:40:14 +0800 Subject: [PATCH 144/165] 8192987: keytool should remember real storetype if it is not provided Reviewed-by: mullan --- .../sun/security/tools/KeyStoreUtil.java | 7 +- .../sun/security/tools/keytool/Main.java | 40 +++++------ .../sun/security/tools/keytool/RealType.java | 67 +++++++++++++++++++ 3 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 test/jdk/sun/security/tools/keytool/RealType.java diff --git a/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java b/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java index 0aca1f39bd5..bc93634c642 100644 --- a/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java +++ b/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -93,8 +93,9 @@ public class KeyStoreUtil { * MSCAPI KeyStores */ public static boolean isWindowsKeyStore(String storetype) { - return storetype.equalsIgnoreCase("Windows-MY") - || storetype.equalsIgnoreCase("Windows-ROOT"); + return storetype != null + && (storetype.equalsIgnoreCase("Windows-MY") + || storetype.equalsIgnoreCase("Windows-ROOT")); } /** diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index f30eec637fb..e5b503c3151 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java @@ -134,8 +134,6 @@ public final class Main { private Set<Pair <String, String>> providers = null; private Set<Pair <String, String>> providerClasses = null; private String storetype = null; - private boolean hasStoretypeOption = false; - private boolean hasSrcStoretypeOption = false; private String srcProviderName = null; private String providerName = null; private String pathlist = null; @@ -549,14 +547,12 @@ public final class Main { passwords.add(storePass); } else if (collator.compare(flags, "-storetype") == 0 || collator.compare(flags, "-deststoretype") == 0) { - storetype = args[++i]; - hasStoretypeOption = true; + storetype = KeyStoreUtil.niceStoreTypeName(args[++i]); } else if (collator.compare(flags, "-srcstorepass") == 0) { srcstorePass = getPass(modifier, args[++i]); passwords.add(srcstorePass); } else if (collator.compare(flags, "-srcstoretype") == 0) { - srcstoretype = args[++i]; - hasSrcStoretypeOption = true; + srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]); } else if (collator.compare(flags, "-srckeypass") == 0) { srckeyPass = getPass(modifier, args[++i]); passwords.add(srckeyPass); @@ -708,16 +704,6 @@ public final class Main { ksfname = KeyStoreUtil.getCacerts(); } - if (storetype == null) { - storetype = KeyStore.getDefaultType(); - } - storetype = KeyStoreUtil.niceStoreTypeName(storetype); - - if (srcstoretype == null) { - srcstoretype = KeyStore.getDefaultType(); - } - srcstoretype = KeyStoreUtil.niceStoreTypeName(srcstoretype); - if (P11KEYSTORE.equalsIgnoreCase(storetype) || KeyStoreUtil.isWindowsKeyStore(storetype)) { token = true; @@ -742,11 +728,6 @@ public final class Main { (".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype)); } - if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) { - throw new UnsupportedOperationException(rb.getString - (".keypasswd.commands.not.supported.if.storetype.is.PKCS12")); - } - if (token && (keyPass != null || newPass != null || destKeyPass != null)) { throw new IllegalArgumentException(MessageFormat.format(rb.getString (".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype)); @@ -923,9 +904,13 @@ public final class Main { // Create new keystore // Probe for keystore type when filename is available if (ksfile != null && ksStream != null && providerName == null && - hasStoretypeOption == false && !inplaceImport) { + storetype == null && !inplaceImport) { keyStore = KeyStore.getInstance(ksfile, storePass); + storetype = keyStore.getType(); } else { + if (storetype == null) { + storetype = KeyStore.getDefaultType(); + } if (providerName == null) { keyStore = KeyStore.getInstance(storetype); } else { @@ -964,6 +949,11 @@ public final class Main { } } + if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) { + throw new UnsupportedOperationException(rb.getString + (".keypasswd.commands.not.supported.if.storetype.is.PKCS12")); + } + // All commands that create or modify the keystore require a keystore // password. @@ -2123,9 +2113,13 @@ public final class Main { try { // Probe for keystore type when filename is available if (srcksfile != null && is != null && srcProviderName == null && - hasSrcStoretypeOption == false) { + srcstoretype == null) { store = KeyStore.getInstance(srcksfile, srcstorePass); + srcstoretype = store.getType(); } else { + if (srcstoretype == null) { + srcstoretype = KeyStore.getDefaultType(); + } if (srcProviderName == null) { store = KeyStore.getInstance(srcstoretype); } else { diff --git a/test/jdk/sun/security/tools/keytool/RealType.java b/test/jdk/sun/security/tools/keytool/RealType.java new file mode 100644 index 00000000000..3ce12f5316b --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/RealType.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, 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 8192987 + * @summary keytool should remember real storetype if it is not provided + * @library /test/lib + * @build jdk.test.lib.SecurityTools + * jdk.test.lib.Utils + * jdk.test.lib.JDKToolFinder + * jdk.test.lib.JDKToolLauncher + * jdk.test.lib.Platform + * jdk.test.lib.process.* + * @run main/othervm RealType + */ + +import jdk.test.lib.SecurityTools; +import jdk.test.lib.process.OutputAnalyzer; + +import java.nio.file.Files; +import java.nio.file.Paths; + +public class RealType { + + public static void main(String[] args) throws Throwable { + + kt("-genkeypair -alias a -dname CN=A -keypass changeit -storetype jks") + .shouldHaveExitValue(0); + + // -keypasswd command should be allowed on JKS + kt("-keypasswd -alias a -new t0ps3cr3t") + .shouldHaveExitValue(0); + + Files.delete(Paths.get("ks")); + + kt("-genkeypair -alias a -dname CN=A -keypass changeit -storetype pkcs12") + .shouldHaveExitValue(0); + + // A pkcs12 keystore cannot be loaded as a JCEKS keystore + kt("-list -storetype jceks").shouldHaveExitValue(1); + } + + static OutputAnalyzer kt(String arg) throws Exception { + return SecurityTools.keytool("-debug -keystore ks -storepass changeit " + arg); + } +} From c30bc22a0e4290139be446e5d962147dc1d65d5b Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan <ksrini@openjdk.org> Date: Thu, 7 Dec 2017 21:07:12 -0800 Subject: [PATCH 145/165] 8034254: Don't use binary testing files broken.jar Reviewed-by: jjg --- .../TestBadPackageFileInJar.java | 39 ++++++++-- .../badPackageFileInJar.jar | Bin 488 -> 0 bytes .../javac/T5090006/AssertionFailureTest.java | 73 ------------------ .../langtools/tools/javac/T5090006/broken.jar | Bin 24053 -> 0 bytes 4 files changed, 32 insertions(+), 80 deletions(-) delete mode 100644 test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar delete mode 100644 test/langtools/tools/javac/T5090006/AssertionFailureTest.java delete mode 100644 test/langtools/tools/javac/T5090006/broken.jar diff --git a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java index 38dc0e7ab0c..ac9ad3b995e 100644 --- a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java +++ b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, 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 @@ -24,27 +24,52 @@ /* * @test * @bug 4691095 6306394 - * @summary Test to make sure that Javadoc emits a useful warning - * when a bad package.html file is in the JAR. + * @summary Make sure that Javadoc emits a useful warning + * when a bad package.html exists in a JAR archive. * @author jamieh - * @library ../lib + * @library /tools/lib ../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool - * @build JavadocTester + * @build JavadocTester toolbox.ToolBox toolbox.JarTask * @run main TestBadPackageFileInJar */ +import toolbox.JarTask; +import toolbox.Task.Result; +import toolbox.ToolBox; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + public class TestBadPackageFileInJar extends JavadocTester { + final ToolBox tb = new ToolBox(); + public static void main(String... args) throws Exception { TestBadPackageFileInJar tester = new TestBadPackageFileInJar(); tester.runTests(); } @Test - void test() { + void test() throws IOException { + // create the file + Path pkgDir = Paths.get("pkg"); + tb.createDirectories(pkgDir); + Path pkgfilePath = pkgDir.resolve("package.html"); + tb.writeFile(pkgfilePath, "<html>\n\n</html>"); + + // create the jar file + Path jarFile = Paths.get("badPackageFileInJar.jar"); + JarTask jar = new JarTask(tb, "badPackageFileInJar.jar"); + jar.files(pkgDir.toString()).run(); + + // clean up to prevent accidental pick up + tb.cleanDirectory(pkgDir); + tb.deleteFiles(pkgDir.toString()); + javadoc("-d", "out", "-sourcepath", testSrc, - "-classpath", testSrc("badPackageFileInJar.jar"), + "-classpath", jarFile.toString(), "pkg"); checkExit(Exit.OK); diff --git a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar deleted file mode 100644 index 5e71b3b33709e910b3561706c89bf7661e26445c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 488 zcmWIWW@Zs#-~d9Ug{Ha;NPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+Id% zlCRR9O<f}PRP~ug2Fvtknm^Li#Gi_Xi%nnhOia7tnd<AzPemV#7{Sh&%KKE-7i1X_ zBb+0S<`)5=Y(aLqenDb#c4B&}UPei7&gPREC(mwt{Akm~b_1|cYKtZo@d6cs6bE=S zGKnyt1`N#CpnyRIaG!#L30*6)3qiqx0JcCTTq{x_A)5g539`dLjzfSmKqk7s0=!w- PKq{Dla5s<^X9V#8z>#D) diff --git a/test/langtools/tools/javac/T5090006/AssertionFailureTest.java b/test/langtools/tools/javac/T5090006/AssertionFailureTest.java deleted file mode 100644 index 334754cdb01..00000000000 --- a/test/langtools/tools/javac/T5090006/AssertionFailureTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2013, 2016, 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 5090006 - * @summary javac fails with assertion error - * @library /tools/lib - * @modules jdk.compiler/com.sun.tools.javac.api - * jdk.compiler/com.sun.tools.javac.main - * jdk.jdeps/com.sun.tools.javap - * @build toolbox.ToolBox toolbox.JavacTask - * @run main AssertionFailureTest - */ - -import java.io.File; -import java.nio.file.Paths; - -import toolbox.JavacTask; -import toolbox.ToolBox; - -// Original test: test/tools/javac/T5090006/compiler.sh -public class AssertionFailureTest { - - private static final String testSrc = - "import stub_tie_gen.wsdl_hello_lit.client.*;\n" + - "import junit.framework.*;\n" + - "import testutil.ClientServerTestUtil;\n" + - "\n" + - "public class Test {\n" + - "\n" + - " void getStub() throws Exception {\n" + - " Hello_PortType_Stub x = null;\n" + - " new ClientServerTestUtil().setTransport(x, null, null, null);\n" + - " }\n" + - "\n" + - " public static void main(String[] args) {\n" + - " System.out.println(\"FISK\");\n" + - " }\n" + - "}"; - - public static void main(String args[]) throws Exception { - ToolBox tb = new ToolBox(); - String classpath = Paths.get(tb.testSrc, "broken.jar") - + File.pathSeparator - + "."; - new JavacTask(tb) - .classpath(classpath) - .sources(testSrc) - .run(); - } - -} diff --git a/test/langtools/tools/javac/T5090006/broken.jar b/test/langtools/tools/javac/T5090006/broken.jar deleted file mode 100644 index 8127ec4b203b19a0deca26d353301884eb339b79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24053 zcmbSzV{~O}(`~GdZFg*&oup&iw(X9Mj&0jEJGN~*9pk3^oLA@j-gED`dykbL8TBkw zt(rA!J~g(CI4}q@0K~`l2!)a!;NL!w03ZO8LJIuUqEf=NZ^HlpzyLDh;1Itofc`X- z`LBzSKP>*aSdw2#R9Hw}fksmJMsjpSLY$gr3PzloVtjO>R*r6#adXd(Mof}QRC3C( z1Ozha7~_y6KY<vN(gY-NpZvjy1Z@vZ88tC)AC=N&|MQ9IwcV8+;O~2R&jgAQ`?%7t z<JY}F{l2e#<o|pSu#f+mIa--I(EfKD_<!0M+38vuI$PVB|F-_aHGe%L_WxqdZ*OmC z=Rl)xp=)oSo~Z7kFo!a@K$4h@O#%okNVXLNPpllCHEl<qG-|+P3q6^wGd+Dr%nk`0 z%g%;|uU0Cco*$a1))lNKLm@^hP(Gy4R<|sWk9H=s;-&pO%~LPk&Q%Zh`lRiinI<81 z7p#3l=Wdkl_;%7b-%+mdc2R0AgHWEs3!zrUnHp}a79_VHT)!c8W@;&2SgtT2A~3nS zw$D?`tV<H4&U$~*0H#BYsL(<m$+`@J!pvlarhr`oUJej1Q_SDLi^fEXDXqr6^xccH ze(Y3Q#g`)Y?pyMrRl)Yl3O%Z^Q~!)Fs_;($ED(%>mj>$ifM?&pLu5&+*ih0S*4v>6 zK_e~#vLe^@CT>3aBMLtw*?jzs;CQLj-WcP+(ySUlPImN(zOy|1De$#G9<NT{{mW-O zBqsH(FQZ>DAC)Md_4p}oA^FIF7|g2}<7?!3)P|XXMi@#EkY&`;oei={Owz%4j;T8a zdL9$iB7f9DNpRePnt*DKG1kh-uqT!bI`x1R`5dza3G;iMbZI@P`^|*y6yXQ6M|X4j z9y9YhXaK5J^r=@SY4U$EW?eA<Ifdz0v14J@?g+O;<X$~6U2?e;x0j36bXW?bCXRPs z<^5H`NPFz`q?(KQ!oOD+PHk~qPxARtm}=Rjg_6CG(|^H5Fv(a4Y0W?etDdsg$q1^N zfT~DOY?p<xc0>f>l(Lq$T%#ATC7=bmCAp=VY)gy5<TC@welEWN-H|ZKS{Z4?7eWaR z<70ke-PFZ_OoP{8y%i&waGreV7)x+0E9F24q+9y#LVozIA98U-)_XrPkreVnR@cUS zZfe4oxtw3imdksfF`4+tV)(2+!>p}|%^FNKY1K(vJ(L!l;xwdJjpjumQZ-FxA&Awe zXlEj>^y)bu_H%G>`QzkbNazMaaw2c>_|??m`QuP6Pi23Zp>j*qVnWuNa+SZEq+twb z*WUtoAfyb6yKV<t6TIhV?;FmJmg`83?<`Y}?B`;{STVeze+4KVh&xt>;gEn6U>+!` zPLgZSRK%Mqb#ip2SdJf6>6Lh9o<Eao(Pllg<$zkFnLj(0Yl*5pN0&lzOxV&m8X{>U zwO!|T3^+@4c1X)ARDH%MV^LRIYyCz9av10KR%RZ#1<?g~hHd>q$+w|!FmVIOQQpxW z9aXY^d4o0ls(zgD;Ph>r;{F1PkT-n?Yg%yaI}YYHq9>g5Fc0JRdKNfHwc-&sqaMH~ zp7tK_#U_5*8%cb#ms?;So%{3p7&b3;hx<k7Sb=ZLpl%TXS|T(U>fA_7o%~pjl1~KB zZxBd4uB!{gXA+W_pqDc%O+Z1Xv%$&V7rL+)0`Mv~4hzF@n|ww0yI<pgJN$}GOP8z* z*fA+zp-mefkI=rV3n9<GzOR34`LdCJ?*ir>@RL3nH0U*A_i(}}DFEi8m%zwa9$1NZ zoO;N@(O$bZM&VO{-gc*3xQR8yOoaA=Zr^lPOaUYXXB2Jik1kSh;uM$#03OK9X-Iwc z0fuuTY#*ucQ+1_R;>Q#_CTp=gAWe)ZuHezZ<xgVSpu-pmS$X7s`A~>d&6D5TaCxvR z6sU%$2x%3V=}-^&<HbOgN}RF@FAX+A3y$fpQPQ)w^&+P2h~*PS6u0CQn-u2UN<M3q z3(GK@7EjhR*VDvI4AV^3XwA0XNh;?l@R*DtB~c-Kl8{o$_3(9tpE)jru;NQ!5Cc@y zb)XtIXW71%pfc_F@|Ga3;L-6&2%4fkQ0xWGo06h#H_P<75#lPCMQ_;3UjmDag&;Xq z=GKW^JGXCDk2>4P?7M;Zq<<q{fS>jb$Oect*mX$3KDOpbeg{Uk)le#MCNj~ZWqh1r z^_&&SX9kzEaEXco37W}eEdxgW%%qZ2pf&*qtePXAStB}YA4_vBl6otWdI;RO<XJ+O z$svPumROi|2`Bv674Bq^H3?rS5-gl*`OAcrLLLzueFgaq7mx6HR?V!2^(V;V-D(6P zl6~m8Iab(%!});E(p-Xy9B3D-$<gP7+qis<TwfPrgmf`uOeC-24;>iKK@1d(Rv~{B zm5Gp+pa{*OHx72Uqhyw8kSMWG^=U0{kMZ~bMeU;0OF)(4jQ5yH*4R`duOCBXe!&-p zTG%t;JeFzPK144UjHzC4fjr)`1r=5e+H<4~w7X3%jVfjsy}V4&#Unfw!GO(z&Rx`S zmJ2=~rW9PH7DTV(G{or1S|c~@%~}i5NV=<qwPao2Ieh`5Vt;CDgu3rO(a}PAkxSfd zRFnegoK+Mbad2=wyJUbNEcaPAL}KdGrxqSda0OTly72{BWAcSyrSbzouv+xl*sIW1 zG|N;`i_KlrS_<mE3^sm+Z9Q>bBv{`;EKUE>N7O!tWN_1jo*Vc;1`&lqTwxbdnuhOg zeA*;5$1dh-DE;k$9L_dgTx0LFsV<Ohqz~K$<O&exsutg)0{^N=oa-2Vqxkciuksxz zeCHtnu49M@&UPzv-#y(GKQ6zAjNl6*!~_a4&n_aKU2tdK=eJPhI~4dR_h7i@z98)F zXXd`3<Oc#={+H;$2WI#Vt>}kfagRO%FBS2ZBIP@3_|7u|o^3=t+hDvIc;4~8!H=uc z!(4IVw`oK_G>Lnx5_k!SzvL<3X~K6N5b&HM;wAQxR!z5iLbQ}yyVS3X@Zd?D2uck< z$Dw3oOyO+Z-J;}jQcOFDc=ruG76PqJi5?j7<QKY)m;N-Cc@+|O6cceQrkm5||Jg_> zlkUWsi6eC&zrK3?Vo+#rBvvDr5#$ZN%KPnBy6ctF+G#}d$gZ)M+?g|N29zTWt-bF( z@YcIxax2X-q_Hc=4@+Y7_12l|wQlnc?jl&#wJ0#mtm)&ATLZwcIIhCh;BCAoSxNW1 zDu>=Pb~SVSL9QfP@i9>Q>Zi>R$YI3}4k_#K8FkqPX3hpY%9Xl#MJHoIKA{|J859NT z{Yf)=)+pVGSTzKi%gi4v!<*iLIDv&aiOQ~fj~PJF+!pG4i!|NyTvCaY-jTG_FMElg z#rr;HJVm})YJ3E;idBv#w(3Es?4D;abr5PWG^Vqvp@ACyof%7AVM0l>$5Cv>UT>9? zvOLGLS+7L%n~kx|4_)$p_A{)3DxpABf`Dc+-<I2US2U}f(8|^%JH|w{CNJ{VFW$j_ zQO;!oW+HJ&0DvsCKWN_nPRelq7gDBRXzw7XYj5}mEsIw=k;hU*eqV$Cs*)ggl#2RQ zmY!?bq&!M2A5M)5lZ+bqsp=S!g}BPVwid9ip}xLD4KFv+>#!&{l2Kt0?krpqqx8<t z>pIHrWo^w6Qq*Knx2E-e)!yqMZG-zj<L&Xjkyq9S=RrZ|#(*ZaL0?onb^buYoPwpy z_zai2GzcGr1@!{=hmqq5Y_@=oC;JFI1t>)<a2(|9AZfiSwUJ43$YQi*J|mJQkfQqx z(+O%(a6<$Hi2JCmrVukSD1Q-!0E9BpH)bHmD+gh`ACgf=^HnTHY~PFo7=@WAsTVTn zObVw}eF7iMk|&P>O-#um@57M}Q~Z-h7R8@bNgH?onRSm+%JCLz*?Un<LL1_LZk^AF z_a3(rFp(`n#ZM~H<mxdtkd4-eX0f~RPBtK*L<E0bJKG(<7Fcq6=ffP)=P+mrjv6*m z0~HA0&ocTBym#p#kXb!r6!=)FZe-48m~0<&1i$QBr@F>NpPM6}p$x#O0RmY;xIc`G z2C>p&gu`%<bV#H8kjGcPKRf=-6mAzf_fR4-Qt}v*b*=ESjw-+yWFGY*s48d^sbx(c zK3sPg>B<pXH!+mvRPho!!x$9BGO$WIE9LIBC7odq^e(YDjPY}HfD{$CDP2rb7@sw` zjZC-K?GGbzUydYk5=Tn^Ug+hys5<{E-?F=(P;T9brPD@6x6njKRLa?(3DLf5X$xo& z-hP^``@&FwBU<A-*vm#S#Hwl7U{Z(dJ1}UYfeBar7OS=_V=FL#)EA5mUTT2ZBg*2R zH;ye$I@1!bXp5POoYsS(`j~H&p#@=#A<w~?gvB@w*%E$NpRRO8p(TF_#l+$$PMpeM zKj2~@5jD!DrYloTk)F(4sFLhHj69soP%lp*mrX=2NQ`D4YubA_0==ZBXSO(v2(n)1 zi`zMAnlO;(3o8NX5N9+$*o2sJ8mBu(NaVHSPdDa3Mjlg2_gM>!(c<2)4bYw}@oV!f zs*VcKcf)+1-bqrX9X`Tr<62NPY@oT*wtN9AmG&>7Aogpx-rw|(&&Ysl%xY$>LwL!? zD^POt@Lw9)*n&weAW_f^xWQ~}g$7&aFnd0mjhhKe@unb~P_r@)$3Jj3<!myn8(vS* zttaA$xlExBC~g~SPGGwU8jXt`wwt<spQRc5k&Z~$luiKJ&9O_1U-=oJ{$*dW-xx;O zogB@B^2}Lgg~lrP990>xY|kEsRz)WoV$j4E8_qVrWw~gOqvP|m8(eXs5+Gi2F7!0S zcVSc8Y5wIyGxx98=D<7pzN<^Sx=tMCIUwFw)g{<NOTH#RxWY18q8WQMw3U~d7Tk{D zD^{pWC!lw_ys5$)Z^4NX%NZi}ZS6!+36ZHnj|MVB6*6@4(NV{`iZN$FSHVcUw7hWj zD}v?6_^0|KFJzGp2}<^$DvCU;iabiQQ<nBMhQ^#l9GwPSV<2lo8%Upu&O3cl#@$(E z>H$7WEuClfQI~+4%IvgS;?vn=Oozp35qc(8N93>iQ-a<yJ>L$9rH2W(B#ckgzlXL6 zaZ%<qS~bJFVu#XZHNVriNzdUm`=RACMfp7O^IW5A^^v)Hx+3T=Oey3rb(GC&U4h>n ze8<%68h9J$3YX>4>OAmVZ|w&takW#A-s8&L)E;nE|KS?qlp8rw$`xxq`x2g#$@oa? zBJCaG`4GCiQ2fp;_W(BYkdUx}aN<6?1Kt*bhr{4><B(J!BFyY)<f?|P{z7YjYdt9+ zdOIr5%eMmGInKVe(Ck}x1N(|2Re#l}0c556rqR`U>ALaL8{aCy2mZR_*32f_VyInd zh;8EdOsGv{`GgmL<NEOWL1x-}EWfkK+6pbJ@#zrF<2GrbI{q3xEW(?XuHrU=_gW-) z16OFL*D|Wp<mR5@teS~~$H{`{5$+e(gRJbUXMq9$;6nc$)?@w`tp5Y&Ie%I&@PHYC zX@Xe^gMBZXZW~2>t;^|KoxOg^v3QN1-9~f*6BY*ZDeJ5G@|xDy=HU6BS2k;qMaD=3 ztZnCIss;J=yS<*R89fnjZD0XuG!?~|zCrZDWw()rjG3*eR7kxMIuWq4jhCscU5UDp z6a^75=~p>81DI4oU?kvAzz}rRbPQDtRrGWKrcPk6Sr9S=U(<X({gy1qj2C|<ecXN1 z$MN6gh~JWhu9L3I-$)j}#16CHHZGPHe`5#xA9i*&`hO#Ap#KkBc?U<mKfD?cGbV*a zj~o!?6La~lj&G@}D7)w~%UsG=AV-{p5kVfTXkb(}YqA|;Aq`bI4+_^MNMMyyf+_GB zRe53Xfb+V3uke633ZO&3(<dFw32RY*@qxUD$xPe7oj4+vuDwlnk*hP72m^Wg7I>?i z<rOI~zIK|8Hli~|C_lwSmc{gwEly#&_Yw&YeO*scY~R{p&BD3Ev`lcyYqABu`WoA@ zW?e{j$&xmDg1gjXUxJ|FD{yaPZe#DP)AkBy{A27+scn@u9PS&b%L|KWW6=R2yG*3+ z!6B`ht0stMW!Y{1XBXMH__0|yO|n4Htx|QK{ll?=GoB_qWFH)(?nPadOrA*HcV_1j zNvYVWE24DDkxR#I`)l^c)#9?6WnLTAN9{JdVa$|E91F7aXU*j_TwTmsqIB{K2_CBY zD~<hT#Yh?tIv9>Z@m}y4vJzLYK#UUA(QKigKJz!X$wrg-Yh_2E(_zf*U)-7tl<7DJ zt3sK-J#ny*cp|CqGFV6_MSTPg9AX_*vd9k*0HE|kg!`BN_E&|9=zj!Kz|_jX)XG@K z&f3Y;z|iiGm|BL@!*$Cb2TUl&G2o;6si?LkNq&VUB&b&@wMr3dpYC9JwFKksibAZE zLp`^Vwi|yO-*IgPPRcUvVVJ_N<5ptPMv9yxj0<HHLWdSPiH+q*{xR+oOjDsUc6a9_ zi(KG8o*GJ<9sU@mx}5fWeJCC^`Mg9%D5>5`7owr{1c=lU&>7J)%WKCpPA>3H`$CSO z=1K!3;-+WMR$W>GA^`b_Vd(3_kX0{df>Iy_!Tc&-)cHG&68K;n>pz|m^1~DV-3z~U zx__p~uUG$zslAP^gTBe1givlp3Wy#)o!?-uzy;9tYOaW$g-0;lg<!x0Kf0*0enrRj z$f`vXsB_;BZ&L)rIFYYM2njD`JS~}>oBM3xevlL(O>oXf6Z{UFjL0Ak_vr$0>+&#r zA}GyO<6h;Yo;IKyhgJ}xdF6Ywk75TbP7(etDRhKQ8EDT@B@+<~rpqXZucHC_WL+71 zRaEp@Nya`41M&7X-ptRv`A;$TO}=_Q1mE2T>rX@(+w~3N2mLFB48ms<m~&MSuA1L> z)QgWN7>`M-PkZ|M6A~so!ez7!`;d}1ei9KG$h+<;T?q#OHg2C$oeLF>j-)6dh=plD zfZwz$i?xc~FAv~RC}aAYG3bGXzTiG6jpKTn-gaC~n`%!SV}ST0moq<^A2A7F&XbSy zVVuDdU`6LglE?UQ36YZ##}g97dc<B+lgWlxrQ>t*{(3Fna5H;EK7!Hn=WF?g6kG5Y zP5ED#W#(-EH#qbg-+u@0mz}bqp1h%*ld1lnJl;~aTdbE4KHU;2*RsA?soM_%_X}-c z1~;S<ych}+@>QK^q3(ziQcbFY_t>w%g~UP#3&QoDx250jkDVSYJc9#-Z+7``g9pN% zGFBmL6Z&l}JsNa>Iv1_bn_x|rx%%wa9WNKMB}{ybdBBwz2uOE`mC<if@MVrXDtKcq z35Br7G=E<GC^?N-R)`oyp_U0@c$LC$sg2O#g?NIn4Iw7x4Jq&~s!Qm|w|h&lB3xxV zcaktpWn~gN@{Mh;6HZIUm2k$X+fb^6^yFj!F|kPEGJz09z5C`hX`fbHyS-n`y&etB zs@w*RhA!tSY9VwUOd5l_NNB1+a|P8AAhiZ&co`ELh$Xd43F(l@=bZQ3uDgR}H_Ped zQ39{PRtJC}de=GYudu)u?39sygyrZz^5m~!!TBdz{ON8P-47L<Skl_yPmg;lsLRjD zz`wzYrm}#Mp{RwGqfsq~$#ioDi1p{B7jH@c7CyzztMuj-7f^4vdiN-6djv25Gcssp zyyCpUI!<Kc-*cThJ58K7CR$vdOe|ag3|#hoqO~e=z*L5p6KyWuMzx}B%I%40rFgQD z?)C@w@=D%6kHrOHlX8XMO_II*mQBe!h|gQz$$(zhW{EW(p3jOY>_?!WGOv6)H+%go zeB3PSRlw;+J<L4=J^iu`<VVR%DicCu_$jQJuy=Dr-glL<Wo~<POXY=$*Ht)fMxD-5 zze14`Yus0>;YSXH@8beA5j{d?jYDe+1q;qhMZr#?%+sfzp;pvKkweKG4mIa&vOptM zkSZAkx^`>x=3sE_F+OTZOqR9Dx)9j%d^{arrp8!F>JO<?6xFR8u=Qf}UV0c+t2w%d zJgeDT6;rvYuc7x8!hR;m(;G=@+25bPlEF>cz!EG2z|8zGLmv<XWyHiK7^J)96<Mzd zFR?OAH!C$-99uQ(cUfUNX$Uk=+9NF3q!+yOr|=H~;{{^^X@b!EmVKZDR|kVqY9$}? zTK&-Rwm&i=oS)2Nv7TCVQ56wr(Pn;x3S*1!6i4>99qoC4HWU0Rf|Z1ut9X$cC7c0! z1q;x_2kwXM>E{u=>bHwO?spekM<DQ4WLYKY@akVDj49}!r@l(w=K+Yu`G$wz-Onp^ zO-tM<Et>rGfst_N$Adq13@ZUC%hnT!^H;5BvT9myZ|h!7F$?}jetu1G6WZ?*GVBaF zO%g&=+eolmP>-(Ux^Yr54{*>sCBX&vf(%p^F-`J_Lzk#rCxbEO>Go;&(#1{Eh`1W0 z%`*}FLyxg*fsMP$`P6Ak-$>%DRLQsnjvOKT58i}RW&@R|hAeN9#Vc4m`eCYmwr4+r z=7X=z_Tnp)sAh{f$3*EU=s>-C>>_XVd#0tsz5@SB2oc!znSl?Cx(508!V&owoWl5P zLP#1qm{=S9Su~^Dr9R-t-}uHDf|$)e5Qsn)-=wOjI2!Q_fAAM7vZ8S#dSlJ;@1C%i zX2@IsZ~-dvefaq*=~hm+XX9R;-X1_~gOh$2x|_O*lOw2Ul!c7$rTY?#ob2tQb8?`< zOT<ad8Y{cLbWL?E_MIIFyrt7@KoB@6%81#VBg0(#l$QQe-kT;p=bzm<N@e_CTc<g= z58O2mf7pysK!AG>6e|)zC}vc}4xQyQ<nu+%KMw>RSZF?U=+HZp>|DBW5JFSls7H68 zcAH`NY7-U64p;lLX^5Aza*>2AMVk#DVNwfxI-_WMNde_TV0nYwUMovG)DzdzR$Gy4 z7lF@>O2_-<N31Z>VZaYi3HT3SN%+6{QO?lb(c;ftGRtUQ>0Ubc{>*1unJh_DG%)|Z zZ#o2eMgnFCz%s=VIy!baM0UGuil@jAX3!o0JjmNI*+-LG2A!>inVA>&7ti2bFeJFb zxRKo9pd7+R#B|MVZW4QHU}`DEDlG!ZyGl9^#uDdw^3BQ9tyJ|$hbRUoBa=gf1oxu= ztugfS_><cEh6pq5(`De5$kb-{j-Y64s&NCw;aYKu-wOh*K&2XievIKmoQSZAJ5xIj zP2Q|^Bo~B-f9h1~+h3od@<@^f9f*Hq{K{)l1xd8Y8tfV;H(FksK1;F{u(?s+wA|LK z6vP@tQ;J*K5HJ|gem214{v3}4&bB*qFom$N3FAxW{|@)dm(mKVVxS*>jQrD=f7H&v zzxWdFKi)}uYh9avE1-Whrv0DB^3wb=f097r-}r9mU}z`r;A-&?b?g_$^C7<_<Vj)) zlFLjIDt4E&>rlD`$*E<HI}`xp<ggvelT`<b%1g-mJHPu^c03Flf*Bcn$^3=!KP}3B zjA5h(+25VAZ#dJmrdspz-~gfbm*IJ>CVUrA6X=u}z)5ZxBwmf*>gUGLY@*%DhT}i? z=<CIMhTnG6=mGI#OV=i&0~t;!pK}mtR5YHM#fZx>Jh}@nX2x{$BT!YFo3D?!Jzf!R zkzJUdKKAI~vQ(iARE)^zLoB}aAkd~kGcrZd98JtXQ3(8=XesNmz+5}TSg3b(7riav z8Gw06fsw$oP)2=nCvTbDNZcHEQ;A;i(c3J~mYJzJT5Kl=H^%1`DpZ>6=NMt7wZ`+! zT5yUD&r-{k{g~9XSs|nfN*fZjG=d(nseyhyh*JosMO=2ZoJ4;qk*#McG)0Pq-+|RG z&}-<59jpQ^RUN6D5>F_Jo^zY2IFyE77rIoj3B4gZ6y(*U1;vn{Ko?f(iSxz2hVJ!7 zLX(uQG&5SSU@>pmLuR3PO3|OC29=etw-xMx7JTUxx2K2lJye7jpw*m?)nss?Fj~ur z5v+cwh8(4>JVr6zzP+0o^((wTM89Q~9LuA>?;l6(*G^l0x7R;}2YXwSw2H%3MdOwy zK--rD$P?562Y>i7L_`=K)&wGq$)p1IHpHppe)s^T8%8zEKYjAl5^}`=rhb(Qzq!>3 z7)tvYjx@l+;1c))?H8NSQSb3d?lxWFtW{RsNUNfFJMp?&;}(N%1Nd-AO^Aq*6e7P8 zbqeH9>373dY-9F6io#8a*}Sv$Hl61jnS2dg<L>?`8nU~|<FRrOLwT;&Be~9N)l5(S z08jeZn#wLV-Z=~SBF<e?74@lAMk80=A>!KsqiPQ<vZLrKKi?YY3Qvkm7j+JJdYtaJ z8mMwANMcQH0iG0Uq30`rwdTv;9TK#3<z)W{#0!1M2><F0jQ^5O_<x%r|L6}ir7>mU zxs?~S%&Geu%(4{`s*CAcu9nac8bTncu~b%=cr+b|#S=a)wT4|UcwB-%<c0$`Sn+oA ztV-;UCbQqzT3c0C0G{f~>q+L|SJTKM|7fC6&<rKB5bpg3vL97fP=!dQ|KqC#L|!C} zXdFn{1@g%1%5+e<nh4Bn{#r9#!%=a^T3O<npmyeCiza66Jt*q1+C3r4Be!P0Jqq}D zzAO5W+BvtqhuE8?9bCO$-~J)rRZ?S=0(pb8ANH7gm{A+=mM`f;h@Dnp;+F4d>Ry&{ znhV8Hqs;EME@F7E-%aic^VVd!eKd&Xzi;Hzc3i0gex_ssqSTk<lxeXn4f=LgTqTK6 zssfDCcdI|-eVO*HC6CB@5wAY(uA@1U?`=ch##=PqtyHkkqpQ9%L24O!(d||bM2R#r zJc{G2wG9nEX)AGCJ45UfS1*_Q#8NE;Mw<><SOADyh%$8hfF2OFZt~1V6{8>awM8wq z$H>JRB#16(sY{PZhN`J>xgR$1ifV)O(m2lU`R~RLh8Z;^>H{Z@f&V?YPJan5@!tlQ zSn(e_0uc(TAG{X%jpl>b2Fhh;36)SOwvwUr!H8LsI~FkF9HZ-ic|v`jr!x&T^?f+c zyqrVRRyEQQ7QS8D*t(FJfC;t;JsM@dr`m8%*u7hcl6D6Wro;IH-)IO;M3gN;vJ)wB zE(_^w_&`sR3w%$^1%W^62j}{f3qYt5C7z@cO|iE$T`Lg4pJaXnEw=PQK26NZ6V~+( zE2+#&m(`3OB`Igj%qTJH)m%6XRm1}oqmaHa#u<gq9ZfT(Z{GwAYls@6yV&xKca|j6 zRMN+NeG_R>v7nLl#zE<wHeMabv6?UoBXK2;DtwC3rP`V@kQgg5Rv09?{BrHFKQ2~V zkbIM3Y10F&BwKsNMlRp@wVFXNwcAqM{1Dr&1Rf@inW}e){x)q&gq>a5<RG3O7+f*e z$~4j(DK>i3hp)j1_nSzRj(Eb#j|^76NU;&KnR@dlB)>*no*4SkZm5<i!HpnaDiRcE zLei~jUoI$UKkt(G53Dmc>aJ*oS+f%}XZGx;Pf74)D`;w>$^K#UbK}(|1ghsy4xS%P z@bG~Px+7(&u4v*bp{v3C@pg@tJ&9BcTq73GQEtFVrRG>(fD@?{(&x;~%l9{YleKnl zJOW+;<y=3S#OSDN0)T0Rir#jQ2xJ6={?8g<L@>)Fz}~x(RhD(00aa3{bNEO1dQkLQ z{2Tml>JXP#BjS(ox9fS)VLv_jsBo}w&~mp~`es=?F1DgVPcA_2t`6=sAJF7DqdVY> z&d@Ex4MLX@O!t(FnOi=gcQN0}YenAzq=Q^|>%j}+rf9(o6&)b8CB=!|pW#>8k~D9p ze_q|Z*(pej<pU6nd7c@Sv3-NSAZ(f>|HRo;1=`QJEHnY}$P@e7LO>R+Ptt#ux+<a< zCZLUE`3Q|roxr?P49Zk`Lx6OH+7R3p5A9}3`xkLI&@{tp{Xn|s{~)ure@O<CzfXpL zqW}^AP63L4Q2-htcUg+yBAKfE+2nDB;eShq(91v4p?S0`Jp8b>vv(sqBLbzFJ^F(J zq;dbI02jQxctGd_;U5$rmfk<kpC&o{nW0&Pm@9g#zbKrAEAH|~5dMb8Xm94jpA^8l zAWq#SBQxFjvS_@Zn2`*0Wcn<vc<#h4J-f51q}&{<c+UB!LVH1Z{;Zsn)pC^*bSa`E z!55;oq!IeBQ~egU1eFZXZ7tBltrf{I8TxdnoydHnb4o=$4LQ0;wGqcPRukqUZAL?e z6zxXnVa;F2<!dR(=c*xj5IUiq-K&-6M}@{%*bHNp|5^LHCiQGqrb{@2(nN(M^I=A8 z3ea1|i}T>MiR*3#vI<^HWUHA9b$=CqtPY1I3ty2a-5aIcl#ZqVaxt3H`asS3OpLaN z&FUlqSmntOnievs=_G~8C~`<?6ocq|{kOzQofJ1A##*O***R*`PTBbd>|7)AV7-(u zen^Mqb~MG^KnjQ)L4F7vXrEZ_sd&m%<}eDF{_VjhYU;{kL*wnbXOo`p86M1*w}$Lw zrrGgD)trYB`M1smU*YxFcM^u)Te0QCbjV~nYao1_ZH~n;a8XXeo~FPPojm><gU!q~ z;D(?CYxs{Q7@N5LJmX6^>*P%MhCcwD_aL&i1g;d6{P&EgU}7So9X*v*zobRK1K>7Y z+mrS#p4TT^fD1W2ylG{i`n0Tw=#Ng1Fye(IUYHihvnfC;^d|N^H-7Vt;fa3pckS2A z+&mWzF_`$H_K*FvT<1M+`lIw0rmB9F{%qif#AQQWJY}csCgIXUOuz}W8h;$J<CrGD zZ!Mo1Jejh;sr$o~E7-6aBBrP!er<W8S$?!2Uyh>J81wi3-QG!nD^YcPWCYlM)c&l$ zWW>K@?tj-i{;2t$(GyZVeDDLE0Ro`>Z^49wDd_r}dOJ&DU+4)Ci20bhXMiXZ>eQ#L zWVOfq;5G#@3PJkF+>%buSvfnCt=D<AHUQG}v<E99b0WEMK7*BSL4ykB(B#|DQk}c@ z5GmTF&T2H<aqr7psc1xoN7_lUbLjt2$ag?iE+C@q2fnB72MCPYKL1)o67Pn@ljSy+ z(H&+#T1)SB9nLvrZJW3v!f%{|@-`H3EE#y1yD7NE;6D<tl_Vik=`wh==F`MLy-kW{ zND3GdY^hB#gW6bNd`DfKmXH?Tfw8R(nahcl&LLc|dL820^rSt8*brUv%m6tMR$p>v zmOEcwk5SX9Ntc%xo%*H%Z6XU!**t=QaqWG&ll1)xUZ@IKQh%~qxDvg+2X~ve>>U~L z>v%wL7nmGz{&&@vBLeX<$(F$AUet6`F-!T~i*67n8NRq#HgZg%-L^}}UwHNHhySq3 zNAVW<h|quE{P+Mf0DWuA-`&k0t<Yc9?O#Kvzbxz>t^URe=HIMr3~l~y{PUOI_>U9* zHnp)cv^TVJ&~-4i{^Jt=`^>>Fp7Gz;{cG>Uaikj7mk%CfQuPabT~&M!CMNBC!@zK# zZ{Iw&fgrvMyvhsPrw^T-!7d;KRjPLAH1*)*4WO!jRb-Qhn2LsbB*UDhj*cKS%GF-+ zRYzf@ylBEWF|C}s<BUS;O-CS!MKJLuyG#T<kHH{SY=cSnwbq35d?`4ghJKi$XPQIN zT)s$N$(9D!XR~X-780qc%cbygAaHX9xAwP6*Z_I$o%-=8%OA&okITo7+&>@o@3)+O zP1F9vEx#=PvFY?jH2eONjUQ7T|HE3}!t}%a|JLBP{ACf-e_tfVuOcTSC}?fvVCdrT zvEO0yF_&v<X#Yogr^l-+uL#2r8poohF~voHf;3HOvW#iy`#P3ysg_a~);3^gG4$2k z&s`p+`dbk-`E&~KMowlSoagyb(DBh%^fnp%gI&-&*2MSG@$=#Lm*)d*fJ+FkRR3dQ zI-o?&eFsUu<Z`E(T9Z$b$|);vk(KwaCi2dPn8K-HAO+!U!+Cf^6&rY=h_eu@#Va%u z@@+Ksqba5`R?7(sPNyvPE|nl7AO$CBjoiHr+9{*vGKC;Na|)(xCDsLl`j;?ieM065 zXZPZqH5?RFAu&Prv6Yx{jBF-!DRK7ggx){<VA1U0?xkV=M6FdYMoJ$kWLF2VVz=xy zPf2613t6^WY0%g&-+d<c5!!T{b6APPa;)svNTgwmx?eu5fgHtcDO#z8u9PY_i``mZ z2C-r}RIBhnXP{{j&>3h*Y7}$VvZ5SN1Okr@FGyznfn%mi1bc5byV>9;O5<ZrB%0`) zfXp7*t7^_{j%?gRg`H$dO?eKBOWZP8L1XDu^-kkcDq=4jsd(y#31*&2))<IHE=6gL z1fJdG<}#YwSA8Xd?Jc|ib-HL)nyt63G1h}upP&syDV{^onwZn>)i;q?n$zqRWl&9l z!|V=1qiC6W;hYUj%S<n0Fsw6DII-dk%f5F5gr<2_x6aXo2n7huc31pP4){XT>JgaQ zic11m_S3U(*V!vlHz`4Xg+-w)p&Ul2#0VZNAayXf8%y}@8o#!5EFaucc5t4T80xv% z%jT)_3SqQMUGi7w9=;^MJX)}%vk|&TD(`Q5nb{qIPngH%MU`hK=IU}JYTV-n{<@mq z1F>{v@*>R65SqM|N)QG+gL1YCL52r+7!aUmWX`wNL+>C*GnYU+4AI<ijfC4Gsd?Ec z7ZFZ$LtCBtPO^XOHmSe5B+%yLS?ujhY7iU|V1P@e$5(sk4YbR1hzlibl1i7IPP}Z9 z>z?Q)JTNRygAtCrND|y5hF7$aFHhX%=!|@5en5qu_b^)^xG(Qq(>UMFoQRcPxv>ru zv6*}V<nzL}@uXiN8smpk@P4rMBTQI5w+3WjcT6MhNsaFUVR?}-BY<9Uyg_Zs4_WG` zaqMT@`SB5pAcoS5*!iI8Cua!(cul_$A4Sm=W|SIKXTp>1iNthb0!fGMC1BrD?%90v zn`7;b@Y{1DWTD)9?7u!aSk~7#%>C?x1`ZW}y#mQ3b$oi(M{-#Td~iu*55B+2)#i6g zGtHjFm!Z()@4>$n_Tz%w1JYG&>;8KoN=G<X?j_1@DU`NgN5v)8HB5jt;RLzIrz7A1 zYa;I=n~uy@sKoEQau52u(#Rb_9@<;rKQbv^Fs=`vCWv?ynta=`sshU>UqG*k0`Q1< zWgp0|6QR5VUg~=$qC0)8;am|OIRi3@I%OW*ckz%59um9cs#)HCvx(aB0&Vh-a-{z; zeD*IT;jh(*^uI&VuL>aWF@MHD_b2)&TG}HiB70eFG>A4*!{pb?K>bipO`sA&&I_Fs z$X`UY5C`1YW>KB)H$Y;+BFo(YcN~b~cmw?Dw{wVg0!>ZG7@27>fVcOsy0$VVj&%A! z;BJ4Bag%nCcCcFO{eJU|2QaWDO#dw(LaQr+3~vOdDMa1QUA>o6woX@+$7*P>^*a6C zr4r}D{DCj!GqVS}kTZbp@L*`4sOi_n-q0L0XC?!tGP5e?)w#>>fn|Ko)Ai@^WjU<z z&RphWNWl{Y*X+3}cH#9J^ERKziQ?<22N^50+UjjQ#g@i)p_U+hG#zMBucfA-UUNk! zm~n7hR-sveQm+&9sl^3;#=RDd#U(o9L7{UxD8qgUA)Zm1A5O~nJc;LqR_1H0AxCSH zU%n`{?8E>hf+>^)O{I`wS-BK>mX2nEi>r!il0009k>x(`RQ4!m)o+(L8c!vySNj;3 ztjVIOem<ug)@X(X3d;&re;BVsGM3|bOiXk%l(VPld;>c2Q^jYex7K;=x!^l<Iy8)? zTuKpJbA#Q2m@5(w>EP%ibi>l8x^>pt{FKCDMIBaP8Zwj-pPvQ=F@x0{T*AlM^=JZ_ z8Dhb%HTnoof$_>iwY3A`#?2)0E5pxnMQiI@VB13qD<hcCOe0pn{?^29l}zQ97(Q1} ziRn(NmBg($$Fou^o}WZXh|a6zFv$6Ta&u)-)T8PtYVy?t8xHrxSK`5uHnzCyU<JAf zUBD&_nz*(R{2UI$f6Q32Ql(l9RJY^eH0GwAnRy?S&0<uQG9?*HuALKz+{0i+H8LS2 zdwnbE?GK7HuF%vHuZBEr6HGnS>)0(QGga3bI=@aA|H_Kfi#}xwBI2kqP#2g63r^~Y z-34E`C`FnR$u)jRwwcxi$?^FxE=%55Dx`~CmUp7d>!3V$M+h6$CEsVJ<Nzg{(u3)G zJ&FM5V<c{nxWFZX!km<j@O1)1n7-a@Z!||DJBjsENLwmBF7k}3;Q0%Fjm<sC9e3d! zP|%zGrR?0YDj}VMW9+Aw>B*tE=nhg^K1XZtpyv}7uE9x9PRr&{Ge7Zuby;Rl&Jv@| zinARH>GtZTaKV>r*ene9YB;|ay~ZDIH;fyE&mrbWu+zd73RAqs2alAWx0YcDuwo|I zoQ+LMn}m0w2)6ZrB|D!wa<9WFgO>%K>EC96#nbqFDLb>+ie>hP3RTtmSf{u+?m(!= ziuzq)a9q{~+0hp%uph0l3q34wXt(ODU+scDMRef?@Unkmv}{XHIX%<Y!PE`MPE||R zCF=M8Ky3nmL3{L$VNNLtW=9{uhSrBINwRQBlcn$6)mn}UW5?K`g?`LtK}g1O7bamC zAq{5P+K(beKHndh7bFzS1CX$H21OW+9#y>Eq7J1W?9nX6XU2`eRL)B=u~qu+hH`*h zJfi_zjD88p0Wv(b{K6XMXqcjN1nhrPm(Xt+7B!E+5v3%B7BY#Z6416xwkpf}<l2^9 z1>G+kI;kL@r^27dOl;?q0x~X2LbDOkXGlZ8R=J*exT?d9mrW_toJw&aN4f{#(*@V$ zHEMnddGcr~RP~(>T|`>Qe%M2gzf881(-5>}80GLf{vn(!GZ$Q2L4g2Oi6BtCO@_f- z$~J&I7x9R6D5GTm^gHPZGhm0FyoY3#H3ivyP~1k@C-uSubKTC*r_TPb9W<vXJ(dep zN~KG1j?AsY3FR|f)yw;jAw0aSCb;a*{<K47JuO95gjcd99U*J=Hl11NYnp*!{x%3D zbO*g-H>jXC)|3I9nK}1P78Cln<rATfys;2~&!wUD-LaD6IU52Gux}MeUzVK%lW8B~ zP~Sm+bsA2g?@@*yJ)qD>6YwwO{wwqR{C`L8KXF^x+(y9|!%M1WskW8fr)sqYo@{|> ziMTaDz%4t6KmaMlKMq7~5)&>}(C*O4nf<^*%%qxnR<2G<tsW)+zIDC<9}<+RU$$HJ z-8K0*obl}NLD2XmH}Yxl>al~H-F>LQUy9`5I^*7b{p7}L<89mffCfOtFN~ni=4(Hi zKv7pVzS`xtCN44>xMDVJ-xMS~*{iBh+CZ5IK?48F;0HSe4_5^)9Hd0r;s-l`r~vo@ zWq}8Z4%Ds%!C4nBe^MK!Z`L4}V*ni}U9o^%C|!+yY>^lKmzrr1n@0c{2xfzQzQ&;E z_S;8~W=vb4S5G;ii2^3b;~i8TZ8_VDD3!%7n*H|*9@_m_6kb_6ci#+8ComgkGNjlC z07dxgD8J)|tSl+KbbV#8GDN)kGR-6><zc>}x#GT}**^YV!p_fEji!<DD9+O5q!gS% zi9=r{(sZ%!V!1Ht_^M2wWM|FI$ghemrapZ<?kLG^n#+Vt#)%RFhAkn3Fit2q@JPcX zvGGk*GG}b4awUf}t&X_>pQm^494KcN8v3NTJ5RSB?!-u}o8ssvpmqB*^SW$Szu}v@ zWMH*yw<8Rr>SU9=PbD|ENpM{fc`hku&0y8jPi|4M9?c{FjMZZG0vG219^*v=#6)Q{ znatR=TG2+BryLX!8BRI_HXNaxr@(>afP9$6z?oFa?Ibh}uHwQ#lPLyyK`bcZmAg|$ zL@Ao4I37a5<ebVZGX-fQah?`&l+pa|Gb^G!7<0nF7jsVxUd)kfP=@ss_5gV9+sGx2 z&3%G}xb#TfBMU#69Q!ffiZ)j>G|b{JZqY_N>8FmA`J~T-3}CGsf=f>NKk9ItV!wXr zvfDte^Ws8g(7W;R|86U>|D`|Gmlb2=kWo(0RD{Db%J-yj53*MOkVpafDRA!KFp>c0 z6G_uFQ^PSTrQWA%--J>DS-LRQx}VuxI|~wkRWoiR=Oz8m4fQ{r(5MlZhnEBAh*myF zPgy>%*D)9I&4Gin%cu}r&Q^`K8wsF6Mt4L)3V@Kr4tj)FspGue+xjS^SYm~g8FJ`n z7}o<Q*PS7ME*EzA;_Ws6WeJ8cgXJa^)g9P)ES&zD$T$OkNuda<aDH|?$(AzJZWKYE zgH2Y=u+rBN&$!yRo)M=h;BFrpavb#nXF&>sUMLkzG9c($;yxjOY}KH^&M`uw+lCiX z>NdYtsb^6yRI4aC&5J&}A{)kfs+6mi)4@HdZrX;(YTAa;YU)zdBeZoy;+_@eq$=aa z8uPWMn0%If;Cf(%N*wQmFfl;e+MF$f2Buu4atry1=>>jN=hdMt*5T%`Qs+DMXZlpY zLAfK}F?*S=OeOjXT_EEo+o92Z2y`MHix_x%?i1)iki9<t`6?VwiuGn+w~U0J`DXJ) z$#i-S6tN`W`ncxSdSB>;cZ>H4Lt!|jNapz|pl&!nC8cMcQ;J#G`1bX&7Ngo9ZK#wn zu|w}JBy}|~a^UF0LnS0|P#1o&urn)SE8W5#FMaqP=xod1eH>-i!OhE>7=P?)D!%I` z(tDP(jAc_A$h7$Ff*^kjX_G(OF(u{`))lPQFjA!8Qwc<gE^kfV4oV~p-jr{DZe|;G zK^~t>RUVbJ>Wr*Bjai1%$%#JP53q(GtmlH{gEWFn(w<!>539{4b0QJV9uD?5L4H6i ziY&ob%#GQb@c6+LL28auLxnAeFS49J$>=**zx&fkDp%+3lbAXWWw;3@Q|}G8n!QjY zN{oviy%#uem>x<*S9(83j*ZGJ#943mX}fK1(<ill%b=1R+-;5ya`*Pt<qWiD_YUrp z31F)@h$^mc^6B*T(ED*c7%wd$vQF98l;A97;Yu2{n+_7=hP~aka9s?Onw6DqO;nTz zR>k24CU(n3;grt5$L1ls!B&zZxpOIBB!`YJa+&A<%v2e+dEkhZJ+IG#lN6-&#Rgo- zR={dM5tLoVqTBMD$8nbJdHrFZ(t-mmu$+rFCO6Ex;b>v@WW`NU0sR@YnZK!*%StVK zSOpL#`jci|z54Q$9Yh6=&?p>Qd)Tr7@0VyD`5O?Uy>>X!iy8<$g&e98Q@8cN$q3vK zY`ySyxo227*UB&rk=><4ddkeKB>_1-%28}U{o5z}%!^*VRc)Qx@mke`r80X<3cW^K zOWdv2No(1bfCzE+9Z963GpmkpbAxb?<{L$m*on2V$64xp*G<3<?!GLzneRkYQ@tua zA`h+iQ$Rs#TtR9YK@>$(7aVoKoSH%N!+Jn~x>i8;*9|XnZz}w*Kw7Tn21gXTPZ4cl zbx*mc4JFglT;uaDIa%LdGL*ee^^Q<Hgq`YqC$8OCc@hN`-}=!@9`^*@6dkdd4-nQ- zGTgy#*7b$=oke;-kyKR`?jZ_{ZGloFNocSM3{iIkUm%vG_I?kRhC+kXRm>BELK7;< zv#ExEBJv{Fz`hdKO@}W*P=q=mEJ3D0=;0&A4-h8=B-fzu@BS(Sl_;E4XWln5{PhyK zBE73-N5HgM21-ak&eWBdSh<1>xgxs9qX)LurYn}}Qa;HKNn&wE#n-qBdm;yL!d8b% zI<ljS;F1vO332J3;rSSa<cb-3(-C?L=Dgmzr(6AWRNf+VhAq%GVG{>?P?az}_pX)e zv*zZ8&<hCf0PPjy3ufXKk5?qzC8cVSAJrY`EPQREkO5>{jA!tKpm8y(%^EK8;XUFD zvfpfJo-?WL<&a1?l0iZeWUFnQ*3X(*Qx)|6Sh33Uz@b6_&*Ls09?+w1s#T(L6c$0d z8Vq#v&r{##HO^a8X|wZP85St*o6k)N+`m`{qr%6;bH8+rtHe)SLaBjz3DSCnJwD(( zKJwVVxzFyvVGny7IPpefsEC;VOba9w#;Ye}K=V~7*P>Bd(<s8;dnPW1^2FmG&R;l- zvEuWOXnSHX%bkLp&FpuCqO4W>AvU<33Grwgxz&k$Nwa+w8AOiO6uZ2=BH+uk8>mba zr0jqVBOnL$tlaY!v9o;vTJ?#0@wH4EwqY#ao^IIzVbs{3Jomku_^tNa6hG}}{zVHO zp&mocrWK7Qvua@GFDPK1HGQ?-j4SU158j{nO1US^e68ZLa+?|@4!d)jagbwTCga75 zLg94tODqn-hkFv1V8wKM{nN!4T@7^yf8=6Y)N<-j#>GsP%%YT)T`H}UZk)gW9wKGk zI{x&l*S16XJ00?uu516%SZjZ%dfLW@e<uO|8UX$eQ)hbv3vCla3kz#)i@*IrfL{aD zf4A~~2$X+S1-Sm-PVn{jy~tSGIViZ=7;66#4E~^;?Mf^1NXqE%IDD*BComUidGntL zqN&VIV9aD^p#sH`Lgi*1L?d-=V<TFz`rdOHJ&(k-k0mnQu+r;hmK(=!qmFN#odgld z*(QTX?8h_gJ4PNmHk_AUPcF2*z-@489n!jk=ruyOY9eQvatcZ{n*z6_DukIl^0rz6 zqx8QX2kR>I!oaXPp7ta~t~JdCtTg3rA^mK_>TQb2lu30r6QlebE4CK5ELCbM+*^pr zfLRmzQMYQ*1DQrmn<}?_0$l<1ML8l<Q{%d0)Zy6@v>KGCW~zsUW@4qWli@~#S*WUv z`|Cf|PQziIa*CYR;Ao(kMBPt7I)pcaigwg#CdkiC=iC~V3k8u@GmH8NaGinA^=s&i zXvijv9e8yQ2??_~>FndECji@5jPtH`;+%)yQd%t~)+u(jrzFOA?JMbskP%$Eh&yj6 zf~&W(8CW(mvj_<I2WL2zYH6)k$G?jR46KHqfY#DdFeK(5#|bR+@gf2p%(^kxtJ%qE zea^hjZ`3<56ifJovOoh)UMh2$CoK(21IoXbVhVQ4viGrT$28P+ph9;EAH~GQ65Wdi zwtc8ioHB)NPnEBugJlDiwm|^~vjDB4f_!MH#kFTt`j}6qkI9d42&u_(R3n-=7J>!G zz^D(?;==_6g<ZTA;zO7Ka>wE*MN7lT2(zey%2sDl9#}*!Lz$ue9hdqJhATin3xvX6 zmi`ht0}v0nRIk6wzKnq`TdL9^6+_hycu8-wdfzeGjJ-;{yf5sXfD$H6o<77HMcya- ztRSl^-%^B1LfA*%wb9^d&W?!C(MYJ>^zphUn$HX=gUm(WiaoltTY?Dal!jfq0;diB zFs!jI(PDFHZ`;PrQz1x^l-|V4Mx3L@1oZaUq>Z=T$vJ?YZsBWLvmX{>63r~#P)>Dd zjI!d5#DULGGm0__4xQqx6l;2UTjL^pGlyI(m5yjFWzAEJi^B}J8C)OWt2b{-<gaa5 zSxcWMJS#ZNf@L>QHM2>*W@0lQ_Jt|RF<q@yefnEMX)UnRJa37KG8L0oekRp!p1_t@ z9&!MCo`i;15$;*%3uxXCG@dEgH5xxSg+!Hl8dppPUu{X&=J@GMu5>>n09QCT2V|7b zO|S5mr1ljo_vAs5c|1q#AZu3}PlDkX_a^s`-*;4C(r4npm(1DYZqkG}ReaM7S%42Y zZ#chGK78;2uS>eIit#~0KKAj6<&q>6%11`waJs~PV7Y?gG+QL6dnBskrJuaQ(z`i( zo=Q$%hHG!7e1sa1p48IYn5|soo;b_cH9z%a{CbCZSFan5xOQ^}yo7mYzu@@|6K>w7 zC_N4nwvph9F%o*-Caw=N5>n#IR%4U1NE>^qGGd2qP&9Iewwm%0T&itrHn$*lW9F_M z1i!m7=|>}^gFTOk&eK;>8K-ZQv8BYy<QiLr(=ZxcOf<THiPb-lz?GLi5>h;TlTu@F z0llBKNwgSZ5eZu`&K+E!of@JYjlh9IpvalX-|d~&e|-Zw+x-if<S>(=@z>1HB7tv) zA%oZmEz-k4?{FcLM1;3V5PQMVLjKoZz9MGpeG;J8Yf11j?%f6E09`wnOero5+ULoS z?u`x0e#CqEt)jXennMhHsHlA(+du#6#s7+ztp7iF`3F=+D4746T;%>l&95c24*I39 z1b;c$x*HtyWHDKe!*a^nY1*mKUz9cZw4qD;#*eoPZ_kA_UjkU}nV{BrBE$V|-9Gl^ z?dAsU8?^_+Wlxtla3y-q;Hs@gAl`{;aY}qmjcfCGfGdT$6gwLwX9GiN<X8Yhr<sm% zuC<wu_ykJK1zmXFM9Hc=izQ5(kqQM<Kn)fbky=9?C9E{$76m0f-KddtS%RZcMw-|S zXC=0c<LUfY!_}Wnc+qxBA3tp{<tZtGKuhSmb0vcUO>g+<Sm97{TSh^EB&lsdNi)~L zanH7v)}*GqejQTLghydR;$h1-t?7otVoM0qH~`a@fB20susR%mb;Fs8{HNlTw+S&* ztUnk7d*D`r`GS}NwqSYXqetuJJ!=kZtGQULGQC;xA<HYpyB=p<GAuOpfu?r+kIFo& zZ@M(AU%x+7x0dXY^f&~{F7b>wMihE<7kd4lPOdzjs;v#{`b6fLgGlBfW2B-1DN|kZ zRCEmG6bFgS4Tg{@Ge_omC<+~O99%=DkYsi;XS&yvl<(}Mz0WDP@Ao}_ZNH!Q-D|yT z?Y-Ao>v^7&n5%{h;gL@#;u<izULDLbCOxWh!o6im!$=*&9&U_rXpr)&l*+M=ho8@r z&Rh<F7YeD~E3D-in!NFBCG8al4@xM5B1d%9>G-61Maa6(gc2*}ns|eRQB9G1j$%AK zZ{$*5YmcgD=!{`1l2iYpOVr0lBw-_kZGYII120e*gtc~by(0Dd#6GM{%+(yf4QnlY zGa&C5TwQfFw%n8wGv4-x66H#5r}TxIm-oqB0|XnCiXYM5xEh-(CyDGrD$~oUp(Fzw zG%nOjsQyJ^jFq*blkfG*IzCx+=9Yu#TKJ<oAEGzIc&v>K0#4r72v3cYofp&RTR<$Z zEigomy7)NR*vr@I@E-qyPO=EO^)@>QR$2QI$w9M3O@r{%nBy<tFX(W_-rWamJymB2 zoD9SraL;|GApH6W8@I|Kx{Xt(JKaX?i1&Vdzzi%ye*Bx#w?k1t3S)H(P`2ygVt1Z^ z41)t69D1<rOXU^Pah3Gtn#tY9@%q*;Rb|!lN}VX8_>XAXch_??6gQk0s!Z=*Xcv`! zfxzA;=X4n_=%MIvN!Iq!R4Zl<ec&HBPBKg?9Juaze+1Ppg3@H*tuY%$tye92PHlTG z4!CnxVHxjH(d(|<>oY&5qRXbj#GwrzEi~kyQ&Ky}mdPp#gTV~?y68;&(#$0<GZ_Wt z$ORS|b70UxQ#oYbA}-ajW{3P(0>{{x#OoeeRVw@X-L$%#Wlr)`Z%Lc!<dPIj+@Og3 z87*d2$~4EE%9PK~K*S|D)+}$-i5y9r^vqysKZ%|05<iKZ{t`3yXn{G_R(Lpsy`j%R zvQmk1kr7j2S!vSfu4~h%DJ(lyB{iINm|Bc^k;ROQ79;au-COEZ`nZ&a;D-IdQx8I0 zHe&tj+JsMqT*}_yMLu>=JeMUm6ktN~N#}tFDsbR&grty#nxcdi-)iK45YK!%`TP** z0bbF-{1G1RPB@k00kyYd2a*D&>l-;1sNa>(Sc(orb;4A63vRz|jxVy9(S6;{YtbB8 zt9k70;x(GmIqH#kx%|+XqDW*2eVgp-E@8$Ltsz&@X6dK!xR9U-`lqo5D~*p>L_Mxp zrX<9O`FCfr>gNm0U3)I3RhGEcPO~``$`m{dmxB#yavFyfIJV9Uz08{$g(u!lO0IW% zh{9AUd?EJSniahxh#FvF&hSwmH0rz1aDsHd+h;j=?QNHs<L0(eomw+BO>eQ$+DUIi zm%Of=zFchEXY?Wb&XsF%j#ddF8Q0CfC=9q)lwqgSJVcbZHeqhdqJC#TE^{vo5yS3C zXkfc34aV=WJ`G=n`RQzkVi1Eua6LZGCqZ;CUS!@(ceQzyJg2=FXOR_b$aTp??D_`B zDRFo!$C<UB#>&sVV_Jd7=58s>7_Xrv)+nT^o9y@BX-1ng#;`8b#_%PW!8+ISAG1xC z`b4K&g|Ma>owfP<=+RHJ@q-Um_*-pK-*MTPqgl-8Fb1${zt5H)PsZ5MHGz3d2BBjp zw0hmFt6jE_-{j<E=GlMz5eH>Z^(qW>tku4DIx@?Z5@qb{AJxgOFJ9lEt`>p2I;3?w zNf-CS<M_`e<`QFwUBbrPUBYrB?}=m9eVa9236?(`pqA7!ZjdTXu_eaP6eg9bK2R?` zN7^WzT3EIfEAl$Cj%T!JC<5nH)5dmSiqVB(ki&^aa*Ot~`G%y|VQd!VX-o%<vre4V zE8sJ$m+wQESMWo=1Op-qcV|nV0S|QOoT*x|u;A*tPe%<MhQ}8xtu0(Hv8A6~#!_Rk zp|Gj?K7r*t*`~Ko3Fp>8Q<-dwDhe|%$<sVAU3y&<b9-QA430v22c4GMI&e4I9+`2? zZ%Q#{nPuNrP0I!<b~~{{t-orPfAo%<mmKBxD=)vh<7SlGjdv}#V#c)O`QcVw#$4lH zEU8|S`paFYhn0WH<%c5zbUhe&MTN8mj)*ikrRJm!9uZ^9(-mK~?2XAjC^Y22+r$%H zWpasuQY_b*U(4a%tk#p}Q0KUiJe2B*URtBm6|CynOm=C=2+aq4T3Qs9@fEgfY5Jzh zC!8GR8s=Xx&QBgk4i>`ePps(XwsO1(v9Az$lJZVWz}tC=P5~9KXT)oh@Q-JK=_C2o z0>|8SuCno)Q|y0I=R5p0fw;kwFY23BGz}MTs2n}y#O=nfBo{^dL_ktHaQH3xR}Yo; z#%Jj0OS!MFnHuc#l|I5dZCNiY!!jtSOhwval5UP>+Slt)PN`dRcy7H!C#tBaSNlrZ zZ6?ZQmijk|XOf#TCa`Dy-_5ORaCYShGujw`NNPnca6T6d9HY<a<98}mk8ZMX6K-`^ z@u9e)cjcLYN$X<)xKVAfnnrj{?{tp|X9-ozf||thXG3$cD!xKq1%JD%-w>>5s+V&6 z8l2flTa>T=#?c_NW-Dji=+666s~C-fVa6~jb#)W3PpjGF^E3jK5i6x)v`(pF5y+^k zNh=Y&-1IZX)}gS(q2^9vr+e~3R)~4|Ovb$SmX_3yq(x+vy^oE)bZRh%JHVs-a(t>& zvzu(R^p2l9l72o@dVA~0MQxuUZD;qxFDQi-c(>21pYL3ki8RX;Ms^gJs7+s!>O7J* z8IWse9$hu@ek%8*W1)r2my2^LTqQj>{ly}yXQxr|+H9oOCE?TbZR+s?+%n?j2W8rH zXuA*i*BEz|Od4T0d1%Q=GifXPKSyUfc*+LQehwx(e5n2C%VL!b4#jGJ?oAq6tZAt0 z2o^q^bRBu<{o!~fbAHn@wnaVHVd_+0>)}BU_8TEQ_Ni9nYSblJDUHMhzKi89e^OZ2 zMqHKY@e${I^GBCzg0%1Ejnt$FmMU*+sWubxR1@`7@2>FaFy<abzOMA<2v4L;Kk47k z?UG%{`umu0URHeFWHg%Q2sHP@#%PZA*0C*)=(fD#iHp|9%#B(R)>fO2hNtd36~C+- zF_E-bRAtEK2_B0|Pk!^-xGQm@&BOooWsQG`Ejvhtx(#KN(TEoKDk8uCAk~K@OUcWu zRA!Nw_tq{~<iatV*68Dd<81|F*~ea%B*rUhcud+m$S0E+JHDfR$Cy6RsF~8(Nk8bx zp!i6>PG|f%qXC=uUny^9a~Q%9ZpM}Rc?#Yk2phKe#!2o1XL=L<<rVaaU^x=oo0S)J z9MQb(N1gWv8$W5hg<ffqw;eA!UtG>n+Q3A=8icqk+r#?CQZuf8%_LXuuEgRN$x#Z{ z@RRcbU0O=Evmp!mb$#vqS>dehNwZnf<ZTt1_O9!U&hv>X0!Hun2G@uk&*d>E&%`ZX zjT?2jplm>)<$|rl{%1n1J*7gM>FYP^oilx(KI-A<cBS;d+I`<2j6^%@lwGq(+&bqQ z0JBJZ{BVB+tRo3Sp~j7XRU`-n+~to@h;by4ft={qYbn4j0Yo$Ia@X#0?-+oh24Ws} zxf27me+0V={8)jy5CQv2AlPw~M#5;Q9VE2(Kqjci1>jl)q7c_X!c6!cMufSbei8tS z5kvy6i@(aXvts<;>*+y<%k$lfV()tHjLh!clYhkQ5C!*+1KcS<G~vbV&<62G0cw8$ z(0D<t{1gjrrV&;RY99bBu|ZTZ?Dlq-xcE!s1j2++`8)pPz~JULXw&ht2LW^-2zA-` zMb%~A&9oz)$1e<2>5V@TJn-TO#q`jg0QZLqWC2|`h&dclbEhXz)-*&V4ow9pYM~lY zKv4@~3)e!tR8V~_Gz}mqg~}uG(<tLgjh6;0FNGGCP#y_rDM5tc`U$4_uB-&B3j{+o zWq`I4MAgs1zf)R5I|#_qpqi#bU}oIa2<}gUmO+w(Px9_-8PGvNMIit`8ARpbf6E4{ zqM+>q#3WEsJs>6l(TrnI?A<<Ih_nQn6QJWmso?m}@qi#SCvLia6RU!K-RmoW{{eCp z-XRr3X93}s6F?&ef%+b?Ln`0<jRhP(fT)E;eJ7dk9g6>4ek2SfAf1CV5NJ4*qm4hT zydoff`e*Vw{!4JL;sM-l5Okto#^1fb@js*|2_ko2;k)-GWZ(eYZxD!IME^Kw0m}h! z!J*VlfC~;HaqlGm$_$6jP@v#Y3K2l@1R*4eUml>7-<t_k8%p?uCI$KqJ>de_M<B4W zzfJo4X8?b0#+@NJ!}_;}0spI2PZ;{s!P(xk9RGv*^*5P*F}^_cp_|6?V3lxJr_U1f zilAxV6YhDi1|1UMPSE{2U=tdIoyz}lr{50}u=NMRR28zrN7y##@*BUbzR)nGJvRpb WR44hiIu<4pCMu>NA`;OC?)+ckehWkZ From 51cb238d81b39ef5f6076d46588da16f14a21a2c Mon Sep 17 00:00:00 2001 From: Christoph Langer <clanger@openjdk.org> Date: Fri, 8 Dec 2017 09:48:10 +0100 Subject: [PATCH 146/165] 8193183: Fix format string in libdt_shmem/shmemBase.c Reviewed-by: cjplummer, sspitsyn --- src/jdk.jdi/share/native/libdt_shmem/shmemBase.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c b/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c index dc3e6956f6b..fbe15b465f7 100644 --- a/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c +++ b/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2017, 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 @@ -31,6 +31,10 @@ #include "shmemBase.h" #include "jdwpTransport.h" /* for Packet, TransportCallback */ +#if defined(_WIN32) + #define PRId64 "I64d" +#endif + #define MIN(x,y) ((x)<(y)?(x):(y)) /* @@ -537,7 +541,7 @@ openConnection(SharedMemoryTransport *transport, jlong otherPID, return SYS_NOMEM; } - sprintf(connection->name, "%s.%ld", transport->name, sysProcessGetID()); + sprintf(connection->name, "%s.%" PRId64, transport->name, sysProcessGetID()); error = sysSharedMemOpen(connection->name, &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { @@ -601,7 +605,7 @@ createConnection(SharedMemoryTransport *transport, jlong otherPID, return SYS_NOMEM; } - sprintf(connection->name, "%s.%ld", transport->name, otherPID); + sprintf(connection->name, "%s.%" PRId64, transport->name, otherPID); error = sysSharedMemCreate(connection->name, sizeof(SharedMemory), &connection->sharedMemory, &connection->shared); if (error != SYS_OK) { From cebb2a31b4c48ad16edb2f6a9328725a1d6d0980 Mon Sep 17 00:00:00 2001 From: Attila Szegedi <attila@openjdk.org> Date: Fri, 8 Dec 2017 11:48:38 +0100 Subject: [PATCH 147/165] 8192970: Element getters/setters with fixed key fail to link properly Reviewed-by: hannesw, sundar --- .../jdk/dynalink/beans/BeanLinker.java | 46 ++++----- .../dynalink/beans/test/BeanLinkerTest.java | 95 ++++++++++++++++++- 2 files changed, 114 insertions(+), 27 deletions(-) diff --git a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java index 7f2aa301be9..b2389d61066 100644 --- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java +++ b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java @@ -205,9 +205,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL return nextComponent; } - return guardComponentWithRangeCheck(gicact, linkerServices, - callSiteDescriptor, nextComponent, new Binder(linkerServices, callSiteType, typedName), - isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2); + return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, + new Binder(linkerServices, callSiteType, typedName), isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2); } private static class GuardedInvocationComponentAndCollectionType { @@ -276,21 +275,19 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL } private static GuardedInvocationComponent guardComponentWithRangeCheck( - final GuardedInvocationComponentAndCollectionType gicact, final LinkerServices linkerServices, - final CallSiteDescriptor callSiteDescriptor, final GuardedInvocationComponent nextComponent, final Binder binder, - final MethodHandle noOp) { - final MethodType callSiteType = callSiteDescriptor.getMethodType(); + final GuardedInvocationComponentAndCollectionType gicact, final MethodType callSiteType, + final GuardedInvocationComponent nextComponent, final Binder binder, final MethodHandle noOp) { final MethodHandle checkGuard; switch(gicact.collectionType) { case LIST: - checkGuard = convertArgToNumber(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor); + checkGuard = binder.convertArgToNumber(RANGE_CHECK_LIST); break; case MAP: - checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP); + checkGuard = binder.linkerServices.filterInternalObjects(CONTAINS_MAP); break; case ARRAY: - checkGuard = convertArgToNumber(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); + checkGuard = binder.convertArgToNumber(RANGE_CHECK_ARRAY); break; default: throw new AssertionError(); @@ -301,7 +298,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL if (nextComponent != null) { finalNextComponent = nextComponent; } else { - finalNextComponent = createGuardedInvocationComponentAsType(noOp, callSiteType, linkerServices); + finalNextComponent = createGuardedInvocationComponentAsType(noOp, callSiteType, binder.linkerServices); } final GuardedInvocationComponent gic = gicact.gic; @@ -377,18 +374,6 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL return intIndex; } - private static MethodHandle convertArgToNumber(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) { - final Class<?> sourceType = desc.getMethodType().parameterType(1); - if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) { - return mh; - } else if(ls.canConvert(sourceType, Number.class)) { - final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class); - return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType( - mh.type().parameterType(1)))); - } - return mh; - } - /** * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a * fixed key first. @@ -412,6 +397,18 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL return bindToFixedKey(Guards.asType(handle, methodType)); } + /*private*/ MethodHandle convertArgToNumber(final MethodHandle mh) { + final Class<?> sourceType = methodType.parameterType(1); + if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) { + return mh; + } else if(linkerServices.canConvert(sourceType, Number.class)) { + final MethodHandle converter = linkerServices.getTypeConverter(sourceType, Number.class); + return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType( + mh.type().parameterType(1)))); + } + return mh; + } + private MethodHandle bindToFixedKey(final MethodHandle handle) { return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey); } @@ -506,8 +503,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL return gic.replaceInvocation(binder.bind(invocation)); } - return guardComponentWithRangeCheck(gicact, linkerServices, callSiteDescriptor, - nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3); + return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3); } private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size", diff --git a/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java b/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java index 44c009fdc0d..00bf14ee92f 100644 --- a/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java +++ b/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java @@ -243,13 +243,56 @@ public class BeanLinkerTest { Assert.assertEquals((int) cs.getTarget().invoke(list, 1), (int) list.get(1)); Assert.assertEquals((int) cs.getTarget().invoke(list, 2), (int) list.get(2)); try { - final int x = (int) cs.getTarget().invoke(list, -1); + cs.getTarget().invoke(list, -1); throw new RuntimeException("expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException ex) { } try { - final int x = (int) cs.getTarget().invoke(list, list.size()); + cs.getTarget().invoke(list, list.size()); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + private Object invokeWithFixedKey(boolean publicLookup, Operation op, Object name, MethodType mt, Object... args) throws Throwable { + return createCallSite(publicLookup, op.named(name), mt).getTarget().invokeWithArguments(args); + } + + @Test(dataProvider = "flags") + public void getElementWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(int.class, Object.class); + + final int[] arr = {23, 42}; + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 0, mt, arr), 23); + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 1, mt, arr), 42); + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, arr); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, arr.length, mt, arr); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List<Integer> list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + for (int i = 0; i < 3; ++i) { + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, i, mt, list), (int) list.get(i)); + } + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, list); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, list.size(), mt, list); throw new RuntimeException("expected IndexOutOfBoundsException"); } catch (final IndexOutOfBoundsException ex) { } @@ -286,7 +329,9 @@ public class BeanLinkerTest { cs.getTarget().invoke(list, 0, -list.get(0)); Assert.assertEquals((int) list.get(0), -23); cs.getTarget().invoke(list, 1, -430); + Assert.assertEquals((int) list.get(1), -430); cs.getTarget().invoke(list, 2, 4354); + Assert.assertEquals((int) list.get(2), 4354); try { cs.getTarget().invoke(list, -1, 343); throw new RuntimeException("expected IndexOutOfBoundsException"); @@ -300,6 +345,52 @@ public class BeanLinkerTest { } } + @Test(dataProvider = "flags") + public void setElementWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class, int.class); + + final int[] arr = {23, 42}; + invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, arr, 0); + Assert.assertEquals(arr[0], 0); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, arr, -5); + Assert.assertEquals(arr[1], -5); + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, arr, 12); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, arr.length, mt, arr, 20); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List<Integer> list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + + invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, list, -list.get(0)); + Assert.assertEquals((int) list.get(0), -23); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, list, -430); + Assert.assertEquals((int) list.get(1), -430); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 2, mt, list, 4354); + Assert.assertEquals((int) list.get(2), 4354); + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, list, 343); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, list.size(), mt, list, 43543); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + @Test(dataProvider = "flags") public void newObjectTest(final boolean publicLookup) { final MethodType mt = MethodType.methodType(Object.class, Object.class); From 6534e2ad5406995d79d6c925dbab86b7645b66b6 Mon Sep 17 00:00:00 2001 From: Priya Lakshmi Muthuswamy <priya.lakshmi.muthuswamy@oracle.com> Date: Fri, 8 Dec 2017 12:02:30 +0100 Subject: [PATCH 148/165] 8193137: Nashorn crashes when given an empty script file Reviewed-by: hannesw, sundar --- .../classes/jdk/nashorn/tools/Shell.java | 2 +- test/nashorn/script/nosecurity/JDK-8193137.js | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/nashorn/script/nosecurity/JDK-8193137.js diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java index b4f3a0bdf32..9722ac133d4 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java @@ -295,7 +295,7 @@ public class Shell implements PartialParser { } catch (final IOException ioe) { // ignore } - if (l.startsWith("#!")) { + if (l != null && l.startsWith("#!")) { shebangFilePos = i; } // We're only checking the first non-option argument. If it's not a shebang file, we're in normal diff --git a/test/nashorn/script/nosecurity/JDK-8193137.js b/test/nashorn/script/nosecurity/JDK-8193137.js new file mode 100644 index 00000000000..b1fef72ce77 --- /dev/null +++ b/test/nashorn/script/nosecurity/JDK-8193137.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 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. 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. + */ + +/** + * JDK-8193137 : Nashorn crashes when given an empty script file. + * + * @test + * @option -scripting + * @run + */ + +var System = java.lang.System; +var File = java.io.File; +var javahome = System.getProperty("java.home"); +var nashornJar = new File(System.getProperty("nashorn.jar")); +if (! nashornJar.isAbsolute()) { + nashornJar = new File(".", nashornJar); +} + +// we want to use nashorn.jar passed and not the one that comes with JRE +var jjsCmd = javahome + "/../bin/jjs"; +jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); +if (! new File(jjsCmd).isFile()) { + jjsCmd = javahome + "/bin/jjs"; + jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); +} +jjsCmd += " -J--patch-module=jdk.scripting.nashorn=" + nashornJar; + +$ENV.PWD=System.getProperty("user.dir") + +var emptyFile = new File($ENV.PWD+File.separator+"empty.js"); +emptyFile.createNewFile(); +emptyFile.deleteOnExit(); + +$EXEC(jjsCmd + " empty.js"); +if($ERR != "") + fail("jjs fails with empty script file"); From a87551990a5f188ed5af446c956fcf760aa9061f Mon Sep 17 00:00:00 2001 From: Daniel Fuchs <dfuchs@openjdk.org> Date: Fri, 8 Dec 2017 11:50:39 +0000 Subject: [PATCH 149/165] 8191033: Regression in logging.properties: specifying .handlers= for root logger (instead of handlers=) no longer works The behavior observed for Java 8 is restored Reviewed-by: martin, mchung --- .../classes/java/util/logging/LogManager.java | 18 +- .../RootLoggerHandlers.java | 180 ++++++++++++++++++ .../rootLoggerHandlers/custom/DotHandler.java | 50 +++++ .../rootLoggerHandlers/custom/Handler.java | 49 +++++ .../rootLoggerHandlers/logging.properties | 18 ++ 5 files changed, 310 insertions(+), 5 deletions(-) create mode 100644 test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java create mode 100644 test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java create mode 100644 test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java create mode 100644 test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java index 31c5e74c624..e864f5ebabf 100644 --- a/src/java.logging/share/classes/java/util/logging/LogManager.java +++ b/src/java.logging/share/classes/java/util/logging/LogManager.java @@ -388,15 +388,23 @@ public class LogManager { // create root logger before reading primordial // configuration - to ensure that it will be added // before the global logger, and not after. - owner.rootLogger = owner.new RootLogger(); + final Logger root = owner.rootLogger = owner.new RootLogger(); // Read configuration. owner.readPrimordialConfiguration(); // Create and retain Logger for the root of the namespace. - owner.addLogger(owner.rootLogger); - if (!owner.rootLogger.isLevelInitialized()) { - owner.rootLogger.setLevel(defaultLevel); + owner.addLogger(root); + + // For backward compatibility: add any handlers configured using + // ".handlers" + owner.createLoggerHandlers("", ".handlers") + .stream() + .forEach(root::addHandler); + + // Initialize level if not yet initialized + if (!root.isLevelInitialized()) { + root.setLevel(defaultLevel); } // Adding the global Logger. @@ -1708,7 +1716,7 @@ public class LogManager { * @param k a property key in the configuration * @param previous the old configuration * @param next the new configuration (modified by this function) - * @param remappingFunction the mapping function. + * @param mappingFunction the mapping function. */ static void merge(String k, Properties previous, Properties next, BiFunction<String, String, String> mappingFunction) { diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java new file mode 100644 index 00000000000..8b7b841f43d --- /dev/null +++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017, 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. + */ +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @test + * @bug 8191033 + * @build custom.DotHandler custom.Handler + * @run main/othervm RootLoggerHandlers + * @author danielfuchs + */ +public class RootLoggerHandlers { + + public static final Path SRC_DIR = + Paths.get(System.getProperty("test.src", "src")); + public static final Path USER_DIR = + Paths.get(System.getProperty("user.dir", ".")); + public static final Path CONFIG_FILE = Paths.get("logging.properties"); + + // Uncomment this to run the test on Java 8. Java 8 does not have + // List.of(...) + // static final class List { + // static <T> java.util.List<T> of(T... items) { + // return Collections.unmodifiableList(Arrays.asList(items)); + // } + // } + + public static void main(String[] args) throws IOException { + Path initialProps = SRC_DIR.resolve(CONFIG_FILE); + Path loggingProps = USER_DIR.resolve(CONFIG_FILE); + System.setProperty("java.util.logging.config.file", loggingProps.toString()); + Files.copy(initialProps, loggingProps); + System.out.println("Root level is: " + Logger.getLogger("").getLevel()); + if (Logger.getLogger("").getLevel() != Level.INFO) { + throw new RuntimeException("Expected root level INFO, got: " + + Logger.getLogger("").getLevel()); + } + // Verify that we have two handlers. One was configured with + // handlers=custom.Handler, the other with + // .handlers=custom.DotHandler + // Verify that exactly one of the two handlers is a custom.Handler + // Verify that exactly one of the two handlers is a custom.DotHandler + // Verify that the two handlers has an id of '1' + checkHandlers(Logger.getLogger("").getHandlers(), + 1L, + custom.Handler.class, + custom.DotHandler.class); + + // The log message "hi" should appear twice on the console. + // We don't check that. This is just for log analysis in case + // of test failure. + Logger.getAnonymousLogger().info("hi"); + + // Change the root logger level to FINE in the properties file + // and reload the configuration. + Files.write(loggingProps, + Files.lines(initialProps) + .map((s) -> s.replace("INFO", "FINE")) + .collect(Collectors.toList())); + LogManager.getLogManager().readConfiguration(); + + System.out.println("Root level is: " + Logger.getLogger("").getLevel()); + if (Logger.getLogger("").getLevel() != Level.FINE) { + throw new RuntimeException("Expected root level FINE, got: " + + Logger.getLogger("").getLevel()); + } + + // Verify that we have now only one handler, configured with + // handlers=custom.Handler, and that the other configured with + // .handlers=custom.DotHandler was ignored. + // Verify that the handler is a custom.Handler + // Verify that the handler has an id of '2' + checkHandlers(Logger.getLogger("").getHandlers(), + 2L, + custom.Handler.class); + + // The log message "there" should appear only once on the console. + // We don't check that. This is just for log analysis in case + // of test failure. + Logger.getAnonymousLogger().info("there!"); + + // Change the root logger level to FINER in the properties file + // and reload the configuration. + Files.write(loggingProps, + Files.lines(initialProps) + .map((s) -> s.replace("INFO", "FINER")) + .collect(Collectors.toList())); + LogManager.getLogManager().readConfiguration(); + + System.out.println("Root level is: " + Logger.getLogger("").getLevel()); + if (Logger.getLogger("").getLevel() != Level.FINER) { + throw new RuntimeException("Expected root level FINER, got: " + + Logger.getLogger("").getLevel()); + } + + // Verify that we have only one handler, configured with + // handlers=custom.Handler, and that the other configured with + // .handlers=custom.DotHandler was ignored. + // Verify that the handler is a custom.Handler + // Verify that the handler has an id of '3' + checkHandlers(Logger.getLogger("").getHandlers(), + 3L, + custom.Handler.class); + + // The log message "done" should appear only once on the console. + // We don't check that. This is just for log analysis in case + // of test failure. + Logger.getAnonymousLogger().info("done!"); + } + + static void checkHandlers(Handler[] handlers, Long expectedID, Class<?>... clz) { + // Verify that we have the expected number of handlers. + if (Stream.of(handlers).count() != clz.length) { + throw new RuntimeException("Expected " + clz.length + " handlers, got: " + + List.of(Logger.getLogger("").getHandlers())); + } + for (Class<?> cl : clz) { + // Verify that the handlers are of the expected class. + // For each class, we should have exactly one handler + // of that class. + if (Stream.of(handlers) + .map(Object::getClass) + .filter(cl::equals) + .count() != 1) { + throw new RuntimeException("Expected one " + cl +", got: " + + List.of(Logger.getLogger("").getHandlers())); + } + } + // Verify that all handlers have the expected ID + if (Stream.of(Logger.getLogger("").getHandlers()) + .map(RootLoggerHandlers::getId) + .filter(expectedID::equals) + .count() != clz.length) { + throw new RuntimeException("Expected ids to be " + expectedID + ", got: " + + List.of(Logger.getLogger("").getHandlers())); + } + } + + static long getId(Handler h) { + if (h instanceof custom.Handler) { + return ((custom.Handler)h).id; + } + if (h instanceof custom.DotHandler) { + return ((custom.DotHandler)h).id; + } + return -1; + } +} diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java new file mode 100644 index 00000000000..21734f190c4 --- /dev/null +++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, 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. + */ +package custom; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * + * @author danielfuchs + */ +public class DotHandler extends java.util.logging.ConsoleHandler { + + public static final AtomicLong IDS = new AtomicLong(); + public final long id = IDS.incrementAndGet(); + public DotHandler() { + System.out.println("DotHandler(" + id + ") created"); + //new Exception("DotHandler").printStackTrace(); + } + + @Override + public void close() { + System.out.println("DotHandler(" + id + ") closed"); + super.close(); + } + + @Override + public String toString() { + return this.getClass().getName() + '(' + id + ')'; + } +} diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java new file mode 100644 index 00000000000..5f845cfb093 --- /dev/null +++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, 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. + */ +package custom; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * + * @author danielfuchs + */ +public class Handler extends java.util.logging.ConsoleHandler { + + static final AtomicLong IDS = new AtomicLong(); + public final long id = IDS.incrementAndGet(); + public Handler() { + System.out.println("Handler(" + id + ") created"); + } + + @Override + public void close() { + System.out.println("Handler(" + id + ") closed"); + super.close(); + } + + @Override + public String toString() { + return this.getClass().getName() + '(' + id + ')'; + } +} diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties new file mode 100644 index 00000000000..a432c19a810 --- /dev/null +++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties @@ -0,0 +1,18 @@ +############################################################ +# Global properties +############################################################ + +# "handlers" specifies a comma separated list of log Handler +# classes. These handlers will be installed during VM startup. +#handlers= java.util.logging.ConsoleHandler +handlers= custom.Handler +.handlers= custom.DotHandler + +# Default global logging level. +.level= INFO + +# Other configuration +custom.Handler.level=ALL +custom.DotHandler.level=ALL +java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n + From 93aa3ae4d70890ff68b92946944131c6ddbe8c4a Mon Sep 17 00:00:00 2001 From: Andrej Golovnin <andrej.golovnin@gmail.com> Date: Fri, 8 Dec 2017 14:28:51 +0000 Subject: [PATCH 150/165] 8193256: Configuration and ModuleLayer findModule cleanup Reviewed-by: redestad, alanb --- src/java.base/share/classes/java/lang/ModuleLayer.java | 5 ++--- .../share/classes/java/lang/module/Configuration.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index 41ce156b372..3dc809632c7 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -845,9 +845,8 @@ public final class ModuleLayer { return layers() .skip(1) // skip this layer - .map(l -> l.nameToModule) - .filter(map -> map.containsKey(name)) - .map(map -> map.get(name)) + .map(l -> l.nameToModule.get(name)) + .filter(Objects::nonNull) .findAny(); } diff --git a/src/java.base/share/classes/java/lang/module/Configuration.java b/src/java.base/share/classes/java/lang/module/Configuration.java index 042a1362dd9..988d5c7a40a 100644 --- a/src/java.base/share/classes/java/lang/module/Configuration.java +++ b/src/java.base/share/classes/java/lang/module/Configuration.java @@ -543,9 +543,8 @@ public final class Configuration { if (!parents.isEmpty()) { return configurations() .skip(1) // skip this configuration - .map(cf -> cf.nameToModule) - .filter(map -> map.containsKey(name)) - .map(map -> map.get(name)) + .map(cf -> cf.nameToModule.get(name)) + .filter(Objects::nonNull) .findFirst(); } From b93586c51e7638800ac3066703c35aa00235e3ce Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan <sundar@openjdk.org> Date: Fri, 8 Dec 2017 20:46:40 +0530 Subject: [PATCH 151/165] 8192986: Inconsistent handling of exploded modules in jlink Reviewed-by: redestad, jlaskey --- .../jlink/builder/DefaultImageBuilder.java | 20 ++-- .../jdk/tools/jlink/internal/DirArchive.java | 8 +- .../jdk/tools/jlink/internal/JlinkTask.java | 19 +++- .../tools/jlink/resources/jlink.properties | 2 + .../tools/jlink/ExplodedModuleNameTest.java | 96 +++++++++++++++++++ test/jdk/tools/lib/tests/Helper.java | 2 +- test/jdk/tools/lib/tests/JImageGenerator.java | 11 +++ 7 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 test/jdk/tools/jlink/ExplodedModuleNameTest.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java index b1f0ca1c1b4..aa3c66d44f2 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java @@ -267,15 +267,17 @@ public final class DefaultImageBuilder implements ImageBuilder { assert !mainClassName.isEmpty(); } - String path = "/" + module + "/module-info.class"; - Optional<ResourcePoolEntry> res = imageContent.findEntry(path); - if (!res.isPresent()) { - throw new IOException("module-info.class not found for " + module + " module"); - } - ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes()); - Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass(); - if (mainClassName == null && mainClass.isPresent()) { - mainClassName = mainClass.get(); + if (mainClassName == null) { + String path = "/" + module + "/module-info.class"; + Optional<ResourcePoolEntry> res = imageContent.findEntry(path); + if (!res.isPresent()) { + throw new IOException("module-info.class not found for " + module + " module"); + } + ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes()); + Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass(); + if (mainClass.isPresent()) { + mainClassName = mainClass.get(); + } } if (mainClassName != null) { diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java index 84fee1b4710..59205ac02ae 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java @@ -85,17 +85,17 @@ public class DirArchive implements Archive { private static final Consumer<String> noopConsumer = (String t) -> { }; - public DirArchive(Path dirPath) { - this(dirPath, noopConsumer); + public DirArchive(Path dirPath, String moduleName) { + this(dirPath, moduleName, noopConsumer); } - public DirArchive(Path dirPath, Consumer<String> log) { + public DirArchive(Path dirPath, String moduleName, Consumer<String> log) { Objects.requireNonNull(dirPath); if (!Files.isDirectory(dirPath)) { throw new IllegalArgumentException(dirPath + " is not a directory"); } chop = dirPath.toString().length() + 1; - this.moduleName = Objects.requireNonNull(dirPath.getFileName()).toString(); + this.moduleName = Objects.requireNonNull(moduleName); this.dirPath = dirPath; this.log = log; } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 7db0934a54a..5904907cdab 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -24,6 +24,7 @@ */ package jdk.tools.jlink.internal; +import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -824,13 +825,29 @@ public class JlinkTask { return modularJarArchive; } else if (Files.isDirectory(path)) { - return new DirArchive(path); + Path modInfoPath = path.resolve("module-info.class"); + if (Files.isRegularFile(modInfoPath)) { + return new DirArchive(path, findModuleName(modInfoPath)); + } else { + throw new IllegalArgumentException( + taskHelper.getMessage("err.not.a.module.directory", path)); + } } else { throw new IllegalArgumentException( taskHelper.getMessage("err.not.modular.format", module, path)); } } + private static String findModuleName(Path modInfoPath) { + try (BufferedInputStream bis = new BufferedInputStream( + Files.newInputStream(modInfoPath))) { + return ModuleDescriptor.read(bis).name(); + } catch (IOException exp) { + throw new IllegalArgumentException(taskHelper.getMessage( + "err.cannot.read.module.info", modInfoPath), exp); + } + } + @Override public ExecutableImage retrieve(ImagePluginStack stack) throws IOException { ExecutableImage image = ImageFileCreator.create(archives, order, stack); diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties index ec8121a15fe..63caa691f52 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties @@ -135,6 +135,8 @@ err.orphan.arguments=invalid argument: {0} err.config.defaults=property {0} is missing from configuration err.config.defaults.value=wrong value in defaults property: {0} err.bom.generation=bom file generation failed: {0} +err.not.a.module.directory=directory {0} does not contain module-info.class file under it +err.cannot.read.module.info=cannot read module descriptor from {0} err.not.modular.format=selected module {0} ({1}) not in jmod or modular JAR format err.signing=signed modular JAR {0} is currently not supported,\ \ use --ignore-signing-information to suppress error diff --git a/test/jdk/tools/jlink/ExplodedModuleNameTest.java b/test/jdk/tools/jlink/ExplodedModuleNameTest.java new file mode 100644 index 00000000000..bcbc1ffd86c --- /dev/null +++ b/test/jdk/tools/jlink/ExplodedModuleNameTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017, 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. + */ + +import java.io.IOException; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Files; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.util.spi.ToolProvider; + +import tests.Helper; +import tests.JImageGenerator; +import tests.Result; + +/* + * @test + * @bug 8192986 + * @summary Inconsistent handling of exploded modules in jlink + * @library ../lib + * @modules java.base/jdk.internal.jimage + * jdk.jdeps/com.sun.tools.classfile + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jmod + * jdk.jlink/jdk.tools.jimage + * jdk.compiler + * @build tests.* + * @run main ExplodedModuleNameTest + */ +public class ExplodedModuleNameTest { + static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink") + .orElseThrow(() -> + new RuntimeException("jlink tool not found") + ); + + public static void main(String[] args) throws Exception { + Helper helper = Helper.newHelper(); + if (helper == null) { + System.err.println("Test not run"); + return; + } + + // generate a new exploded module + String modName = "mod8192986"; + Path modDir = helper.generateDefaultExplodedModule(modName).getFile(); + // rename the module containing directory + Path renamedModDir = modDir.resolveSibling("modified_mod8192986"); + // copy the content from original directory to modified name directory + copyDir(modDir, renamedModDir); + + Path outputDir = helper.createNewImageDir("image8192986"); + JImageGenerator.getJLinkTask() + .modulePath(renamedModDir.toAbsolutePath().toString()) + .output(outputDir) + .addMods(modName) + .launcher(modName + "=" + modName + "/" + modName +".Main") + .call().assertSuccess(); + } + + private static void copyDir(Path srcDir, Path destDir) throws IOException { + Files.walkFileTree(srcDir, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path target = destDir.resolve(srcDir.relativize(dir)); + Files.createDirectory(target); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.copy(file, destDir.resolve(srcDir.relativize(file))); + return FileVisitResult.CONTINUE; + } + }); + } +} diff --git a/test/jdk/tools/lib/tests/Helper.java b/test/jdk/tools/lib/tests/Helper.java index 022370440c6..006807b1c85 100644 --- a/test/jdk/tools/lib/tests/Helper.java +++ b/test/jdk/tools/lib/tests/Helper.java @@ -219,7 +219,7 @@ public class Helper { generateModuleCompiledClasses(explodedmodssrc, explodedmodsclasses, moduleName, classNames, dependencies); - Path dir = explodedmods.resolve(moduleName); + Path dir = explodedmods.resolve("classes").resolve(moduleName); return new Result(0, "", dir); } diff --git a/test/jdk/tools/lib/tests/JImageGenerator.java b/test/jdk/tools/lib/tests/JImageGenerator.java index 59e7d37cbce..763cd355e3a 100644 --- a/test/jdk/tools/lib/tests/JImageGenerator.java +++ b/test/jdk/tools/lib/tests/JImageGenerator.java @@ -110,6 +110,7 @@ public class JImageGenerator { private static final String ADD_MODULES_OPTION = "--add-modules"; private static final String LIMIT_MODULES_OPTION = "--limit-modules"; private static final String PLUGIN_MODULE_PATH = "--plugin-module-path"; + private static final String LAUNCHER = "--launcher"; private static final String CMDS_OPTION = "--cmds"; private static final String CONFIG_OPTION = "--config"; @@ -579,12 +580,18 @@ public class JImageGenerator { private String repeatedLimitMods; private Path output; private Path existing; + private String launcher; // optional public JLinkTask modulePath(String modulePath) { this.modulePath = modulePath; return this; } + public JLinkTask launcher(String cmd) { + launcher = Objects.requireNonNull(cmd); + return this; + } + public JLinkTask repeatedModulePath(String modulePath) { this.repeatedModulePath = modulePath; return this; @@ -682,6 +689,10 @@ public class JImageGenerator { options.add(PLUGIN_MODULE_PATH); options.add(toPath(pluginModulePath)); } + if (launcher != null && !launcher.isEmpty()) { + options.add(LAUNCHER); + options.add(launcher); + } options.addAll(this.options); return options.toArray(new String[options.size()]); } From f29e21abb19380d9aceea04b066f83c7635ffcd8 Mon Sep 17 00:00:00 2001 From: Roger Riggs <rriggs@openjdk.org> Date: Fri, 1 Dec 2017 16:40:08 -0500 Subject: [PATCH 152/165] 8080225: FileInput/OutputStream/FileChannel cleanup should be improved Reviewed-by: mchung, plevart, bpb --- make/mapfiles/libjava/mapfile-vers | 2 +- .../classes/java/io/FileInputStream.java | 107 +++++++- .../classes/java/io/FileOutputStream.java | 131 ++++++++-- .../classes/java/io/RandomAccessFile.java | 1 + .../classes/java/net/SocketInputStream.java | 2 +- .../classes/java/net/SocketOutputStream.java | 2 +- .../misc/JavaIOFileDescriptorAccess.java | 4 +- .../classes/sun/nio/ch/FileChannelImpl.java | 14 +- .../unix/classes/java/io/FileDescriptor.java | 109 +++++++- .../unix/native/libjava/FileDescriptor_md.c | 11 +- .../unix/native/libjava/io_util_md.c | 5 + .../classes/java/io/FileDescriptor.java | 117 ++++++++- .../sun/nio/ch/FileDispatcherImpl.java | 1 + .../WindowsAsynchronousFileChannelImpl.java | 4 +- .../sun/nio/fs/WindowsChannelFactory.java | 4 +- .../native/libjava/FileDescriptor_md.c | 13 +- .../ch/WindowsAsynchronousFileChannelImpl.c | 10 - .../FileInputStream/FinalizeShdCallClose.java | 90 ------- .../UnreferencedFISClosesFd.java | 247 ++++++++++++++++++ .../FinalizeShdCallClose.java | 90 ------- .../UnreferencedFOSClosesFd.java | 236 +++++++++++++++++ .../UnreferencedRAFClosesFd.java | 96 +++++++ .../FileInputStreamPoolTest.java | 69 ++++- 23 files changed, 1104 insertions(+), 261 deletions(-) delete mode 100644 test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java create mode 100644 test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java delete mode 100644 test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java create mode 100644 test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java create mode 100644 test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java diff --git a/make/mapfiles/libjava/mapfile-vers b/make/mapfiles/libjava/mapfile-vers index a2f7303f06e..ce5100c539e 100644 --- a/make/mapfiles/libjava/mapfile-vers +++ b/make/mapfiles/libjava/mapfile-vers @@ -74,7 +74,7 @@ SUNWprivate_1.1 { JNU_ThrowStringIndexOutOfBoundsException; JNU_ToString; - Java_java_io_FileDescriptor_close; + Java_java_io_FileDescriptor_close0; Java_java_io_FileDescriptor_initIDs; Java_java_io_FileDescriptor_sync; Java_java_io_FileDescriptor_getAppend; diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java index 5dfe71907fe..72a12ff76de 100644 --- a/src/java.base/share/classes/java/io/FileInputStream.java +++ b/src/java.base/share/classes/java/io/FileInputStream.java @@ -25,6 +25,7 @@ package java.io; +import java.lang.reflect.Method; import java.nio.channels.FileChannel; import sun.nio.ch.FileChannelImpl; @@ -37,6 +38,22 @@ import sun.nio.ch.FileChannelImpl; * <p><code>FileInputStream</code> is meant for reading streams of raw bytes * such as image data. For reading streams of characters, consider using * <code>FileReader</code>. + * + * @apiNote + * To release resources used by this stream {@link #close} should be called + * directly or by try-with-resources. Subclasses are responsible for the cleanup + * of resources acquired by the subclass. + * Subclasses that override {@link #finalize} in order to perform cleanup + * should be modified to use alternative cleanup mechanisms such as + * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method. + * + * @implSpec + * If this FileInputStream has been subclassed and the {@link #close} + * method has been overridden, the {@link #close} method will be + * called when the FileInputStream is unreachable. + * Otherwise, it is implementation specific how the resource cleanup described in + * {@link #close} is performed. + * * @author Arthur van Hoff * @see java.io.File @@ -63,6 +80,8 @@ class FileInputStream extends InputStream private volatile boolean closed; + private final AltFinalizer altFinalizer; + /** * Creates a <code>FileInputStream</code> by * opening a connection to an actual file, @@ -137,6 +156,10 @@ class FileInputStream extends InputStream fd.attach(this); path = name; open(name); + altFinalizer = AltFinalizer.get(this); + if (altFinalizer == null) { + fd.registerCleanup(); // open set the fd, register the cleanup + } } /** @@ -173,6 +196,7 @@ class FileInputStream extends InputStream } fd = fdObj; path = null; + altFinalizer = null; /* * FileDescriptor is being shared by streams. @@ -316,6 +340,14 @@ class FileInputStream extends InputStream * <p> If this stream has an associated channel then the channel is closed * as well. * + * @apiNote + * Overriding {@link #close} to perform cleanup actions is reliable + * only when called directly or when called by try-with-resources. + * Do not depend on finalization to invoke {@code close}; + * finalization is not reliable and is deprecated. + * If cleanup of native resources is needed, other mechanisms such as + * {@linkplain java.lang.ref.Cleaner} should be used. + * * @exception IOException if an I/O error occurs. * * @revised 1.4 @@ -404,16 +436,27 @@ class FileInputStream extends InputStream private static native void initIDs(); - static { initIDs(); } /** - * Ensures that the <code>close</code> method of this file input stream is + * Ensures that the {@link #close} method of this file input stream is * called when there are no more references to it. + * The {@link #finalize} method does not call {@link #close} directly. * - * @deprecated The {@code finalize} method has been deprecated. + * @apiNote + * To release resources used by this stream {@link #close} should be called + * directly or by try-with-resources. + * + * @implSpec + * If this FileInputStream has been subclassed and the {@link #close} + * method has been overridden, the {@link #close} method will be + * called when the FileInputStream is unreachable. + * Otherwise, it is implementation specific how the resource cleanup described in + * {@link #close} is performed. + * + * @deprecated The {@code finalize} method has been deprecated and will be removed. * Subclasses that override {@code finalize} in order to perform cleanup * should be modified to use alternative cleanup mechanisms and * to remove the overriding {@code finalize} method. @@ -425,15 +468,57 @@ class FileInputStream extends InputStream * @exception IOException if an I/O error occurs. * @see java.io.FileInputStream#close() */ - @Deprecated(since="9") + @Deprecated(since="9", forRemoval = true) protected void finalize() throws IOException { - if ((fd != null) && (fd != FileDescriptor.in)) { - /* if fd is shared, the references in FileDescriptor - * will ensure that finalizer is only called when - * safe to do so. All references using the fd have - * become unreachable. We can call close() - */ - close(); + } + + /** + * Class to call {@code FileInputStream.close} when finalized. + * If finalization of the stream is needed, an instance is created + * in its constructor(s). When the set of instances + * related to the stream is unreachable, the AltFinalizer performs + * the needed call to the stream's {@code close} method. + */ + static class AltFinalizer { + private final FileInputStream fis; + + /* + * Returns a finalizer object if the FIS needs a finalizer; otherwise null. + * If the FIS has a close method; it needs an AltFinalizer. + */ + static AltFinalizer get(FileInputStream fis) { + Class<?> clazz = fis.getClass(); + while (clazz != FileInputStream.class) { + try { + clazz.getDeclaredMethod("close"); + return new AltFinalizer(fis); + } catch (NoSuchMethodException nsme) { + // ignore + } + clazz = clazz.getSuperclass(); + } + return null; + } + + private AltFinalizer(FileInputStream fis) { + this.fis = fis; + } + + @Override + @SuppressWarnings("deprecation") + protected final void finalize() { + try { + if ((fis.fd != null) && (fis.fd != FileDescriptor.in)) { + /* if fd is shared, the references in FileDescriptor + * will ensure that finalizer is only called when + * safe to do so. All references using the fd have + * become unreachable. We can call close() + */ + fis.close(); + } + } catch (IOException ioe) { + // ignore + } } } } diff --git a/src/java.base/share/classes/java/io/FileOutputStream.java b/src/java.base/share/classes/java/io/FileOutputStream.java index f9c1baa3518..cb9c8cda0f3 100644 --- a/src/java.base/share/classes/java/io/FileOutputStream.java +++ b/src/java.base/share/classes/java/io/FileOutputStream.java @@ -25,6 +25,7 @@ package java.io; +import java.lang.reflect.Method; import java.nio.channels.FileChannel; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.JavaIOFileDescriptorAccess; @@ -44,6 +45,21 @@ import sun.nio.ch.FileChannelImpl; * such as image data. For writing streams of characters, consider using * <code>FileWriter</code>. * + * @apiNote + * To release resources used by this stream {@link #close} should be called + * directly or by try-with-resources. Subclasses are responsible for the cleanup + * of resources acquired by the subclass. + * Subclasses that override {@link #finalize} in order to perform cleanup + * should be modified to use alternative cleanup mechanisms such as + * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method. + * + * @implSpec + * If this FileOutputStream has been subclassed and the {@link #close} + * method has been overridden, the {@link #close} method will be + * called when the FileInputStream is unreachable. + * Otherwise, it is implementation specific how the resource cleanup described in + * {@link #close} is performed. + * * @author Arthur van Hoff * @see java.io.File * @see java.io.FileDescriptor @@ -80,6 +96,8 @@ class FileOutputStream extends OutputStream private volatile boolean closed; + private final AltFinalizer altFinalizer; + /** * Creates a file output stream to write to the file with the * specified name. A new <code>FileDescriptor</code> object is @@ -218,6 +236,10 @@ class FileOutputStream extends OutputStream this.path = name; open(name, append); + altFinalizer = AltFinalizer.get(this); + if (altFinalizer == null) { + fd.registerCleanup(); // open set the fd, register the cleanup + } } /** @@ -253,6 +275,7 @@ class FileOutputStream extends OutputStream } this.fd = fdObj; this.path = null; + this.altFinalizer = null; fd.attach(this); } @@ -340,6 +363,14 @@ class FileOutputStream extends OutputStream * <p> If this stream has an associated channel then the channel is closed * as well. * + * @apiNote + * Overriding {@link #close} to perform cleanup actions is reliable + * only when called directly or when called by try-with-resources. + * Do not depend on finalization to invoke {@code close}; + * finalization is not reliable and is deprecated. + * If cleanup of native resources is needed, other mechanisms such as + * {@linkplain java.lang.ref.Cleaner} should be used. + * * @exception IOException if an I/O error occurs. * * @revised 1.4 @@ -429,34 +460,35 @@ class FileOutputStream extends OutputStream /** * Cleans up the connection to the file, and ensures that the - * <code>close</code> method of this file output stream is + * {@link #close} method of this file output stream is * called when there are no more references to this stream. + * The {@link #finalize} method does not call {@link #close} directly. + * + * @apiNote + * To release resources used by this stream {@link #close} should be called + * directly or by try-with-resources. + * + * @implSpec + * If this FileOutputStream has been subclassed and the {@link #close} + * method has been overridden, the {@link #close} method will be + * called when the FileOutputStream is unreachable. + * Otherwise, it is implementation specific how the resource cleanup described in + * {@link #close} is performed. + * + * @deprecated The {@code finalize} method has been deprecated and will be removed. + * Subclasses that override {@code finalize} in order to perform cleanup + * should be modified to use alternative cleanup mechanisms and + * to remove the overriding {@code finalize} method. + * When overriding the {@code finalize} method, its implementation must explicitly + * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}. + * See the specification for {@link Object#finalize()} for further + * information about migration options. * - * @deprecated The {@code finalize} method has been deprecated. - * Subclasses that override {@code finalize} in order to perform cleanup - * should be modified to use alternative cleanup mechanisms and - * to remove the overriding {@code finalize} method. - * When overriding the {@code finalize} method, its implementation must explicitly - * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}. - * See the specification for {@link Object#finalize()} for further - * information about migration options. * @exception IOException if an I/O error occurs. * @see java.io.FileInputStream#close() */ - @Deprecated(since="9") + @Deprecated(since="9", forRemoval = true) protected void finalize() throws IOException { - if (fd != null) { - if (fd == FileDescriptor.out || fd == FileDescriptor.err) { - flush(); - } else { - /* if fd is shared, the references in FileDescriptor - * will ensure that finalizer is only called when - * safe to do so. All references using the fd have - * become unreachable. We can call close() - */ - close(); - } - } } private static native void initIDs(); @@ -465,4 +497,59 @@ class FileOutputStream extends OutputStream initIDs(); } + /** + * Class to call {@code FileOutputStream.close} when finalized. + * If finalization of the stream is needed, an instance is created + * in its constructor(s). When the set of instances + * related to the stream is unreachable, the AltFinalizer performs + * the needed call to the stream's {@code close} method. + */ + static class AltFinalizer { + private final FileOutputStream fos; + + /* + * Returns a finalizer object if the FOS needs a finalizer; otherwise null. + * If the FOS has a close method; it needs an AltFinalizer. + */ + static AltFinalizer get(FileOutputStream fos) { + Class<?> clazz = fos.getClass(); + while (clazz != FileOutputStream.class) { + try { + clazz.getDeclaredMethod("close"); + return new AltFinalizer(fos); + } catch (NoSuchMethodException nsme) { + // ignore + } + clazz = clazz.getSuperclass(); + } + return null; + } + + private AltFinalizer(FileOutputStream fos) { + this.fos = fos; + } + + @Override + @SuppressWarnings("deprecation") + protected final void finalize() { + try { + if (fos.fd != null) { + if (fos.fd == FileDescriptor.out || fos.fd == FileDescriptor.err) { + // Subclass may override flush; otherwise it is no-op + fos.flush(); + } else { + /* if fd is shared, the references in FileDescriptor + * will ensure that finalizer is only called when + * safe to do so. All references using the fd have + * become unreachable. We can call close() + */ + fos.close(); + } + } + } catch (IOException ioe) { + // ignore + } + } + } + } diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java index 5c946d1d715..4b6e66ff140 100644 --- a/src/java.base/share/classes/java/io/RandomAccessFile.java +++ b/src/java.base/share/classes/java/io/RandomAccessFile.java @@ -257,6 +257,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable { fd.attach(this); path = name; open(name, imode); + fd.registerCleanup(); // open sets the fd, register the cleanup } /** diff --git a/src/java.base/share/classes/java/net/SocketInputStream.java b/src/java.base/share/classes/java/net/SocketInputStream.java index 04ee3378eaa..bb3481b7164 100644 --- a/src/java.base/share/classes/java/net/SocketInputStream.java +++ b/src/java.base/share/classes/java/net/SocketInputStream.java @@ -283,7 +283,7 @@ class SocketInputStream extends FileInputStream /** * Overrides finalize, the fd is closed by the Socket. */ - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "removal"}) protected void finalize() {} /** diff --git a/src/java.base/share/classes/java/net/SocketOutputStream.java b/src/java.base/share/classes/java/net/SocketOutputStream.java index 0ba877bf56e..ddf3dcf9bb5 100644 --- a/src/java.base/share/classes/java/net/SocketOutputStream.java +++ b/src/java.base/share/classes/java/net/SocketOutputStream.java @@ -175,7 +175,7 @@ class SocketOutputStream extends FileOutputStream /** * Overrides finalize, the fd is closed by the Socket. */ - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "removal"}) protected void finalize() {} /** diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java index 017aefb16af..a82d04d6eed 100644 --- a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java +++ b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java @@ -25,6 +25,7 @@ package jdk.internal.misc; import java.io.FileDescriptor; +import java.io.IOException; /* * @author Chris Hegarty @@ -35,7 +36,8 @@ public interface JavaIOFileDescriptorAccess { public int get(FileDescriptor fdo); public void setAppend(FileDescriptor fdo, boolean append); public boolean getAppend(FileDescriptor fdo); - public void close(FileDescriptor fdo); + public void close(FileDescriptor fdo) throws IOException; + public void registerCleanup(FileDescriptor fdo); // Only valid on Windows public void setHandle(FileDescriptor fdo, long handle); diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index 5e48758bac0..274a17851e2 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -27,6 +27,7 @@ package sun.nio.ch; import java.io.FileDescriptor; import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.ref.Cleaner.Cleanable; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; @@ -109,7 +110,12 @@ public class FileChannelImpl } public void run() { - fdAccess.close(fd); + try { + fdAccess.close(fd); + } catch (IOException ioe) { + // Rethrow as unchecked so the exception can be propagated as needed + throw new UncheckedIOException("close", ioe); + } } } @@ -188,7 +194,11 @@ public class FileChannelImpl } else if (closer != null) { // Perform the cleaning action so it is not redone when // this channel becomes phantom reachable. - closer.clean(); + try { + closer.clean(); + } catch (UncheckedIOException uioe) { + throw uioe.getCause(); + } } else { fdAccess.close(fd); } diff --git a/src/java.base/unix/classes/java/io/FileDescriptor.java b/src/java.base/unix/classes/java/io/FileDescriptor.java index 6c20e9d5763..5e0b80eb625 100644 --- a/src/java.base/unix/classes/java/io/FileDescriptor.java +++ b/src/java.base/unix/classes/java/io/FileDescriptor.java @@ -25,10 +25,14 @@ package java.io; +import java.lang.ref.Cleaner; import java.util.ArrayList; import java.util.List; + import jdk.internal.misc.JavaIOFileDescriptorAccess; import jdk.internal.misc.SharedSecrets; +import jdk.internal.ref.CleanerFactory; +import jdk.internal.ref.PhantomCleanable; /** * Instances of the file descriptor class serve as an opaque handle @@ -64,7 +68,7 @@ public final class FileDescriptor { SharedSecrets.setJavaIOFileDescriptorAccess( new JavaIOFileDescriptorAccess() { public void set(FileDescriptor fdo, int fd) { - fdo.fd = fd; + fdo.set(fd); } public int get(FileDescriptor fdo) { @@ -79,10 +83,14 @@ public final class FileDescriptor { return fdo.append; } - public void close(FileDescriptor fdo) { + public void close(FileDescriptor fdo) throws IOException { fdo.close(); } + public void registerCleanup(FileDescriptor fdo) { + fdo.registerCleanup(); + } + public void setHandle(FileDescriptor fdo, long handle) { throw new UnsupportedOperationException(); } @@ -94,6 +102,11 @@ public final class FileDescriptor { ); } + /** + * Cleanup in case FileDescriptor is not explicitly closed. + */ + private FDCleanup cleanup; + /** * Constructs an (invalid) FileDescriptor * object. @@ -177,6 +190,34 @@ public final class FileDescriptor { /* This routine initializes JNI field offsets for the class */ private static native void initIDs(); + /** + * Set the fd. + * If setting to -1, clear the cleaner. + * The {@link #registerCleanup()} method should be called for new fds. + * @param fd the fd or -1 to indicate closed + */ + @SuppressWarnings("unchecked") + synchronized void set(int fd) { + if (fd == -1 && cleanup != null) { + cleanup.clear(); + cleanup = null; + } + this.fd = fd; + } + + /** + * Register a cleanup for the current raw fd. + * Used directly in java.io and indirectly via fdAccess. + * The cleanup should be registered after the fd is set in the FileDescriptor. + */ + @SuppressWarnings("unchecked") + synchronized void registerCleanup() { + if (cleanup != null) { + cleanup.clear(); + } + cleanup = FDCleanup.create(this); + } + /** * Returns true, if the file was opened for appending. */ @@ -185,9 +226,30 @@ public final class FileDescriptor { /** * Close the raw file descriptor or handle, if it has not already been closed * and set the fd and handle to -1. + * Clear the cleaner so the close does not happen twice. * Package private to allow it to be used in java.io. + * @throws IOException if close fails */ - native void close(); + @SuppressWarnings("unchecked") + synchronized void close() throws IOException { + if (cleanup != null) { + cleanup.clear(); + cleanup = null; + } + close0(); + } + + /* + * Close the raw file descriptor or handle, if it has not already been closed + * and set the fd and handle to -1. + */ + private native void close0() throws IOException; + + /* + * Raw close of the file descriptor. + * Used only for last chance cleanup. + */ + private static native void cleanupClose0(int fd) throws IOException; /* * Package private methods to track referents. @@ -252,4 +314,45 @@ public final class FileDescriptor { } } } + + /** + * Cleanup for a FileDescriptor when it becomes phantom reachable. + * Create a cleanup if fd != -1. + * Subclassed from {@code PhantomCleanable} so that {@code clear} can be + * called to disable the cleanup when the fd is closed by any means other + * than calling {@link FileDescriptor#close}. + * Otherwise, it may close the native fd after it has been reused. + */ + static final class FDCleanup extends PhantomCleanable<Object> { + private final int fd; + + static FDCleanup create(FileDescriptor fdo) { + return fdo.fd == -1 + ? null + : new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.fd); + } + + /** + * Constructor for a phantom cleanable reference. + * @param obj the object to monitor + * @param cleaner the cleaner + * @param fd file descriptor to close + */ + private FDCleanup(Object obj, Cleaner cleaner, int fd) { + super(obj, cleaner); + this.fd = fd; + } + + /** + * Close the native fd. + */ + @Override + protected void performCleanup() { + try { + cleanupClose0(fd); + } catch (IOException ioe) { + throw new UncheckedIOException("close", ioe); + } + } + } } diff --git a/src/java.base/unix/native/libjava/FileDescriptor_md.c b/src/java.base/unix/native/libjava/FileDescriptor_md.c index fcde1ef7f93..49669384489 100644 --- a/src/java.base/unix/native/libjava/FileDescriptor_md.c +++ b/src/java.base/unix/native/libjava/FileDescriptor_md.c @@ -71,8 +71,17 @@ Java_java_io_FileDescriptor_getAppend(JNIEnv *env, jclass fdClass, jint fd) { return ((flags & O_APPEND) == 0) ? JNI_FALSE : JNI_TRUE; } +JNIEXPORT void JNICALL +Java_java_io_FileDescriptor_cleanupClose0(JNIEnv *env, jclass fdClass, jint fd) { + if (fd != -1) { + if (close(fd) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "close failed"); + } + } +} + // instance method close0 for FileDescriptor JNIEXPORT void JNICALL -Java_java_io_FileDescriptor_close(JNIEnv *env, jobject this) { +Java_java_io_FileDescriptor_close0(JNIEnv *env, jobject this) { fileDescriptorClose(env, this); } diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c index 64374b50196..1ec21237856 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -139,6 +139,11 @@ fileDescriptorClose(JNIEnv *env, jobject this) if ((*env)->ExceptionOccurred(env)) { return; } + + if (fd == -1) { + return; // already closed and set to -1 + } + /* Set the fd to -1 before closing it so that the timing window * of other threads using the wrong fd (closed but recycled fd, * that gets re-opened with some other filename) is reduced. diff --git a/src/java.base/windows/classes/java/io/FileDescriptor.java b/src/java.base/windows/classes/java/io/FileDescriptor.java index 21fbd951fa5..ecc1934795d 100644 --- a/src/java.base/windows/classes/java/io/FileDescriptor.java +++ b/src/java.base/windows/classes/java/io/FileDescriptor.java @@ -25,10 +25,14 @@ package java.io; +import java.lang.ref.Cleaner; import java.util.ArrayList; import java.util.List; + import jdk.internal.misc.JavaIOFileDescriptorAccess; import jdk.internal.misc.SharedSecrets; +import jdk.internal.ref.CleanerFactory; +import jdk.internal.ref.PhantomCleanable; /** * Instances of the file descriptor class serve as an opaque handle @@ -81,12 +85,16 @@ public final class FileDescriptor { return fdo.append; } - public void close(FileDescriptor fdo) { + public void close(FileDescriptor fdo) throws IOException { fdo.close(); } + public void registerCleanup(FileDescriptor fdo) { + fdo.registerCleanup(); + } + public void setHandle(FileDescriptor fdo, long handle) { - fdo.handle = handle; + fdo.setHandle(handle); } public long getHandle(FileDescriptor fdo) { @@ -96,6 +104,11 @@ public final class FileDescriptor { ); } + /** + * Cleanup in case FileDescriptor is not explicitly closed. + */ + private FDCleanup cleanup; + /** * Constructs an (invalid) FileDescriptor * object. @@ -149,7 +162,7 @@ public final class FileDescriptor { * relevant device(s). In particular, if this FileDescriptor * refers to a physical storage medium, such as a file in a file * system, sync will not return until all in-memory modified copies - * of buffers associated with this FileDesecriptor have been + * of buffers associated with this FileDescriptor have been * written to the physical medium. * * sync is meant to be used by code that requires physical @@ -175,20 +188,69 @@ public final class FileDescriptor { /* This routine initializes JNI field offsets for the class */ private static native void initIDs(); - private static native long set(int d); - private static FileDescriptor standardStream(int fd) { FileDescriptor desc = new FileDescriptor(); - desc.handle = set(fd); + desc.handle = getHandle(fd); return desc; } + private static native long getHandle(int d); + + /** + * Set the handle. + * If setting to -1, clear the cleaner. + * The {@link #registerCleanup()} method should be called for new handles. + * @param handle the handle or -1 to indicate closed + */ + @SuppressWarnings("unchecked") + void setHandle(long handle) { + if (handle == -1 && cleanup != null) { + cleanup.clear(); + cleanup = null; + } + this.handle = handle; + } + + /** + * Register a cleanup for the current handle. + * Used directly in java.io and indirectly via fdAccess. + * The cleanup should be registered after the handle is set in the FileDescriptor. + */ + @SuppressWarnings("unchecked") + synchronized void registerCleanup() { + if (cleanup != null) { + cleanup.clear(); + } + cleanup = FDCleanup.create(this); + } + /** * Close the raw file descriptor or handle, if it has not already been closed * and set the fd and handle to -1. + * Clear the cleaner so the close does not happen twice. * Package private to allow it to be used in java.io. + * @throws IOException if close fails */ - native void close(); + @SuppressWarnings("unchecked") + synchronized void close() throws IOException { + if (cleanup != null) { + cleanup.clear(); + cleanup = null; + } + close0(); + } + + /* + * Close the raw file descriptor or handle, if it has not already been closed + * and set the fd and handle to -1. + */ + private native void close0() throws IOException; + + /* + * Raw close of the file handle. + * Used only for last chance cleanup. + */ + private static native void cleanupClose0(long handle) throws IOException; /* * Package private methods to track referents. @@ -253,4 +315,45 @@ public final class FileDescriptor { } } } + + /** + * Cleanup for a FileDescriptor when it becomes phantom reachable. + * Create a cleanup if handle != -1. + * Subclassed from {@code PhantomCleanable} so that {@code clear} can be + * called to disable the cleanup when the fd is closed by any means other + * than calling {@link FileDescriptor#close}. + * Otherwise, it may close the handle after it has been reused. + */ + static final class FDCleanup extends PhantomCleanable<Object> { + private final long handle; + + static FDCleanup create(FileDescriptor fdo) { + return fdo.handle == -1 + ? null + : new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.handle); + } + + /** + * Constructor for a phantom cleanable reference. + * @param obj the object to monitor + * @param cleaner the cleaner + * @param handle file handle to close + */ + private FDCleanup(Object obj, Cleaner cleaner, long handle) { + super(obj, cleaner); + this.handle = handle; + } + + /** + * Close the native handle. + */ + @Override + protected void performCleanup() { + try { + cleanupClose0(handle); + } catch (IOException ioe) { + throw new UncheckedIOException("close", ioe); + } + } + } } diff --git a/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java b/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java index 23adf004e6d..9424d36f892 100644 --- a/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java @@ -114,6 +114,7 @@ class FileDispatcherImpl extends FileDispatcher { FileDescriptor result = new FileDescriptor(); long handle = duplicateHandle(fdAccess.getHandle(fd)); fdAccess.setHandle(result, handle); + fdAccess.registerCleanup(result); return result; } diff --git a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java index 30f62f22f71..2a4fda647d8 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java @@ -139,7 +139,7 @@ public class WindowsAsynchronousFileChannelImpl invalidateAllLocks(); // close the file - close0(handle); + nd.close(fdObj); // waits until all I/O operations have completed ioCache.close(); @@ -728,8 +728,6 @@ public class WindowsAsynchronousFileChannelImpl private static native int lockFile(long handle, long position, long size, boolean shared, long overlapped) throws IOException; - private static native void close0(long handle); - static { IOUtil.load(); } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java b/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java index 16d65c78a30..3e219c98f24 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java @@ -216,8 +216,7 @@ class WindowsChannelFactory { } catch (IOException x) { // IOException is thrown if the file handle cannot be associated // with the completion port. All we can do is close the file. - long handle = fdAccess.getHandle(fdObj); - CloseHandle(handle); + fdAccess.close(fdObj); throw x; } } @@ -347,6 +346,7 @@ class WindowsChannelFactory { FileDescriptor fdObj = new FileDescriptor(); fdAccess.setHandle(fdObj, handle); fdAccess.setAppend(fdObj, flags.append); + fdAccess.registerCleanup(fdObj); return fdObj; } } diff --git a/src/java.base/windows/native/libjava/FileDescriptor_md.c b/src/java.base/windows/native/libjava/FileDescriptor_md.c index 0cd3a7321a7..8b64741b08a 100644 --- a/src/java.base/windows/native/libjava/FileDescriptor_md.c +++ b/src/java.base/windows/native/libjava/FileDescriptor_md.c @@ -57,7 +57,7 @@ Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) { } JNIEXPORT jlong JNICALL -Java_java_io_FileDescriptor_set(JNIEnv *env, jclass fdClass, jint fd) { +Java_java_io_FileDescriptor_getHandle(JNIEnv *env, jclass fdClass, jint fd) { SET_HANDLE(fd); } @@ -73,8 +73,17 @@ Java_java_io_FileDescriptor_sync(JNIEnv *env, jobject this) { } } +JNIEXPORT void JNICALL +Java_java_io_FileDescriptor_cleanupClose0(JNIEnv *env, jclass fdClass, jlong handle) { + if (handle != -1) { + if (CloseHandle((HANDLE)handle) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "close failed"); + } + } +} + // instance method close0 for FileDescriptor JNIEXPORT void JNICALL -Java_java_io_FileDescriptor_close(JNIEnv *env, jobject this) { +Java_java_io_FileDescriptor_close0(JNIEnv *env, jobject this) { fileDescriptorClose(env, this); } diff --git a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c index e24ca6e52b2..2269231f410 100644 --- a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c +++ b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c @@ -121,13 +121,3 @@ Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_lockFile(JNIEnv *env, jobject return 0; } -JNIEXPORT void JNICALL -Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_close0(JNIEnv* env, jclass this, - jlong handle) -{ - HANDLE h = (HANDLE)jlong_to_ptr(handle); - BOOL result = CloseHandle(h); - if (result == 0) { - JNU_ThrowIOExceptionWithLastError(env, "Close failed"); - } -} diff --git a/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java b/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java deleted file mode 100644 index 6ebcbf8c8ec..00000000000 --- a/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2007, 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 6524062 - * @summary Test to ensure that FIS.finalize() invokes the close() method as per - * the specification. - */ -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; - -public class FinalizeShdCallClose { - - static final String FILE_NAME = "empty.txt"; - - public static class MyStream extends FileInputStream { - private boolean closed = false; - - public MyStream(String name) throws FileNotFoundException { - super(name); - } - - public void finalize() { - try { - super.finalize(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - - public void close() { - try { - super.close(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - closed = true; - } - - public boolean isClosed() { - return closed; - } - } - - /* standalone interface */ - public static void main(String argv[]) throws Exception { - - File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME); - inFile.createNewFile(); - inFile.deleteOnExit(); - - String name = inFile.getPath(); - MyStream ms = null; - try { - ms = new MyStream(name); - } catch (FileNotFoundException e) { - System.out.println("Unexpected exception " + e); - throw(e); - } - ms.finalize(); - if (!ms.isClosed()) { - throw new Exception("MyStream.close() method is not called"); - } - System.out.println("OK"); - } -} diff --git a/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java b/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java new file mode 100644 index 00000000000..7664348f329 --- /dev/null +++ b/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2007, 2017, 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 + * @modules java.base/java.io:open + * @bug 6524062 + * @summary Test to ensure that FIS.finalize() invokes the close() method as per + * the specification. + * @run main/othervm UnreferencedFISClosesFd + */ +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * Tests for FIS unreferenced. + * - Not subclassed - cleaner cleanup + * - Subclassed no finalize or close - cleaner cleanup + * - Subclassed close overridden - AltFinalizer cleanup + * - Subclasses finalize overridden - cleaner cleanup + * - Subclasses finalize and close overridden - AltFinalizer cleanup + */ +public class UnreferencedFISClosesFd { + + enum CleanupType { + CLOSE, // Cleanup is handled via calling close + CLEANER} // Cleanup is handled via Cleaner + + static final String FILE_NAME = "empty.txt"; + + /** + * Subclass w/ no overrides; not finalize or close. + * Cleanup should be via the Cleaner (not close). + */ + public static class StreamOverrides extends FileInputStream { + + protected final AtomicInteger closeCounter; + + public StreamOverrides(String name) throws FileNotFoundException { + super(name); + closeCounter = new AtomicInteger(0); + } + + final AtomicInteger closeCounter() { + return closeCounter; + } + } + + /** + * Subclass overrides close. + * Cleanup should be via AltFinalizer calling close(). + */ + public static class StreamOverridesClose extends StreamOverrides { + + public StreamOverridesClose(String name) throws FileNotFoundException { + super(name); + } + + public void close() throws IOException { + closeCounter.incrementAndGet(); + super.close(); + } + } + + /** + * Subclass overrides finalize. + * Cleanup should be via the Cleaner (not close). + */ + public static class StreamOverridesFinalize extends StreamOverrides { + + public StreamOverridesFinalize(String name) throws FileNotFoundException { + super(name); + } + + @SuppressWarnings({"deprecation","removal"}) + protected void finalize() throws IOException { + super.finalize(); + } + } + + /** + * Subclass overrides finalize and close. + * Cleanup should be via AltFinalizer calling close(). + */ + public static class StreamOverridesFinalizeClose extends StreamOverridesClose { + + public StreamOverridesFinalizeClose(String name) throws FileNotFoundException { + super(name); + } + + @SuppressWarnings({"deprecation","removal"}) + protected void finalize() throws IOException { + super.finalize(); + } + } + + /** + * Main runs each test case and reports number of failures. + */ + public static void main(String argv[]) throws Exception { + + File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME); + inFile.createNewFile(); + inFile.deleteOnExit(); + + String name = inFile.getPath(); + + int failCount = 0; + failCount += test(new FileInputStream(name), CleanupType.CLEANER); + + failCount += test(new StreamOverrides(name), CleanupType.CLEANER); + + failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE); + + failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER); + + failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE); + + if (failCount > 0) { + throw new AssertionError("Failed test count: " + failCount); + } + } + + private static int test(FileInputStream fis, CleanupType cleanType) throws Exception { + + try { + System.out.printf("%nTesting %s%n", fis.getClass().getName()); + + // Prepare to wait for FIS to be reclaimed + ReferenceQueue<Object> queue = new ReferenceQueue<>(); + HashSet<Reference<?>> pending = new HashSet<>(); + WeakReference<FileInputStream> msWeak = new WeakReference<>(fis, queue); + pending.add(msWeak); + + FileDescriptor fd = fis.getFD(); + WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue); + pending.add(fdWeak); + + Field fdField = FileDescriptor.class.getDeclaredField("fd"); + fdField.setAccessible(true); + int ffd = fdField.getInt(fd); + + Field altFinalizerField = FileInputStream.class.getDeclaredField("altFinalizer"); + altFinalizerField.setAccessible(true); + Object altFinalizer = altFinalizerField.get(fis); + if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) { + throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer + + ", for " + cleanType); + } + + Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup"); + cleanupField.setAccessible(true); + Object cleanup = cleanupField.get(fd); + System.out.printf(" cleanup: %s, alt: %s, ffd: %d, cf: %s%n", + cleanup, altFinalizer, ffd, cleanupField); + if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) { + throw new Exception("unexpected cleanup: " + + cleanup + ", for " + cleanType); + } + if (cleanup != null) { + WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue); + pending.add(cleanupWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", + fdWeak, msWeak, cleanupWeak); + } + if (altFinalizer != null) { + WeakReference<Object> altFinalizerWeak = new WeakReference<>(altFinalizer, queue); + pending.add(altFinalizerWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n altFinalizerWeak: %s%n", + fdWeak, msWeak, altFinalizerWeak); + } + + AtomicInteger closeCounter = fis instanceof StreamOverrides + ? ((StreamOverrides)fis).closeCounter() : null; + + Reference<?> r; + while (((r = queue.remove(1000L)) != null) + || !pending.isEmpty()) { + System.out.printf(" r: %s, pending: %d%n", + r, pending.size()); + if (r != null) { + pending.remove(r); + } else { + fis = null; + fd = null; + cleanup = null; + altFinalizer = null; + System.gc(); // attempt to reclaim them + } + } + Reference.reachabilityFence(fd); + Reference.reachabilityFence(fis); + Reference.reachabilityFence(cleanup); + Reference.reachabilityFence(altFinalizer); + + // Confirm the correct number of calls to close depending on the cleanup type + switch (cleanType) { + case CLEANER: + if (closeCounter != null && closeCounter.get() > 0) { + throw new RuntimeException("Close should not have been called: count: " + + closeCounter); + } + break; + case CLOSE: + if (closeCounter == null || closeCounter.get() == 0) { + throw new RuntimeException("Close should have been called: count: 0"); + } + break; + } + } catch (Exception ex) { + ex.printStackTrace(System.out); + return 1; + } + return 0; + } +} diff --git a/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java b/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java deleted file mode 100644 index a981a5e4154..00000000000 --- a/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2007, 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 6524062 - * @summary Test to ensure that FOS.finalize() invokes the close() method as per - * the specification. - */ -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; - -public class FinalizeShdCallClose { - - static final String FILE_NAME = "empty.txt"; - - public static class MyStream extends FileOutputStream { - private boolean closed = false; - - public MyStream(String name) throws FileNotFoundException { - super(name); - } - - public void finalize() { - try { - super.finalize(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - - public void close() { - try { - super.close(); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - closed = true; - } - - public boolean isClosed() { - return closed; - } - } - - /* standalone interface */ - public static void main(String argv[]) throws Exception { - - File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME); - inFile.createNewFile(); - inFile.deleteOnExit(); - - String name = inFile.getPath(); - MyStream ms = null; - try { - ms = new MyStream(name); - } catch (FileNotFoundException e) { - System.out.println("Unexpected exception " + e); - throw(e); - } - ms.finalize(); - if (!ms.isClosed()) { - throw new Exception("MyStream.close() method is not called"); - } - System.out.println("OK"); - } -} diff --git a/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java b/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java new file mode 100644 index 00000000000..71d0e0008f3 --- /dev/null +++ b/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2007, 2017, 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 + * @modules java.base/java.io:open + * @bug 6524062 + * @summary Test to ensure that FOS.finalize() invokes the close() method as per + * the specification. + * @run main/othervm UnreferencedFOSClosesFd + */ +import java.io.*; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class UnreferencedFOSClosesFd { + + enum CleanupType { + CLOSE, // Cleanup is handled via calling close + CLEANER} // Cleanup is handled via Cleaner + + static final String FILE_NAME = "empty.txt"; + + /** + * Subclass w/ no overrides; not finalize or close. + * Cleanup should be via the Cleaner (not close). + */ + public static class StreamOverrides extends FileOutputStream { + + protected final AtomicInteger closeCounter; + + public StreamOverrides(String name) throws FileNotFoundException { + super(name); + closeCounter = new AtomicInteger(0); + } + + final AtomicInteger closeCounter() { + return closeCounter; + } + } + + /** + * Subclass overrides close. + * Cleanup should be via AltFinalizer calling close(). + */ + public static class StreamOverridesClose extends StreamOverrides { + + public StreamOverridesClose(String name) throws FileNotFoundException { + super(name); + } + + public void close() throws IOException { + closeCounter.incrementAndGet(); + super.close(); + } + } + + /** + * Subclass overrides finalize and close. + * Cleanup should be via the Cleaner (not close). + */ + public static class StreamOverridesFinalize extends StreamOverrides { + + public StreamOverridesFinalize(String name) throws FileNotFoundException { + super(name); + } + + @SuppressWarnings({"deprecation","removal"}) + protected void finalize() throws IOException { + super.finalize(); + } + } + + /** + * Subclass overrides finalize and close. + * Cleanup should be via AltFinalizer calling close(). + */ + public static class StreamOverridesFinalizeClose extends StreamOverridesClose { + + public StreamOverridesFinalizeClose(String name) throws FileNotFoundException { + super(name); + } + + @SuppressWarnings({"deprecation","removal"}) + protected void finalize() throws IOException { + super.finalize(); + } + } + + /** + * Main runs each test case and reports number of failures. + */ + public static void main(String argv[]) throws Exception { + + File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME); + inFile.createNewFile(); + inFile.deleteOnExit(); + + String name = inFile.getPath(); + + int failCount = 0; + failCount += test(new FileOutputStream(name), CleanupType.CLEANER); + + failCount += test(new StreamOverrides(name), CleanupType.CLEANER); + + failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE); + + failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER); + + failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE); + + if (failCount > 0) { + throw new AssertionError("Failed test count: " + failCount); + } + } + + + private static int test(FileOutputStream fos, CleanupType cleanType) throws Exception { + + try { + System.out.printf("%nTesting %s%n", fos.getClass().getName()); + + // Prepare to wait for FOS to be reclaimed + ReferenceQueue<Object> queue = new ReferenceQueue<>(); + HashSet<Reference<?>> pending = new HashSet<>(); + WeakReference<FileOutputStream> msWeak = new WeakReference<>(fos, queue); + pending.add(msWeak); + + FileDescriptor fd = fos.getFD(); + WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue); + pending.add(fdWeak); + + Field fdField = FileDescriptor.class.getDeclaredField("fd"); + fdField.setAccessible(true); + int ffd = fdField.getInt(fd); + + Field altFinalizerField = FileOutputStream.class.getDeclaredField("altFinalizer"); + altFinalizerField.setAccessible(true); + Object altFinalizer = altFinalizerField.get(fos); + if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) { + throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer + + ", for " + cleanType); + } + + Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup"); + cleanupField.setAccessible(true); + Object cleanup = cleanupField.get(fd); + System.out.printf(" cleanup: %s, alt: %s, ffd: %d, cf: %s%n", + cleanup, altFinalizer, ffd, cleanupField); + if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) { + throw new Exception("unexpected cleanup: " + + cleanup + ", for " + cleanType); + } + if (cleanup != null) { + WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue); + pending.add(cleanupWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n cleanupWeak: %s%n", + fdWeak, msWeak, cleanupWeak); + } + if (altFinalizer != null) { + WeakReference<Object> altFinalizerWeak = new WeakReference<>(altFinalizer, queue); + pending.add(altFinalizerWeak); + System.out.printf(" fdWeak: %s%n msWeak: %s%n altFinalizerWeak: %s%n", + fdWeak, msWeak, altFinalizerWeak); + } + + AtomicInteger closeCounter = fos instanceof StreamOverrides + ? ((StreamOverrides) fos).closeCounter() : null; + + Reference<?> r; + while (((r = queue.remove(1000L)) != null) + || !pending.isEmpty()) { + System.out.printf(" r: %s, pending: %d%n", + r, pending.size()); + if (r != null) { + pending.remove(r); + } else { + fos = null; + fd = null; + cleanup = null; + altFinalizer = null; + System.gc(); // attempt to reclaim them + } + } + Reference.reachabilityFence(fd); + Reference.reachabilityFence(fos); + Reference.reachabilityFence(cleanup); + Reference.reachabilityFence(altFinalizer); + + // Confirm the correct number of calls to close depending on the cleanup type + switch (cleanType) { + case CLEANER: + if (closeCounter != null && closeCounter.get() > 0) { + throw new RuntimeException("Close should not have been called: count: " + + closeCounter); + } + break; + case CLOSE: + if (closeCounter == null || closeCounter.get() == 0) { + throw new RuntimeException("Close should have been called: count: 0"); + } + break; + } + } catch (Exception ex) { + ex.printStackTrace(System.out); + return 1; + } + return 0; + } +} diff --git a/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java b/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java new file mode 100644 index 00000000000..b6ebff6a14b --- /dev/null +++ b/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2007, 2017, 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. + */ + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.RandomAccessFile; +import java.lang.ref.Cleaner; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.HashSet; + +/** + * @test + * @bug 8080225 + * @modules java.base/java.io:open + * @summary Test to ensure that an unclosed and unreferenced RandomAccessFile closes the fd + * @run main/othervm UnreferencedRAFClosesFd + */ +public class UnreferencedRAFClosesFd { + + static final String FILE_NAME = "empty.txt"; + + /* standalone interface */ + public static void main(String argv[]) throws Exception { + + File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME); + inFile.createNewFile(); + inFile.deleteOnExit(); + + String name = inFile.getPath(); + RandomAccessFile raf; + try { + // raf is explicitly *not* closed to allow cleaner to work + raf = new RandomAccessFile(name, "rw"); + } catch (FileNotFoundException e) { + System.out.println("Unexpected exception " + e); + throw(e); + } + FileDescriptor fd = raf.getFD(); + + Field fdField = FileDescriptor.class.getDeclaredField("cleanup"); + fdField.setAccessible(true); + Cleaner.Cleanable cleanup = (Cleaner.Cleanable)fdField.get(fd); + + // Prepare to wait for FOS, FD, Cleanup to be reclaimed + ReferenceQueue<Object> queue = new ReferenceQueue<>(); + HashSet<Reference<?>> pending = new HashSet<>(3); + pending.add(new WeakReference<>(cleanup, queue)); + pending.add(new WeakReference<>(raf, queue)); + pending.add(new WeakReference<>(fd, queue)); + + Reference<?> r; + while (((r = queue.remove(10L)) != null) + || !pending.isEmpty()) { + System.out.printf("r: %s, pending: %d%n", r, pending.size()); + if (r != null) { + pending.remove(r); + } else { + cleanup = null; + raf = null; + fd = null; + System.gc(); // attempt to reclaim the RAF, cleanup, and fd + } + } + + // Keep these variables in scope as gc roots + Reference.reachabilityFence(cleanup); + Reference.reachabilityFence(fd); + Reference.reachabilityFence(raf); + Reference.reachabilityFence(pending); + } +} diff --git a/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java b/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java index e7fafc10fb5..860b2d99910 100644 --- a/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java +++ b/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java @@ -24,26 +24,34 @@ /** * @test * @bug 8047769 - * @modules java.base/java.lang.ref:open + * @modules java.base/java.io:open + * java.base/java.lang.ref:open * java.base/sun.security.provider:open * @summary SecureRandom should be more frugal with file descriptors */ import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; +import java.util.HashSet; public class FileInputStreamPoolTest { static final byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; - static void testCaching(File file) throws IOException { + static FilterInputStream testCaching(File file) throws IOException { InputStream in1 = TestProxy.FileInputStreamPool_getInputStream(file); InputStream in2 = TestProxy.FileInputStreamPool_getInputStream(file); assertTrue(in1 == in2, @@ -58,6 +66,8 @@ public class FileInputStreamPoolTest { assertTrue(Arrays.equals(readBytes, bytes), "readBytes: " + Arrays.toString(readBytes) + " not equal to expected: " + Arrays.toString(bytes)); + + return (FilterInputStream)in1; } static void assertTrue(boolean test, String message) { @@ -66,13 +76,34 @@ public class FileInputStreamPoolTest { } } - static void processReferences() { - // make JVM process References - System.gc(); - // help ReferenceHandler thread enqueue References - while (TestProxy.Reference_waitForReferenceProcessing()) { } - // help run Finalizers - System.runFinalization(); + static void processReferences(FilterInputStream in1) throws InterruptedException, IOException { + FileInputStream fis = TestProxy.FilterInputStream_getInField(in1); + FileDescriptor fd = fis.getFD(); + System.out.printf("fis: %s, fd: %s%n", fis, fd); + // Prepare to wait for FD to be reclaimed + ReferenceQueue<Object> queue = new ReferenceQueue<>(); + HashSet<Reference<?>> pending = new HashSet<>(); + pending.add(new WeakReference<>(in1, queue)); + pending.add(new WeakReference<>(fis, queue)); + pending.add(new WeakReference<>(fd, queue)); + + Reference<?> r; + while (((r = queue.remove(10L)) != null) + || !pending.isEmpty()) { + System.out.printf("r: %s, pending: %d%n", r, pending.size()); + if (r != null) { + pending.remove(r); + } else { + fd = null; + fis = null; + in1 = null; + System.gc(); // attempt to reclaim the FD + } + Thread.sleep(10L); + } + Reference.reachabilityFence(fd); + Reference.reachabilityFence(fis); + Reference.reachabilityFence(in1); } public static void main(String[] args) throws Exception { @@ -88,16 +119,14 @@ public class FileInputStreamPoolTest { out.write(bytes); } - // test caching 1t time - testCaching(file); + // test caching 1st time - processReferences(); + processReferences(testCaching(file)); // test caching 2nd time - this should only succeed if the stream // is re-opened as a consequence of cleared WeakReference - testCaching(file); - processReferences(); + processReferences(testCaching(file)); } } @@ -109,6 +138,7 @@ public class FileInputStreamPoolTest { static class TestProxy { private static final Method getInputStreamMethod; private static final Method waitForReferenceProcessingMethod; + private static final Field inField; static { try { @@ -122,6 +152,9 @@ public class FileInputStreamPoolTest { waitForReferenceProcessingMethod = Reference.class.getDeclaredMethod("waitForReferenceProcessing"); waitForReferenceProcessingMethod.setAccessible(true); + + inField = FilterInputStream.class.getDeclaredField("in"); + inField.setAccessible(true); } catch (Exception e) { throw new Error(e); } @@ -165,5 +198,13 @@ public class FileInputStreamPoolTest { throw new RuntimeException(e); } } + + static FileInputStream FilterInputStream_getInField(FilterInputStream fis) { + try { + return (FileInputStream) inField.get(fis); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } } } From 82bf0799c67f224ffb1875e630f5152e8410ad14 Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan <xuelei@openjdk.org> Date: Fri, 8 Dec 2017 16:41:30 +0000 Subject: [PATCH 153/165] 8148421: Transport Layer Security (TLS) Session Hash and Extended Master Secret Extension Co-authored-by: Martin Balao <mbalao@redhat.com> Reviewed-by: jnimeh, ahgross, rhalade, wetmore --- .../com/sun/crypto/provider/SunJCE.java | 4 +- .../provider/TlsMasterSecretGenerator.java | 31 +++-- .../sun/crypto/provider/TlsPrfGenerator.java | 7 +- .../spec/TlsMasterSecretParameterSpec.java | 61 ++++++++- .../sun/security/ssl/ClientHandshaker.java | 116 ++++++++++++++++-- .../ssl/ExtendedMasterSecretExtension.java | 70 +++++++++++ .../sun/security/ssl/ExtensionType.java | 6 +- .../sun/security/ssl/HandshakeMessage.java | 6 +- .../classes/sun/security/ssl/Handshaker.java | 77 ++++++++++-- .../sun/security/ssl/HelloExtensions.java | 2 + .../sun/security/ssl/SSLSessionImpl.java | 19 ++- .../sun/security/ssl/ServerHandshaker.java | 70 ++++++++++- .../net/ssl/DTLS/NoMacInitialClientHello.java | 3 +- .../ssl/DHKeyExchange/DHEKeySizing.java | 36 +++--- .../ssl/ExtensionType/OptimalListSize.java | 4 +- 15 files changed, 448 insertions(+), 64 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index e44a8476dc2..3e681a57285 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -769,6 +769,8 @@ public final class SunJCE extends Provider { "com.sun.crypto.provider.TlsMasterSecretGenerator"); put("Alg.Alias.KeyGenerator.SunTls12MasterSecret", "SunTlsMasterSecret"); + put("Alg.Alias.KeyGenerator.SunTlsExtendedMasterSecret", + "SunTlsMasterSecret"); put("KeyGenerator.SunTlsKeyMaterial", "com.sun.crypto.provider.TlsKeyMaterialGenerator"); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java index c772396e990..fda0828f6d4 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -102,21 +102,32 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi { try { byte[] master; - byte[] clientRandom = spec.getClientRandom(); - byte[] serverRandom = spec.getServerRandom(); - if (protocolVersion >= 0x0301) { - byte[] seed = concat(clientRandom, serverRandom); + byte[] label; + byte[] seed; + byte[] extendedMasterSecretSessionHash = + spec.getExtendedMasterSecretSessionHash(); + if (extendedMasterSecretSessionHash.length != 0) { + label = LABEL_EXTENDED_MASTER_SECRET; + seed = extendedMasterSecretSessionHash; + } else { + byte[] clientRandom = spec.getClientRandom(); + byte[] serverRandom = spec.getServerRandom(); + label = LABEL_MASTER_SECRET; + seed = concat(clientRandom, serverRandom); + } master = ((protocolVersion >= 0x0303) ? - doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48, - spec.getPRFHashAlg(), spec.getPRFHashLength(), - spec.getPRFBlockSize()) : - doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48)); + doTLS12PRF(premaster, label, seed, 48, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(premaster, label, seed, 48)); } else { master = new byte[48]; MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha = MessageDigest.getInstance("SHA"); + byte[] clientRandom = spec.getClientRandom(); + byte[] serverRandom = spec.getServerRandom(); byte[] tmp = new byte[20]; for (int i = 0; i < 3; i++) { sha.update(SSL3_CONST[i]); @@ -175,5 +186,5 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi { } } - } + diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java index 90cde7c6bfb..2f945361c3f 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -55,6 +55,11 @@ abstract class TlsPrfGenerator extends KeyGeneratorSpi { static final byte[] LABEL_MASTER_SECRET = // "master secret" { 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 }; + static final byte[] LABEL_EXTENDED_MASTER_SECRET = + // "extended master secret" + { 101, 120, 116, 101, 110, 100, 101, 100, 32, 109, 97, 115, 116, + 101, 114, 32, 115, 101, 99, 114, 101, 116 }; + static final byte[] LABEL_KEY_EXPANSION = // "key expansion" { 107, 101, 121, 32, 101, 120, 112, 97, 110, 115, 105, 111, 110 }; diff --git a/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java b/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java index 832b38edd76..e563360481b 100644 --- a/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java +++ b/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2017, 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 @@ -48,6 +48,7 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { private final SecretKey premasterSecret; private final int majorVersion, minorVersion; private final byte[] clientRandom, serverRandom; + private final byte[] extendedMasterSecretSessionHash; private final String prfHashAlg; private final int prfHashLength; private final int prfBlockSize; @@ -80,6 +81,50 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { int majorVersion, int minorVersion, byte[] clientRandom, byte[] serverRandom, String prfHashAlg, int prfHashLength, int prfBlockSize) { + this(premasterSecret, majorVersion, minorVersion, + clientRandom, serverRandom, + new byte[0], + prfHashAlg, prfHashLength, prfBlockSize); + } + + /** + * Constructs a new TlsMasterSecretParameterSpec. + * + * <p>The <code>getAlgorithm()</code> method of <code>premasterSecret</code> + * should return <code>"TlsRsaPremasterSecret"</code> if the key exchange + * algorithm was RSA and <code>"TlsPremasterSecret"</code> otherwise. + * + * @param premasterSecret the premaster secret + * @param majorVersion the major number of the protocol version + * @param minorVersion the minor number of the protocol version + * @param extendedMasterSecretSessionHash the session hash for + * Extended Master Secret + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * + * @throws NullPointerException if premasterSecret is null + * @throws IllegalArgumentException if minorVersion or majorVersion are + * negative or larger than 255 + */ + public TlsMasterSecretParameterSpec(SecretKey premasterSecret, + int majorVersion, int minorVersion, + byte[] extendedMasterSecretSessionHash, + String prfHashAlg, int prfHashLength, int prfBlockSize) { + this(premasterSecret, majorVersion, minorVersion, + new byte[0], new byte[0], + extendedMasterSecretSessionHash, + prfHashAlg, prfHashLength, prfBlockSize); + } + + private TlsMasterSecretParameterSpec(SecretKey premasterSecret, + int majorVersion, int minorVersion, + byte[] clientRandom, byte[] serverRandom, + byte[] extendedMasterSecretSessionHash, + String prfHashAlg, int prfHashLength, int prfBlockSize) { if (premasterSecret == null) { throw new NullPointerException("premasterSecret must not be null"); } @@ -88,6 +133,9 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { this.minorVersion = checkVersion(minorVersion); this.clientRandom = clientRandom.clone(); this.serverRandom = serverRandom.clone(); + this.extendedMasterSecretSessionHash = + (extendedMasterSecretSessionHash != null ? + extendedMasterSecretSessionHash.clone() : new byte[0]); this.prfHashAlg = prfHashAlg; this.prfHashLength = prfHashLength; this.prfBlockSize = prfBlockSize; @@ -146,6 +194,17 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { return serverRandom.clone(); } + /** + * Returns a copy of the Extended Master Secret session hash. + * + * @return a copy of the Extended Master Secret session hash, or an empty + * array if no extended master secret session hash was provided + * at instantiation time + */ + public byte[] getExtendedMasterSecretSessionHash() { + return extendedMasterSecretSessionHash.clone(); + } + /** * Obtains the PRF hash algorithm to use in the PRF calculation. * diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java b/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java index 1d6c74eab3a..cc12ab581c2 100644 --- a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java +++ b/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java @@ -655,7 +655,8 @@ final class ClientHandshaker extends Handshaker { // validate subject identity ClientKeyExchangeService p = - ClientKeyExchangeService.find(sessionSuite.keyExchange.name); + ClientKeyExchangeService.find( + sessionSuite.keyExchange.name); if (p != null) { Principal localPrincipal = session.getLocalPrincipal(); @@ -663,8 +664,9 @@ final class ClientHandshaker extends Handshaker { if (debug != null && Debug.isOn("session")) System.out.println("Subject identity is same"); } else { - throw new SSLProtocolException("Server resumed" + - " session with wrong subject identity or no subject"); + throw new SSLProtocolException( + "Server resumed session with " + + "wrong subject identity or no subject"); } } @@ -707,6 +709,54 @@ final class ClientHandshaker extends Handshaker { } // Otherwise, using the value negotiated during the original // session initiation + // check the "extended_master_secret" extension + ExtendedMasterSecretExtension extendedMasterSecretExt = + (ExtendedMasterSecretExtension)mesg.extensions.get( + ExtensionType.EXT_EXTENDED_MASTER_SECRET); + if (extendedMasterSecretExt != null) { + // Is it the expected server extension? + if (!useExtendedMasterSecret || + !mesgVersion.useTLS10PlusSpec() || !requestedToUseEMS) { + fatalSE(Alerts.alert_unsupported_extension, + "Server sent the extended_master_secret " + + "extension improperly"); + } + + // For abbreviated handshake, if the original session did not use + // the "extended_master_secret" extension but the new ServerHello + // contains the extension, the client MUST abort the handshake. + if (resumingSession && (session != null) && + !session.getUseExtendedMasterSecret()) { + fatalSE(Alerts.alert_unsupported_extension, + "Server sent an unexpected extended_master_secret " + + "extension on session resumption"); + } + } else { + if (useExtendedMasterSecret && !allowLegacyMasterSecret) { + // For full handshake, if a client receives a ServerHello + // without the extension, it SHOULD abort the handshake if + // it does not wish to interoperate with legacy servers. + fatalSE(Alerts.alert_handshake_failure, + "Extended Master Secret extension is required"); + } + + if (resumingSession && (session != null)) { + if (session.getUseExtendedMasterSecret()) { + // For abbreviated handshake, if the original session used + // the "extended_master_secret" extension but the new + // ServerHello does not contain the extension, the client + // MUST abort the handshake. + fatalSE(Alerts.alert_handshake_failure, + "Missing Extended Master Secret extension " + + "on session resumption"); + } else if (useExtendedMasterSecret && !allowLegacyResumption) { + // Unlikely, abbreviated handshake should be discarded. + fatalSE(Alerts.alert_handshake_failure, + "Extended Master Secret extension is required"); + } + } + } + // check the ALPN extension ALPNExtension serverHelloALPN = (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN); @@ -777,7 +827,8 @@ final class ClientHandshaker extends Handshaker { && (type != ExtensionType.EXT_ALPN) && (type != ExtensionType.EXT_RENEGOTIATION_INFO) && (type != ExtensionType.EXT_STATUS_REQUEST) - && (type != ExtensionType.EXT_STATUS_REQUEST_V2)) { + && (type != ExtensionType.EXT_STATUS_REQUEST_V2) + && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) { // Note: Better to check client requested extensions rather // than all supported extensions. fatalSE(Alerts.alert_unsupported_extension, @@ -788,7 +839,8 @@ final class ClientHandshaker extends Handshaker { // Create a new session, we need to do the full handshake session = new SSLSessionImpl(protocolVersion, cipherSuite, getLocalSupportedSignAlgs(), - mesg.sessionId, getHostSE(), getPortSE()); + mesg.sessionId, getHostSE(), getPortSE(), + (extendedMasterSecretExt != null)); session.setRequestedServerNames(requestedServerNames); session.setNegotiatedMaxFragSize(requestedMFLength); session.setMaximumPacketSize(maximumPacketSize); @@ -1430,6 +1482,44 @@ final class ClientHandshaker extends Handshaker { session = null; } + if ((session != null) && useExtendedMasterSecret) { + boolean isTLS10Plus = sessionVersion.useTLS10PlusSpec(); + if (isTLS10Plus && !session.getUseExtendedMasterSecret()) { + if (!allowLegacyResumption) { + // perform full handshake instead + // + // The client SHOULD NOT offer an abbreviated handshake + // to resume a session that does not use an extended + // master secret. Instead, it SHOULD offer a full + // handshake. + session = null; + } + } + + if ((session != null) && !allowUnsafeServerCertChange) { + // It is fine to move on with abbreviate handshake if + // endpoint identification is enabled. + String identityAlg = getEndpointIdentificationAlgorithmSE(); + if ((identityAlg == null || identityAlg.length() == 0)) { + if (isTLS10Plus) { + if (!session.getUseExtendedMasterSecret()) { + // perform full handshake instead + session = null; + } // Otherwise, use extended master secret. + } else { + // The extended master secret extension does not + // apply to SSL 3.0. Perform a full handshake + // instead. + // + // Note that the useExtendedMasterSecret is + // extended to protect SSL 3.0 connections, + // by discarding abbreviate handshake. + session = null; + } + } + } + } + if (session != null) { if (debug != null) { if (Debug.isOn("handshake") || Debug.isOn("session")) { @@ -1539,6 +1629,14 @@ final class ClientHandshaker extends Handshaker { clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs); } + // add Extended Master Secret extension + if (useExtendedMasterSecret && maxProtocolVersion.useTLS10PlusSpec()) { + if ((session == null) || session.getUseExtendedMasterSecret()) { + clientHelloMessage.addExtendedMasterSecretExtension(); + requestedToUseEMS = true; + } + } + // add server_name extension if (enableSNIExtension) { if (session != null) { @@ -1647,10 +1745,14 @@ final class ClientHandshaker extends Handshaker { // Allow server certificate change in client side during renegotiation // after a session-resumption abbreviated initial handshake? // - // DO NOT need to check allowUnsafeServerCertChange here. We only + // DO NOT need to check allowUnsafeServerCertChange here. We only // reserve server certificates when allowUnsafeServerCertChange is // flase. - if (reservedServerCerts != null) { + // + // Allow server certificate change if it is negotiated to use the + // extended master secret. + if ((reservedServerCerts != null) && + !session.getUseExtendedMasterSecret()) { // It is not necessary to check the certificate update if endpoint // identification is enabled. String identityAlg = getEndpointIdentificationAlgorithmSE(); diff --git a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java new file mode 100644 index 00000000000..bf7f7aefd75 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * 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. + */ + +package sun.security.ssl; + +import java.io.IOException; +import javax.net.ssl.SSLProtocolException; + +/** + * Extended Master Secret TLS extension (TLS 1.0+). This extension + * defines how to calculate the TLS connection master secret and + * mitigates some types of man-in-the-middle attacks. + * + * See further information in + * <a href="https://tools.ietf.org/html/rfc7627">RFC 7627</a>. + * + * @author Martin Balao (mbalao@redhat.com) + */ +final class ExtendedMasterSecretExtension extends HelloExtension { + ExtendedMasterSecretExtension() { + super(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + } + + ExtendedMasterSecretExtension(HandshakeInStream s, + int len) throws IOException { + super(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + + if (len != 0) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + } + + @Override + int length() { + return 4; // 4: extension type and length fields + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); // ExtensionType extension_type; + s.putInt16(0); // extension_data length + } + + @Override + public String toString() { + return "Extension " + type; + } +} + diff --git a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java b/src/java.base/share/classes/sun/security/ssl/ExtensionType.java index 5338807bbdc..0b908a2aa7d 100644 --- a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java +++ b/src/java.base/share/classes/sun/security/ssl/ExtensionType.java @@ -43,7 +43,7 @@ final class ExtensionType { return name; } - static List<ExtensionType> knownExtensions = new ArrayList<>(15); + static List<ExtensionType> knownExtensions = new ArrayList<>(16); static ExtensionType get(int id) { for (ExtensionType ext : knownExtensions) { @@ -105,6 +105,10 @@ final class ExtensionType { static final ExtensionType EXT_STATUS_REQUEST_V2 = e(0x0011, "status_request_v2"); // IANA registry value: 17 + // extensions defined in RFC 7627 + static final ExtensionType EXT_EXTENDED_MASTER_SECRET = + e(0x0017, "extended_master_secret"); // IANA registry value: 23 + // extensions defined in RFC 5746 static final ExtensionType EXT_RENEGOTIATION_INFO = e(0xff01, "renegotiation_info"); // IANA registry value: 65281 diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java b/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java index cf3648685ba..952f8808b4d 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java @@ -389,6 +389,10 @@ static final class ClientHello extends HandshakeMessage { extensions.add(signatureAlgorithm); } + void addExtendedMasterSecretExtension() { + extensions.add(new ExtendedMasterSecretExtension()); + } + void addMFLExtension(int maximumPacketSize) { HelloExtension maxFragmentLength = new MaxFragmentLengthExtension(maximumPacketSize); @@ -1441,7 +1445,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange { } else { sig = getSignature(privateKey.getAlgorithm()); } - sig.initSign(privateKey); // where is the SecureRandom? + sig.initSign(privateKey, sr); updateSignature(sig, clntNonce, svrNonce); signatureBytes = sig.sign(); diff --git a/src/java.base/share/classes/sun/security/ssl/Handshaker.java b/src/java.base/share/classes/sun/security/ssl/Handshaker.java index f2813d57dd7..f2b66c8e5ff 100644 --- a/src/java.base/share/classes/sun/security/ssl/Handshaker.java +++ b/src/java.base/share/classes/sun/security/ssl/Handshaker.java @@ -30,12 +30,6 @@ import java.io.*; import java.util.*; import java.security.*; import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; -import java.security.AccessController; -import java.security.AlgorithmConstraints; -import java.security.AccessControlContext; -import java.security.PrivilegedExceptionAction; -import java.security.PrivilegedActionException; import java.util.function.BiFunction; import javax.crypto.*; @@ -225,6 +219,20 @@ abstract class Handshaker { Debug.getBooleanProperty( "jdk.tls.rejectClientInitiatedRenegotiation", false); + // To switch off the extended_master_secret extension. + static final boolean useExtendedMasterSecret; + + // Allow session resumption without Extended Master Secret extension. + static final boolean allowLegacyResumption = + Debug.getBooleanProperty("jdk.tls.allowLegacyResumption", true); + + // Allow full handshake without Extended Master Secret extension. + static final boolean allowLegacyMasterSecret = + Debug.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true); + + // Is it requested to use extended master secret extension? + boolean requestedToUseEMS = false; + // need to dispose the object when it is invalidated boolean invalidated; @@ -233,6 +241,24 @@ abstract class Handshaker { */ final boolean isDTLS; + // Is the extended_master_secret extension supported? + static { + boolean supportExtendedMasterSecret = true; + try { + KeyGenerator kg = + JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret"); + } catch (NoSuchAlgorithmException nae) { + supportExtendedMasterSecret = false; + } + + if (supportExtendedMasterSecret) { + useExtendedMasterSecret = Debug.getBooleanProperty( + "jdk.tls.useExtendedMasterSecret", true); + } else { + useExtendedMasterSecret = false; + } + } + Handshaker(SSLSocketImpl c, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, @@ -243,7 +269,7 @@ abstract class Handshaker { init(context, enabledProtocols, needCertVerify, isClient, activeProtocolVersion, isInitialHandshake, secureRenegotiation, clientVerifyData, serverVerifyData); - } + } Handshaker(SSLEngineImpl engine, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, @@ -1226,6 +1252,7 @@ abstract class Handshaker { * SHA1 hashes are of (different) constant strings, the pre-master * secret, and the nonces provided by the client and the server. */ + @SuppressWarnings("deprecation") private SecretKey calculateMasterSecret(SecretKey preMasterSecret, ProtocolVersion requestedVersion) { @@ -1276,11 +1303,37 @@ abstract class Handshaker { int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); - @SuppressWarnings("deprecation") - TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( - preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF), - clnt_random.random_bytes, svr_random.random_bytes, - prfHashAlg, prfHashLength, prfBlockSize); + TlsMasterSecretParameterSpec spec; + if (session.getUseExtendedMasterSecret()) { + // reset to use the extended master secret algorithm + masterAlg = "SunTlsExtendedMasterSecret"; + + byte[] sessionHash = null; + if (protocolVersion.useTLS12PlusSpec()) { + sessionHash = handshakeHash.getFinishedHash(); + } else { + // TLS 1.0/1.1, DTLS 1.0 + sessionHash = new byte[36]; + try { + handshakeHash.getMD5Clone().digest(sessionHash, 0, 16); + handshakeHash.getSHAClone().digest(sessionHash, 16, 20); + } catch (DigestException de) { + throw new ProviderException(de); + } + } + + spec = new TlsMasterSecretParameterSpec( + preMasterSecret, + (majorVersion & 0xFF), (minorVersion & 0xFF), + sessionHash, + prfHashAlg, prfHashLength, prfBlockSize); + } else { + spec = new TlsMasterSecretParameterSpec( + preMasterSecret, + (majorVersion & 0xFF), (minorVersion & 0xFF), + clnt_random.random_bytes, svr_random.random_bytes, + prfHashAlg, prfHashLength, prfBlockSize); + } try { KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); diff --git a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java b/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java index 82975c962f5..013b4fb7aa7 100644 --- a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java +++ b/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java @@ -93,6 +93,8 @@ final class HelloExtensions { extension = new CertStatusReqExtension(s, extlen); } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) { extension = new CertStatusReqListV2Extension(s, extlen); + } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) { + extension = new ExtendedMasterSecretExtension(s, extlen); } else { extension = new UnknownExtension(s, extlen, extType); } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 59472f18b84..c747844f1ff 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2017, 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 @@ -91,6 +91,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { private byte compressionMethod; private CipherSuite cipherSuite; private SecretKey masterSecret; + private final boolean useExtendedMasterSecret; /* * Information not part of the SSLv3 protocol spec, but used @@ -148,7 +149,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { */ private SSLSessionImpl() { this(ProtocolVersion.NONE, CipherSuite.C_NULL, null, - new SessionId(false, null), null, -1); + new SessionId(false, null), null, -1, false); } /* @@ -158,9 +159,11 @@ final class SSLSessionImpl extends ExtendedSSLSession { */ SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, Collection<SignatureAndHashAlgorithm> algorithms, - SecureRandom generator, String host, int port) { + SecureRandom generator, String host, int port, + boolean useExtendedMasterSecret) { this(protocolVersion, cipherSuite, algorithms, - new SessionId(defaultRejoinable, generator), host, port); + new SessionId(defaultRejoinable, generator), host, port, + useExtendedMasterSecret); } /* @@ -168,7 +171,8 @@ final class SSLSessionImpl extends ExtendedSSLSession { */ SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, Collection<SignatureAndHashAlgorithm> algorithms, - SessionId id, String host, int port) { + SessionId id, String host, int port, + boolean useExtendedMasterSecret) { this.protocolVersion = protocolVersion; sessionId = id; peerCerts = null; @@ -182,6 +186,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); negotiatedMaxFragLen = -1; statusResponses = null; + this.useExtendedMasterSecret = useExtendedMasterSecret; if (debug != null && Debug.isOn("session")) { System.out.println("%% Initialized: " + this); @@ -203,6 +208,10 @@ final class SSLSessionImpl extends ExtendedSSLSession { return masterSecret; } + boolean getUseExtendedMasterSecret() { + return useExtendedMasterSecret; + } + void setPeerCertificates(X509Certificate[] peer) { if (peerCerts == null) { peerCerts = peer; diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java index 85b96cd3f8b..c6555e8a84e 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java @@ -529,6 +529,27 @@ final class ServerHandshaker extends Handshaker { } } + // check out the "extended_master_secret" extension + if (useExtendedMasterSecret) { + ExtendedMasterSecretExtension extendedMasterSecretExtension = + (ExtendedMasterSecretExtension)mesg.extensions.get( + ExtensionType.EXT_EXTENDED_MASTER_SECRET); + if (extendedMasterSecretExtension != null) { + requestedToUseEMS = true; + } else if (mesg.protocolVersion.useTLS10PlusSpec()) { + if (!allowLegacyMasterSecret) { + // For full handshake, if the server receives a ClientHello + // without the extension, it SHOULD abort the handshake if + // it does not wish to interoperate with legacy clients. + // + // As if extended master extension is required for full + // handshake, it MUST be used in abbreviated handshake too. + fatalSE(Alerts.alert_handshake_failure, + "Extended Master Secret extension is required"); + } + } + } + // check the ALPN extension ALPNExtension clientHelloALPN = (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN); @@ -592,11 +613,45 @@ final class ServerHandshaker extends Handshaker { if (resumingSession) { ProtocolVersion oldVersion = previous.getProtocolVersion(); // cannot resume session with different version - if (oldVersion != protocolVersion) { + if (oldVersion != mesg.protocolVersion) { resumingSession = false; } } + if (resumingSession && useExtendedMasterSecret) { + if (requestedToUseEMS && + !previous.getUseExtendedMasterSecret()) { + // For abbreviated handshake request, If the original + // session did not use the "extended_master_secret" + // extension but the new ClientHello contains the + // extension, then the server MUST NOT perform the + // abbreviated handshake. Instead, it SHOULD continue + // with a full handshake. + resumingSession = false; + } else if (!requestedToUseEMS && + previous.getUseExtendedMasterSecret()) { + // For abbreviated handshake request, if the original + // session used the "extended_master_secret" extension + // but the new ClientHello does not contain it, the + // server MUST abort the abbreviated handshake. + fatalSE(Alerts.alert_handshake_failure, + "Missing Extended Master Secret extension " + + "on session resumption"); + } else if (!requestedToUseEMS && + !previous.getUseExtendedMasterSecret()) { + // For abbreviated handshake request, if neither the + // original session nor the new ClientHello uses the + // extension, the server SHOULD abort the handshake. + if (!allowLegacyResumption) { + fatalSE(Alerts.alert_handshake_failure, + "Missing Extended Master Secret extension " + + "on session resumption"); + } else { // Otherwise, continue with a full handshake. + resumingSession = false; + } + } + } + // cannot resume session with different server name indication if (resumingSession) { List<SNIServerName> oldServerNames = @@ -630,7 +685,7 @@ final class ServerHandshaker extends Handshaker { if (resumingSession) { CipherSuite suite = previous.getSuite(); ClientKeyExchangeService p = - ClientKeyExchangeService.find(suite.keyExchange.name); + ClientKeyExchangeService.find(suite.keyExchange.name); if (p != null) { Principal localPrincipal = previous.getLocalPrincipal(); @@ -784,7 +839,9 @@ final class ServerHandshaker extends Handshaker { session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL, getLocalSupportedSignAlgs(), sslContext.getSecureRandom(), - getHostAddressSE(), getPortSE()); + getHostAddressSE(), getPortSE(), + (requestedToUseEMS && + protocolVersion.useTLS10PlusSpec())); if (protocolVersion.useTLS12PlusSpec()) { if (peerSupportedSignAlgs != null) { @@ -886,6 +943,10 @@ final class ServerHandshaker extends Handshaker { m1.extensions.add(maxFragLenExt); } + if (session.getUseExtendedMasterSecret()) { + m1.extensions.add(new ExtendedMasterSecretExtension()); + } + StaplingParameters staplingParams = processStapling(mesg); if (staplingParams != null) { // We now can safely assert status_request[_v2] in our @@ -963,7 +1024,8 @@ final class ServerHandshaker extends Handshaker { * defined in the protocol spec are explicitly stated to require * using RSA certificates. */ - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { + if (ClientKeyExchangeService.find( + cipherSuite.keyExchange.name) != null) { // No external key exchange provider needs a cert now. } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) { if (certs == null) { diff --git a/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java b/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java index 2a5c74fa684..0f4b6b992d6 100644 --- a/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java +++ b/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -44,6 +44,7 @@ public class NoMacInitialClientHello extends DTLSOverDatagram { boolean needInvalidRecords = true; public static void main(String[] args) throws Exception { + System.setProperty("jdk.tls.useExtendedMasterSecret", "false"); NoMacInitialClientHello testCase = new NoMacInitialClientHello(); testCase.runTest(testCase); } diff --git a/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java b/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java index c00337789f0..26e30608bdf 100644 --- a/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java +++ b/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java @@ -31,45 +31,45 @@ * @bug 6956398 * @summary make ephemeral DH key match the length of the certificate key * @run main/othervm - * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1639 267 + * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267 * @run main/othervm -Djsse.enableFFDHE=false - * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75 + * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=matched - * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75 + * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=legacy - * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75 + * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=1024 - * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75 + * DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75 * * @run main/othervm -Djsse.enableFFDHE=false - * DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 229 75 + * DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 233 75 * * @run main/othervm -Djsse.enableFFDHE=false - * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1383 139 + * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=legacy - * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1319 107 + * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1323 107 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=matched - * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1639 267 + * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=1024 - * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1383 139 + * DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139 * * @run main/othervm -Djsse.enableFFDHE=false - * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 357 139 + * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=legacy - * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 293 107 + * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 297 107 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=matched - * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 357 139 + * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139 * @run main/othervm -Djsse.enableFFDHE=false * -Djdk.tls.ephemeralDHKeySize=1024 - * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 357 139 + * DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139 */ /* @@ -101,10 +101,10 @@ * Here is a summary of the record length in the test case. * * | ServerHello Series | ClientKeyExchange | ServerHello Anon - * 512-bit | 1255 bytes | 75 bytes | 229 bytes - * 768-bit | 1319 bytes | 107 bytes | 293 bytes - * 1024-bit | 1383 bytes | 139 bytes | 357 bytes - * 2048-bit | 1639 bytes | 267 bytes | 357 bytes + * 512-bit | 1259 bytes | 75 bytes | 233 bytes + * 768-bit | 1323 bytes | 107 bytes | 297 bytes + * 1024-bit | 1387 bytes | 139 bytes | 361 bytes + * 2048-bit | 1643 bytes | 267 bytes | 361 bytes */ import javax.net.ssl.*; diff --git a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java b/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java index 89c50b26027..375a060e39b 100644 --- a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java +++ b/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -38,6 +38,6 @@ public class OptimalListSize { public static void main(String[] args) throws Throwable { OptimalCapacity.ofArrayList( Class.forName("sun.security.ssl.ExtensionType"), - "knownExtensions", 15); + "knownExtensions", 16); } } From eb62b5e51e26d3549a5dec7f6563debd27147f2a Mon Sep 17 00:00:00 2001 From: Daniel Fuchs <dfuchs@openjdk.org> Date: Fri, 8 Dec 2017 17:40:57 +0000 Subject: [PATCH 154/165] 8187073: The java.util.logging.Level.findLevel() will not correctly find a Level by it's int value Reviewed-by: rriggs --- .../classes/java/util/logging/Level.java | 15 +++++----- .../java/util/logging/Level/CustomLevel.java | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/java.logging/share/classes/java/util/logging/Level.java b/src/java.logging/share/classes/java/util/logging/Level.java index 2af2c6d83d6..dbc27124d33 100644 --- a/src/java.logging/share/classes/java/util/logging/Level.java +++ b/src/java.logging/share/classes/java/util/logging/Level.java @@ -389,14 +389,15 @@ public class Level implements java.io.Serializable { try { int x = Integer.parseInt(name); level = KnownLevel.findByValue(x, KnownLevel::mirrored); - if (!level.isPresent()) { - // add new Level - Level levelObject = new Level(name, x); - // There's no need to use a reachability fence here because - // KnownLevel keeps a strong reference on the level when - // level.getClass() == Level.class. - return KnownLevel.findByValue(x, KnownLevel::mirrored).get(); + if (level.isPresent()) { + return level.get(); } + // add new Level + Level levelObject = new Level(name, x); + // There's no need to use a reachability fence here because + // KnownLevel keeps a strong reference on the level when + // level.getClass() == Level.class. + return KnownLevel.findByValue(x, KnownLevel::mirrored).get(); } catch (NumberFormatException ex) { // Not an integer. // Drop through. diff --git a/test/jdk/java/util/logging/Level/CustomLevel.java b/test/jdk/java/util/logging/Level/CustomLevel.java index 8045831d33c..0b74148a07d 100644 --- a/test/jdk/java/util/logging/Level/CustomLevel.java +++ b/test/jdk/java/util/logging/Level/CustomLevel.java @@ -22,6 +22,8 @@ */ import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.management.PlatformLoggingMXBean; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; @@ -35,9 +37,11 @@ import java.util.logging.*; /* * @test - * @bug 8026027 6543126 + * @bug 8026027 6543126 8187073 + * @modules java.logging + * java.management * @summary Test Level.parse to look up custom levels by name and its - * localized name + * localized name, as well as severity. * * @run main/othervm CustomLevel */ @@ -73,6 +77,8 @@ public class CustomLevel extends Level { public static void main(String[] args) throws Exception { setupCustomLevels(); setUpCustomLevelsOtherLoader(); + PlatformLoggingMXBean mxbean = ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class); + Logger logger = Logger.getLogger("foo.bar"); // Level.parse will return the custom Level instance for (Level level : levels) { @@ -96,8 +102,27 @@ public class CustomLevel extends Level { + l.getClass() + " for " + localizedName + " in " + rb.getBaseBundleName()); } + l = Level.parse(String.valueOf(level.intValue())); + System.out.println("Level.parse(" + level.intValue() + ") returns " + l); + if (l != level) { + if (l == null || l.intValue() != level.intValue()) { + throw new RuntimeException("Unexpected level " + l + + (l == null ? "" : (" " + l.getClass())) + + " for " + level.intValue()); + } + } + mxbean.setLoggerLevel(logger.getName(), String.valueOf(level.intValue())); + Level l2 = logger.getLevel(); + if (l2 != level) { + if (l2 == null || l2.intValue() != level.intValue()) { + throw new RuntimeException("Unexpected level " + l2 + + (l2 == null ? "" : (" " + l2.getClass())) + + " for " + level.intValue()); + } + } } + final long otherLevelCount = levels.stream() .filter(CustomLevel::isCustomLoader) .count(); From 99332f8fe03ef3007eee546f095f56094b8921a2 Mon Sep 17 00:00:00 2001 From: Joe Darcy <darcy@openjdk.org> Date: Fri, 8 Dec 2017 11:05:42 -0800 Subject: [PATCH 155/165] 8193194: Update javax.lang.model.util visitors for 10 Reviewed-by: jjg --- .../model/util/AbstractAnnotationValueVisitor9.java | 6 +++--- .../javax/lang/model/util/AbstractElementVisitor9.java | 4 ++-- .../javax/lang/model/util/AbstractTypeVisitor9.java | 10 +++++----- .../javax/lang/model/util/ElementKindVisitor9.java | 5 +++-- .../classes/javax/lang/model/util/ElementScanner9.java | 5 +++-- .../lang/model/util/SimpleAnnotationValueVisitor9.java | 7 ++++--- .../javax/lang/model/util/SimpleElementVisitor9.java | 4 ++-- .../javax/lang/model/util/SimpleTypeVisitor9.java | 5 +++-- .../javax/lang/model/util/TypeKindVisitor9.java | 5 +++-- 9 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java index 46b2875b42d..c05736913e7 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -32,7 +32,7 @@ import javax.annotation.processing.SupportedSourceVersion; /** * A skeletal visitor for annotation values with default behavior * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9} - * source version. + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. * * <p> <b>WARNING:</b> The {@code AnnotationValueVisitor} interface * implemented by this class may have methods added to it in the @@ -59,7 +59,7 @@ import javax.annotation.processing.SupportedSourceVersion; * @see AbstractAnnotationValueVisitor8 * @since 9 */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public abstract class AbstractAnnotationValueVisitor9<R, P> extends AbstractAnnotationValueVisitor8<R, P> { /** diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java index 9adc95b49cb..556c897c950 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java @@ -34,7 +34,7 @@ import static javax.lang.model.SourceVersion.*; /** * A skeletal visitor of program elements with default behavior * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9} - * source version. + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. * * <p> <b>WARNING:</b> The {@code ElementVisitor} interface * implemented by this class may have methods added to it in the @@ -65,7 +65,7 @@ import static javax.lang.model.SourceVersion.*; * @since 9 * @spec JPMS */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public abstract class AbstractElementVisitor9<R, P> extends AbstractElementVisitor8<R, P> { /** * Constructor for concrete subclasses to call. diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java index d268e867d0a..b38a032ada7 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -27,13 +27,13 @@ package javax.lang.model.util; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.type.*; - +import javax.lang.model.SourceVersion; import static javax.lang.model.SourceVersion.*; /** * A skeletal visitor of types with default behavior appropriate for - * the {@link javax.lang.model.SourceVersion#RELEASE_9 RELEASE_9} - * source version. + * the {@link SourceVersion#RELEASE_9 RELEASE_9} + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. * * <p> <b>WARNING:</b> The {@code TypeVisitor} interface implemented * by this class may have methods added to it in the future to @@ -63,7 +63,7 @@ import static javax.lang.model.SourceVersion.*; * @see AbstractTypeVisitor8 * @since 9 */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public abstract class AbstractTypeVisitor9<R, P> extends AbstractTypeVisitor8<R, P> { /** * Constructor for concrete subclasses to call. diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java index 9237cc11361..7fc8140e8ec 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java @@ -33,7 +33,8 @@ import javax.lang.model.SourceVersion; /** * A visitor of program elements based on their {@linkplain * ElementKind kind} with default behavior appropriate for the {@link - * SourceVersion#RELEASE_9 RELEASE_9} source version. For {@linkplain + * SourceVersion#RELEASE_9 RELEASE_9} and {@link + * SourceVersion#RELEASE_10 RELEASE_10} source versions. For {@linkplain * Element elements} <code><i>Xyz</i></code> that may have more than one * kind, the <code>visit<i>Xyz</i></code> methods in this class delegate * to the <code>visit<i>Xyz</i>As<i>Kind</i></code> method corresponding to the @@ -77,7 +78,7 @@ import javax.lang.model.SourceVersion; * @since 9 * @spec JPMS */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class ElementKindVisitor9<R, P> extends ElementKindVisitor8<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java index dba0a96e383..a5e61046646 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java @@ -34,7 +34,8 @@ import static javax.lang.model.SourceVersion.*; /** * A scanning visitor of program elements with default behavior * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9} - * source version. The <code>visit<i>Xyz</i></code> methods in this + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. + * The <code>visit<i>Xyz</i></code> methods in this * class scan their component elements by calling {@code scan} on * their {@linkplain Element#getEnclosedElements enclosed elements}, * {@linkplain ExecutableElement#getParameters parameters}, etc., as @@ -90,7 +91,7 @@ import static javax.lang.model.SourceVersion.*; * @since 9 * @spec JPMS */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class ElementScanner9<R, P> extends ElementScanner8<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java index d5cf8e0cfd0..adb172e092e 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, 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 @@ -32,7 +32,8 @@ import static javax.lang.model.SourceVersion.*; /** * A simple visitor for annotation values with default behavior * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9} - * source version. Visit methods call {@link #defaultAction + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. + * Visit methods call {@link #defaultAction * defaultAction} passing their arguments to {@code defaultAction}'s * corresponding parameters. * @@ -66,7 +67,7 @@ import static javax.lang.model.SourceVersion.*; * @see SimpleAnnotationValueVisitor8 * @since 9 */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class SimpleAnnotationValueVisitor9<R, P> extends SimpleAnnotationValueVisitor8<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java index 5c62d078a4c..a73988fb3b4 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java @@ -33,7 +33,7 @@ import static javax.lang.model.SourceVersion.*; /** * A simple visitor of program elements with default behavior * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9} - * source version. + * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. * * Visit methods corresponding to {@code RELEASE_9} and earlier * language constructs call {@link #defaultAction defaultAction}, @@ -73,7 +73,7 @@ import static javax.lang.model.SourceVersion.*; * @since 9 * @spec JPMS */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class SimpleElementVisitor9<R, P> extends SimpleElementVisitor8<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java index 566888c871a..41521e20509 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java @@ -32,7 +32,8 @@ import static javax.lang.model.SourceVersion.*; /** * A simple visitor of types with default behavior appropriate for the - * {@link SourceVersion#RELEASE_9 RELEASE_9} source version. + * {@link SourceVersion#RELEASE_9 RELEASE_9} and + * {@link SourceVersion#RELEASE_10 RELEASE_10} source versions. * * Visit methods corresponding to {@code RELEASE_9} and earlier * language constructs call {@link #defaultAction defaultAction}, @@ -71,7 +72,7 @@ import static javax.lang.model.SourceVersion.*; * @see SimpleTypeVisitor7 * @since 9 */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class SimpleTypeVisitor9<R, P> extends SimpleTypeVisitor8<R, P> { /** * Constructor for concrete subclasses; uses {@code null} for the diff --git a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java index 5e5c52e5a80..321a7fa311a 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java @@ -33,7 +33,8 @@ import static javax.lang.model.SourceVersion.*; /** * A visitor of types based on their {@linkplain TypeKind kind} with * default behavior appropriate for the {@link SourceVersion#RELEASE_9 - * RELEASE_9} source version. For {@linkplain + * RELEASE_9} and {@link SourceVersion#RELEASE_10 RELEASE_10} source + * versions. For {@linkplain * TypeMirror types} <code><i>Xyz</i></code> that may have more than one * kind, the <code>visit<i>Xyz</i></code> methods in this class delegate * to the <code>visit<i>Xyz</i>As<i>Kind</i></code> method corresponding to the @@ -74,7 +75,7 @@ import static javax.lang.model.SourceVersion.*; * @see TypeKindVisitor8 * @since 9 */ -@SupportedSourceVersion(RELEASE_9) +@SupportedSourceVersion(RELEASE_10) public class TypeKindVisitor9<R, P> extends TypeKindVisitor8<R, P> { /** * Constructor for concrete subclasses to call; uses {@code null} From 643da5ac9cb1919b3a849ee38d2093cb7f61e7c0 Mon Sep 17 00:00:00 2001 From: Igor Ignatyev <iignatyev@openjdk.org> Date: Fri, 8 Dec 2017 11:03:37 -0800 Subject: [PATCH 156/165] 8181118: update java/time tests to use RandomFactory from the top level testlibrary Reviewed-by: rriggs --- test/jdk/TEST.ROOT | 2 +- test/jdk/java/time/tck/TEST.properties | 4 +- test/jdk/java/time/test/TEST.properties | 4 +- .../format/TestZoneTextPrinterParser.java | 4 +- .../jdk/testlibrary/RandomFactory.java | 108 ------------------ 5 files changed, 7 insertions(+), 115 deletions(-) delete mode 100644 test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 2f00dbb7f82..0aceb5f4710 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -39,7 +39,7 @@ requires.properties= \ vm.cds # Minimum jtreg version -requiredVersion=4.2 b08 +requiredVersion=4.2 b09 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/java/time/tck/TEST.properties b/test/jdk/java/time/tck/TEST.properties index 2374566eefb..a9a165cdb35 100644 --- a/test/jdk/java/time/tck/TEST.properties +++ b/test/jdk/java/time/tck/TEST.properties @@ -1,5 +1,5 @@ # java.time tests use TestNG TestNG.dirs = .. othervm.dirs = java/time/chrono -lib.dirs = ../../../lib/testlibrary -lib.build = jdk.testlibrary.RandomFactory +lib.dirs = /test/lib +lib.build = jdk.test.lib.RandomFactory diff --git a/test/jdk/java/time/test/TEST.properties b/test/jdk/java/time/test/TEST.properties index 0a36c095408..98f1a0011ac 100644 --- a/test/jdk/java/time/test/TEST.properties +++ b/test/jdk/java/time/test/TEST.properties @@ -1,5 +1,5 @@ # java.time tests use TestNG TestNG.dirs = .. othervm.dirs = java/time/chrono java/time/format -lib.dirs = ../../../lib/testlibrary -lib.build = jdk.testlibrary.RandomFactory +lib.dirs = /test/lib +lib.build = jdk.test.lib.RandomFactory diff --git a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java index 905f77da58a..9739ad6bdbe 100644 --- a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java +++ b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, 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 @@ -42,7 +42,7 @@ import java.util.Locale; import java.util.Random; import java.util.Set; import java.util.TimeZone; -import jdk.testlibrary.RandomFactory; +import jdk.test.lib.RandomFactory; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; diff --git a/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java b/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java deleted file mode 100644 index 4cac61c1a0c..00000000000 --- a/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2015, 2017, 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. - */ - -package jdk.testlibrary; - -import java.util.Random; -import java.util.SplittableRandom; - -/** - * Factory class which generates and prints to STDOUT a long-valued seed - * for use in initializing a PRNG. An instance of {@code Random} or - * {@code SplittableRandom} may likewise be obtained. - * - * @deprecated This class is deprecated. Use the one from - * {@code <root>/test/lib/jdk/test/lib} - * - */ -@Deprecated -public class RandomFactory { - /** - * Attempt to obtain the seed from the value of the "seed" property. - * @return The seed or {@code null} if the "seed" property was not set or - * could not be parsed. - */ - private static Long getSystemSeed() { - Long seed = null; - try { - // note that Long.valueOf(null) also throws a - // NumberFormatException so if the property is undefined this - // will still work correctly - seed = Long.valueOf(System.getProperty("seed")); - } catch (NumberFormatException e) { - // do nothing: seed is still null - } - - return seed; - } - - /** - * Obtain a seed from an independent PRNG. - * - * @return A random seed. - */ - private static long getRandomSeed() { - return new Random().nextLong(); - } - - /** - * Obtain and print to STDOUT a seed appropriate for initializing a PRNG. - * If the system property "seed" is set and has value which may be correctly - * parsed it is used, otherwise a seed is generated using an independent - * PRNG. - * - * @return The seed. - */ - public static long getSeed() { - Long seed = getSystemSeed(); - if (seed == null) { - seed = getRandomSeed(); - } - System.out.println("Seed from RandomFactory = "+seed+"L"); - return seed; - } - - /** - * Obtain and print to STDOUT a seed and use it to initialize a new - * {@code Random} instance which is returned. If the system - * property "seed" is set and has value which may be correctly parsed it - * is used, otherwise a seed is generated using an independent PRNG. - * - * @return The {@code Random} instance. - */ - public static Random getRandom() { - return new Random(getSeed()); - } - - /** - * Obtain and print to STDOUT a seed and use it to initialize a new - * {@code SplittableRandom} instance which is returned. If the system - * property "seed" is set and has value which may be correctly parsed it - * is used, otherwise a seed is generated using an independent PRNG. - * - * @return The {@code SplittableRandom} instance. - */ - public static SplittableRandom getSplittableRandom() { - return new SplittableRandom(getSeed()); - } -} From a820e5eaa8789ee0594661aa3fbd1e319f1d87c6 Mon Sep 17 00:00:00 2001 From: Brent Christian <bchristi@openjdk.org> Date: Fri, 8 Dec 2017 13:04:43 -0800 Subject: [PATCH 157/165] 8193271: ProblemList tools/launcher/TestXcheckJNIWarnings.java Reviewed-by: darcy --- test/jdk/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index a8d5eee330b..a29f5df566c 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -257,6 +257,7 @@ javax/sound/sampled/Mixers/DisabledAssertionCrash.java 7067310 generic-all tools/pack200/CommandLineTests.java 8059906 generic-all tools/launcher/FXLauncherTest.java 8068049 linux-all,macosx-all +tools/launcher/TestXcheckJNIWarnings.java 8190984 solaris-all tools/jimage/JImageExtractTest.java 8170120 generic-all tools/jimage/JImageListTest.java 8170120 generic-all From ed69a7db9c4546807f1d6d31a66f41b4a5488880 Mon Sep 17 00:00:00 2001 From: Martin Buchholz <martin@openjdk.org> Date: Sun, 3 Dec 2017 13:06:51 -0800 Subject: [PATCH 158/165] 8192935: Fix EnumSet's SerializationProxy javadoc Reviewed-by: smarks, rriggs --- .../share/classes/java/util/EnumSet.java | 38 ++++++++++++++----- test/jdk/java/util/EnumSet/BogusEnumSet.java | 4 +- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/java/util/EnumSet.java b/src/java.base/share/classes/java/util/EnumSet.java index 0adeab410b0..88c3cb9056a 100644 --- a/src/java.base/share/classes/java/util/EnumSet.java +++ b/src/java.base/share/classes/java/util/EnumSet.java @@ -75,7 +75,6 @@ import jdk.internal.misc.SharedSecrets; * @author Josh Bloch * @since 1.5 * @see EnumMap - * @serial exclude */ @SuppressWarnings("serial") // No serialVersionUID due to usage of // serial proxy pattern @@ -85,12 +84,12 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> /** * The class of all the elements of this set. */ - final Class<E> elementType; + final transient Class<E> elementType; /** - * All of the values comprising T. (Cached for performance.) + * All of the values comprising E. (Cached for performance.) */ - final Enum<?>[] universe; + final transient Enum<?>[] universe; EnumSet(Class<E>elementType, Enum<?>[] universe) { this.elementType = elementType; @@ -416,7 +415,7 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> * * @serial include */ - private static class SerializationProxy <E extends Enum<E>> + private static class SerializationProxy<E extends Enum<E>> implements java.io.Serializable { @@ -441,10 +440,18 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY); } - // instead of cast to E, we should perhaps use elementType.cast() - // to avoid injection of forged stream, but it will slow the implementation + /** + * Returns an {@code EnumSet} object with initial state + * held by this proxy. + * + * @return a {@code EnumSet} object with initial state + * held by this proxy + */ @SuppressWarnings("unchecked") private Object readResolve() { + // instead of cast to E, we should perhaps use elementType.cast() + // to avoid injection of forged stream, but it will slow the + // implementation EnumSet<E> result = EnumSet.noneOf(elementType); for (Enum<?> e : elements) result.add((E)e); @@ -454,13 +461,24 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> private static final long serialVersionUID = 362491234563181265L; } + /** + * Returns a + * <a href="../../serialized-form.html#java.util.EnumSet.SerializationProxy"> + * SerializationProxy</a> + * representing the state of this instance. + * + * @return a {@link SerializationProxy} + * representing the state of this instance + */ Object writeReplace() { return new SerializationProxy<>(this); } - // readObject method for the serialization proxy pattern - // See Effective Java, Second Ed., Item 78. - private void readObject(java.io.ObjectInputStream stream) + /** + * @param s the stream + * @throws java.io.InvalidObjectException always + */ + private void readObject(java.io.ObjectInputStream s) throws java.io.InvalidObjectException { throw new java.io.InvalidObjectException("Proxy required"); } diff --git a/test/jdk/java/util/EnumSet/BogusEnumSet.java b/test/jdk/java/util/EnumSet/BogusEnumSet.java index 6ba2840b149..c24bbfbbbee 100644 --- a/test/jdk/java/util/EnumSet/BogusEnumSet.java +++ b/test/jdk/java/util/EnumSet/BogusEnumSet.java @@ -34,7 +34,7 @@ public class BogusEnumSet { public static void main(String[] args) throws Throwable { // This test depends on the current serialVersionUID of EnumSet, // which may change if the EnumSet class is modified. - // The current value is 4168005130090799668L = 0x39D7BA9531116234L + // The current value is -2409567991088730183L = 0xde8f7eadb5012fb9L // If the value changes, it will have to be patched into the // serialized byte stream below at the location noted. byte[] serializedForm = { @@ -47,7 +47,7 @@ public class BogusEnumSet { 0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, // EnumSet's serialVersionUID is the following eight bytes (big-endian) - 0x39, (byte)0xd7, (byte)0xba, (byte)0x95, 0x31, 0x11, 0x62, 0x34, + (byte)0xde, (byte)0x8f, 0x7e, (byte)0xad, (byte)0xb5, (byte)0x01, 0x2f, (byte)0xb9, 0x2, 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x74, 0x0, 0x11, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x43, 0x6c, 0x61, 0x73, From 0b3b384a27cbf4495634d32735e8ad9aaaf26d8a Mon Sep 17 00:00:00 2001 From: Doug Lea <dl@openjdk.org> Date: Fri, 8 Dec 2017 15:22:58 -0800 Subject: [PATCH 159/165] 8193174: SubmissionPublisher invokes the Subscriber's onComplete before all of its submitted items have been published Reviewed-by: martin, psandoz, chegar --- .../util/concurrent/SubmissionPublisher.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java index 81f6c6c0825..713c3fbd395 100644 --- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java +++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java @@ -1252,18 +1252,20 @@ public class SubmissionPublisher<T> implements Publisher<T>, head = h += taken; d = subtractDemand(taken); } - else if ((empty = (t == h)) && (c & COMPLETE) != 0) { - closeOnComplete(s); // end of stream - break; - } else if ((d = demand) == 0L && (c & REQS) != 0) weakCasCtl(c, c & ~REQS); // exhausted demand else if (d != 0L && (c & REQS) == 0) weakCasCtl(c, c | REQS); // new demand - else if (t == (t = tail) && (empty || d == 0L)) { - int bit = ((c & ACTIVE) != 0) ? ACTIVE : RUN; - if (weakCasCtl(c, c & ~bit) && bit == RUN) - break; // un-keep-alive or exit + else if (t == (t = tail)) { // stability check + if ((empty = (t == h)) && (c & COMPLETE) != 0) { + closeOnComplete(s); // end of stream + break; + } + else if (empty || d == 0L) { + int bit = ((c & ACTIVE) != 0) ? ACTIVE : RUN; + if (weakCasCtl(c, c & ~bit) && bit == RUN) + break; // un-keep-alive or exit + } } } } From 00d1900dc994bb745654c93bdca95ad3e579f720 Mon Sep 17 00:00:00 2001 From: Doug Lea <dl@openjdk.org> Date: Fri, 8 Dec 2017 15:26:56 -0800 Subject: [PATCH 160/165] 8192943: Optimize atomic accumulators using VarHandle getAndSet Reviewed-by: martin, psandoz, chegar --- .../concurrent/atomic/DoubleAccumulator.java | 48 +++++++++---------- .../util/concurrent/atomic/DoubleAdder.java | 46 ++++++++---------- .../concurrent/atomic/LongAccumulator.java | 48 +++++++++---------- .../util/concurrent/atomic/LongAdder.java | 45 ++++++++--------- .../util/concurrent/atomic/Striped64.java | 43 ++++++++++------- 5 files changed, 113 insertions(+), 117 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java index 20c9efa5f2e..180073006f9 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java @@ -105,20 +105,20 @@ public class DoubleAccumulator extends Striped64 implements Serializable { * @param x the value */ public void accumulate(double x) { - Cell[] as; long b, v, r; int m; Cell a; - if ((as = cells) != null + Cell[] cs; long b, v, r; int m; Cell c; + if ((cs = cells) != null || ((r = doubleToRawLongBits (function.applyAsDouble(longBitsToDouble(b = base), x))) != b && !casBase(b, r))) { boolean uncontended = true; - if (as == null - || (m = as.length - 1) < 0 - || (a = as[getProbe() & m]) == null + if (cs == null + || (m = cs.length - 1) < 0 + || (c = cs[getProbe() & m]) == null || !(uncontended = ((r = doubleToRawLongBits (function.applyAsDouble - (longBitsToDouble(v = a.value), x))) == v) - || a.cas(v, r))) + (longBitsToDouble(v = c.value), x))) == v) + || c.cas(v, r))) doubleAccumulate(x, function, uncontended); } } @@ -133,13 +133,13 @@ public class DoubleAccumulator extends Striped64 implements Serializable { * @return the current value */ public double get() { - Cell[] as = cells; + Cell[] cs = cells; double result = longBitsToDouble(base); - if (as != null) { - for (Cell a : as) - if (a != null) + if (cs != null) { + for (Cell c : cs) + if (c != null) result = function.applyAsDouble - (result, longBitsToDouble(a.value)); + (result, longBitsToDouble(c.value)); } return result; } @@ -153,12 +153,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable { * updating. */ public void reset() { - Cell[] as = cells; + Cell[] cs = cells; base = identity; - if (as != null) { - for (Cell a : as) - if (a != null) - a.reset(identity); + if (cs != null) { + for (Cell c : cs) + if (c != null) + c.reset(identity); } } @@ -173,14 +173,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable { * @return the value before reset */ public double getThenReset() { - Cell[] as = cells; - double result = longBitsToDouble(base); - base = identity; - if (as != null) { - for (Cell a : as) { - if (a != null) { - double v = longBitsToDouble(a.value); - a.reset(identity); + Cell[] cs = cells; + double result = longBitsToDouble(getAndSetBase(identity)); + if (cs != null) { + for (Cell c : cs) { + if (c != null) { + double v = longBitsToDouble(c.getAndSet(identity)); result = function.applyAsDouble(result, v); } } diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java index 57bd8c581a4..3f3343f5c6f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java @@ -87,15 +87,15 @@ public class DoubleAdder extends Striped64 implements Serializable { * @param x the value to add */ public void add(double x) { - Cell[] as; long b, v; int m; Cell a; - if ((as = cells) != null || + Cell[] cs; long b, v; int m; Cell c; + if ((cs = cells) != null || !casBase(b = base, Double.doubleToRawLongBits (Double.longBitsToDouble(b) + x))) { boolean uncontended = true; - if (as == null || (m = as.length - 1) < 0 || - (a = as[getProbe() & m]) == null || - !(uncontended = a.cas(v = a.value, + if (cs == null || (m = cs.length - 1) < 0 || + (c = cs[getProbe() & m]) == null || + !(uncontended = c.cas(v = c.value, Double.doubleToRawLongBits (Double.longBitsToDouble(v) + x)))) doubleAccumulate(x, null, uncontended); @@ -115,12 +115,12 @@ public class DoubleAdder extends Striped64 implements Serializable { * @return the sum */ public double sum() { - Cell[] as = cells; + Cell[] cs = cells; double sum = Double.longBitsToDouble(base); - if (as != null) { - for (Cell a : as) - if (a != null) - sum += Double.longBitsToDouble(a.value); + if (cs != null) { + for (Cell c : cs) + if (c != null) + sum += Double.longBitsToDouble(c.value); } return sum; } @@ -133,12 +133,12 @@ public class DoubleAdder extends Striped64 implements Serializable { * known that no threads are concurrently updating. */ public void reset() { - Cell[] as = cells; + Cell[] cs = cells; base = 0L; // relies on fact that double 0 must have same rep as long - if (as != null) { - for (Cell a : as) - if (a != null) - a.reset(); + if (cs != null) { + for (Cell c : cs) + if (c != null) + c.reset(); } } @@ -153,16 +153,12 @@ public class DoubleAdder extends Striped64 implements Serializable { * @return the sum */ public double sumThenReset() { - Cell[] as = cells; - double sum = Double.longBitsToDouble(base); - base = 0L; - if (as != null) { - for (Cell a : as) { - if (a != null) { - long v = a.value; - a.reset(); - sum += Double.longBitsToDouble(v); - } + Cell[] cs = cells; + double sum = Double.longBitsToDouble(getAndSetBase(0L)); + if (cs != null) { + for (Cell c : cs) { + if (c != null) + sum += Double.longBitsToDouble(c.getAndSet(0L)); } } return sum; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java b/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java index 71fa3e40651..ed9b4bac45f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java @@ -103,17 +103,17 @@ public class LongAccumulator extends Striped64 implements Serializable { * @param x the value */ public void accumulate(long x) { - Cell[] as; long b, v, r; int m; Cell a; - if ((as = cells) != null + Cell[] cs; long b, v, r; int m; Cell c; + if ((cs = cells) != null || ((r = function.applyAsLong(b = base, x)) != b && !casBase(b, r))) { boolean uncontended = true; - if (as == null - || (m = as.length - 1) < 0 - || (a = as[getProbe() & m]) == null + if (cs == null + || (m = cs.length - 1) < 0 + || (c = cs[getProbe() & m]) == null || !(uncontended = - (r = function.applyAsLong(v = a.value, x)) == v - || a.cas(v, r))) + (r = function.applyAsLong(v = c.value, x)) == v + || c.cas(v, r))) longAccumulate(x, function, uncontended); } } @@ -128,12 +128,12 @@ public class LongAccumulator extends Striped64 implements Serializable { * @return the current value */ public long get() { - Cell[] as = cells; + Cell[] cs = cells; long result = base; - if (as != null) { - for (Cell a : as) - if (a != null) - result = function.applyAsLong(result, a.value); + if (cs != null) { + for (Cell c : cs) + if (c != null) + result = function.applyAsLong(result, c.value); } return result; } @@ -147,12 +147,12 @@ public class LongAccumulator extends Striped64 implements Serializable { * updating. */ public void reset() { - Cell[] as = cells; + Cell[] cs = cells; base = identity; - if (as != null) { - for (Cell a : as) - if (a != null) - a.reset(identity); + if (cs != null) { + for (Cell c : cs) + if (c != null) + c.reset(identity); } } @@ -167,14 +167,12 @@ public class LongAccumulator extends Striped64 implements Serializable { * @return the value before reset */ public long getThenReset() { - Cell[] as = cells; - long result = base; - base = identity; - if (as != null) { - for (Cell a : as) { - if (a != null) { - long v = a.value; - a.reset(identity); + Cell[] cs = cells; + long result = getAndSetBase(identity); + if (cs != null) { + for (Cell c : cs) { + if (c != null) { + long v = c.getAndSet(identity); result = function.applyAsLong(result, v); } } diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java b/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java index 0248fad9c5f..e0fdc18c7d7 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java @@ -83,12 +83,12 @@ public class LongAdder extends Striped64 implements Serializable { * @param x the value to add */ public void add(long x) { - Cell[] as; long b, v; int m; Cell a; - if ((as = cells) != null || !casBase(b = base, b + x)) { + Cell[] cs; long b, v; int m; Cell c; + if ((cs = cells) != null || !casBase(b = base, b + x)) { boolean uncontended = true; - if (as == null || (m = as.length - 1) < 0 || - (a = as[getProbe() & m]) == null || - !(uncontended = a.cas(v = a.value, v + x))) + if (cs == null || (m = cs.length - 1) < 0 || + (c = cs[getProbe() & m]) == null || + !(uncontended = c.cas(v = c.value, v + x))) longAccumulate(x, null, uncontended); } } @@ -117,12 +117,12 @@ public class LongAdder extends Striped64 implements Serializable { * @return the sum */ public long sum() { - Cell[] as = cells; + Cell[] cs = cells; long sum = base; - if (as != null) { - for (Cell a : as) - if (a != null) - sum += a.value; + if (cs != null) { + for (Cell c : cs) + if (c != null) + sum += c.value; } return sum; } @@ -135,12 +135,12 @@ public class LongAdder extends Striped64 implements Serializable { * known that no threads are concurrently updating. */ public void reset() { - Cell[] as = cells; + Cell[] cs = cells; base = 0L; - if (as != null) { - for (Cell a : as) - if (a != null) - a.reset(); + if (cs != null) { + for (Cell c : cs) + if (c != null) + c.reset(); } } @@ -155,15 +155,12 @@ public class LongAdder extends Striped64 implements Serializable { * @return the sum */ public long sumThenReset() { - Cell[] as = cells; - long sum = base; - base = 0L; - if (as != null) { - for (Cell a : as) { - if (a != null) { - sum += a.value; - a.reset(); - } + Cell[] cs = cells; + long sum = getAndSetBase(0L); + if (cs != null) { + for (Cell c : cs) { + if (c != null) + sum += c.getAndSet(0L); } } return sum; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java index 95030782a71..ddb1e53a3d0 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java @@ -133,6 +133,9 @@ abstract class Striped64 extends Number { final void reset(long identity) { VALUE.setVolatile(this, identity); } + final long getAndSet(long val) { + return (long)VALUE.getAndSet(this, val); + } // VarHandle mechanics private static final VarHandle VALUE; @@ -178,6 +181,10 @@ abstract class Striped64 extends Number { return BASE.compareAndSet(this, cmp, val); } + final long getAndSetBase(long val) { + return (long)BASE.getAndSet(this, val); + } + /** * CASes the cellsBusy field from 0 to 1 to acquire lock. */ @@ -228,9 +235,9 @@ abstract class Striped64 extends Number { } boolean collide = false; // True if last slot nonempty done: for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { + Cell[] cs; Cell c; int n; long v; + if ((cs = cells) != null && (n = cs.length) > 0) { + if ((c = cs[(n - 1) & h]) == null) { if (cellsBusy == 0) { // Try to attach new Cell Cell r = new Cell(x); // Optimistically create if (cellsBusy == 0 && casCellsBusy()) { @@ -252,17 +259,17 @@ abstract class Striped64 extends Number { } else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, + else if (c.cas(v = c.value, (fn == null) ? v + x : fn.applyAsLong(v, x))) break; - else if (n >= NCPU || cells != as) + else if (n >= NCPU || cells != cs) collide = false; // At max size or stale else if (!collide) collide = true; else if (cellsBusy == 0 && casCellsBusy()) { try { - if (cells == as) // Expand table unless stale - cells = Arrays.copyOf(as, n << 1); + if (cells == cs) // Expand table unless stale + cells = Arrays.copyOf(cs, n << 1); } finally { cellsBusy = 0; } @@ -271,9 +278,9 @@ abstract class Striped64 extends Number { } h = advanceProbe(h); } - else if (cellsBusy == 0 && cells == as && casCellsBusy()) { + else if (cellsBusy == 0 && cells == cs && casCellsBusy()) { try { // Initialize table - if (cells == as) { + if (cells == cs) { Cell[] rs = new Cell[2]; rs[h & 1] = new Cell(x); cells = rs; @@ -312,9 +319,9 @@ abstract class Striped64 extends Number { } boolean collide = false; // True if last slot nonempty done: for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { + Cell[] cs; Cell c; int n; long v; + if ((cs = cells) != null && (n = cs.length) > 0) { + if ((c = cs[(n - 1) & h]) == null) { if (cellsBusy == 0) { // Try to attach new Cell Cell r = new Cell(Double.doubleToRawLongBits(x)); if (cellsBusy == 0 && casCellsBusy()) { @@ -336,16 +343,16 @@ abstract class Striped64 extends Number { } else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, apply(fn, v, x))) + else if (c.cas(v = c.value, apply(fn, v, x))) break; - else if (n >= NCPU || cells != as) + else if (n >= NCPU || cells != cs) collide = false; // At max size or stale else if (!collide) collide = true; else if (cellsBusy == 0 && casCellsBusy()) { try { - if (cells == as) // Expand table unless stale - cells = Arrays.copyOf(as, n << 1); + if (cells == cs) // Expand table unless stale + cells = Arrays.copyOf(cs, n << 1); } finally { cellsBusy = 0; } @@ -354,9 +361,9 @@ abstract class Striped64 extends Number { } h = advanceProbe(h); } - else if (cellsBusy == 0 && cells == as && casCellsBusy()) { + else if (cellsBusy == 0 && cells == cs && casCellsBusy()) { try { // Initialize table - if (cells == as) { + if (cells == cs) { Cell[] rs = new Cell[2]; rs[h & 1] = new Cell(Double.doubleToRawLongBits(x)); cells = rs; From 71a866fe0cd6a708eae6a3f21bf06fb36c85e652 Mon Sep 17 00:00:00 2001 From: Doug Lea <dl@openjdk.org> Date: Fri, 8 Dec 2017 15:30:53 -0800 Subject: [PATCH 161/165] 8192944: Miscellaneous changes imported from jsr166 CVS 2017-12-08 Reviewed-by: martin, psandoz, chegar --- .../util/concurrent/CountedCompleter.java | 2 +- .../java/util/concurrent/ForkJoinTask.java | 233 +++++++++--------- .../tck/ExecutorCompletionServiceTest.java | 36 ++- 3 files changed, 139 insertions(+), 132 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java b/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java index 9d273a56fde..9321ccfbfb0 100644 --- a/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java +++ b/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java @@ -735,7 +735,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> { CountedCompleter<?> a = this, s = a; while (a.onExceptionalCompletion(ex, s) && (a = (s = a).completer) != null && a.status >= 0 && - a.recordExceptionalCompletion(ex) == EXCEPTIONAL) + isExceptionalStatus(a.recordExceptionalCompletion(ex))) ; } diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java index 193cb9e81cb..e4e3aa8f934 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java @@ -219,52 +219,59 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * methods in a way that flows well in javadocs. */ - /* + /** * The status field holds run control status bits packed into a - * single int to minimize footprint and to ensure atomicity (via - * CAS). Status is initially zero, and takes on nonnegative - * values until completed, upon which status (anded with - * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks - * undergoing blocking waits by other threads have the SIGNAL bit - * set. Completion of a stolen task with SIGNAL set awakens any - * waiters via notifyAll. Even though suboptimal for some - * purposes, we use basic builtin wait/notify to take advantage of - * "monitor inflation" in JVMs that we would otherwise need to - * emulate to avoid adding further per-task bookkeeping overhead. - * We want these monitors to be "fat", i.e., not use biasing or - * thin-lock techniques, so use some odd coding idioms that tend - * to avoid them, mainly by arranging that every synchronized - * block performs a wait, notifyAll or both. + * single int to ensure atomicity. Status is initially zero, and + * takes on nonnegative values until completed, upon which it + * holds (sign bit) DONE, possibly with ABNORMAL (cancelled or + * exceptional) and THROWN (in which case an exception has been + * stored). Tasks with dependent blocked waiting joiners have the + * SIGNAL bit set. Completion of a task with SIGNAL set awakens + * any waiters via notifyAll. (Waiters also help signal others + * upon completion.) * * These control bits occupy only (some of) the upper half (16 * bits) of status field. The lower bits are used for user-defined * tags. */ - - /** The run status of this task */ volatile int status; // accessed directly by pool and workers - static final int DONE_MASK = 0xf0000000; // mask out non-completion bits - static final int NORMAL = 0xf0000000; // must be negative - static final int CANCELLED = 0xc0000000; // must be < NORMAL - static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED - static final int SIGNAL = 0x00010000; // must be >= 1 << 16 - static final int SMASK = 0x0000ffff; // short bits for tags + + private static final int DONE = 1 << 31; // must be negative + private static final int ABNORMAL = 1 << 18; // set atomically with DONE + private static final int THROWN = 1 << 17; // set atomically with ABNORMAL + private static final int SIGNAL = 1 << 16; // true if joiner waiting + private static final int SMASK = 0xffff; // short bits for tags + + static boolean isExceptionalStatus(int s) { // needed by subclasses + return (s & THROWN) != 0; + } /** - * Marks completion and wakes up threads waiting to join this - * task. + * Sets DONE status and wakes up threads waiting to join this task. * - * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL - * @return completion status on exit + * @return status on exit */ - private int setCompletion(int completion) { - for (int s;;) { + private int setDone() { + int s; + if (((s = (int)STATUS.getAndBitwiseOr(this, DONE)) & SIGNAL) != 0) + synchronized (this) { notifyAll(); } + return s | DONE; + } + + /** + * Marks cancelled or exceptional completion unless already done. + * + * @param completion must be DONE | ABNORMAL, ORed with THROWN if exceptional + * @return status on exit + */ + private int abnormalCompletion(int completion) { + for (int s, ns;;) { if ((s = status) < 0) return s; - if (STATUS.compareAndSet(this, s, s | completion)) { - if ((s >>> 16) != 0) + else if (STATUS.weakCompareAndSet(this, s, ns = s | completion)) { + if ((s & SIGNAL) != 0) synchronized (this) { notifyAll(); } - return completion; + return ns; } } } @@ -282,10 +289,11 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { try { completed = exec(); } catch (Throwable rex) { - return setExceptionalCompletion(rex); + completed = false; + s = setExceptionalCompletion(rex); } if (completed) - s = setCompletion(NORMAL); + s = setDone(); } return s; } @@ -297,9 +305,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @param timeout using Object.wait conventions. */ final void internalWait(long timeout) { - int s; - if ((s = status) >= 0 && // force completer to issue notify - STATUS.compareAndSet(this, s, s | SIGNAL)) { + if ((int)STATUS.getAndBitwiseOr(this, SIGNAL) >= 0) { synchronized (this) { if (status >= 0) try { wait(timeout); } catch (InterruptedException ie) { } @@ -314,27 +320,24 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @return status upon completion */ private int externalAwaitDone() { - int s = ((this instanceof CountedCompleter) ? // try helping - ForkJoinPool.common.externalHelpComplete( - (CountedCompleter<?>)this, 0) : - ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0); - if (s >= 0 && (s = status) >= 0) { + int s = tryExternalHelp(); + if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) { boolean interrupted = false; - do { - if (STATUS.compareAndSet(this, s, s | SIGNAL)) { - synchronized (this) { - if (status >= 0) { - try { - wait(0L); - } catch (InterruptedException ie) { - interrupted = true; - } + synchronized (this) { + for (;;) { + if ((s = status) >= 0) { + try { + wait(0L); + } catch (InterruptedException ie) { + interrupted = true; } - else - notifyAll(); + } + else { + notifyAll(); + break; } } - } while ((s = status) >= 0); + } if (interrupted) Thread.currentThread().interrupt(); } @@ -345,29 +348,39 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * Blocks a non-worker-thread until completion or interruption. */ private int externalInterruptibleAwaitDone() throws InterruptedException { - int s; - if (Thread.interrupted()) - throw new InterruptedException(); - if ((s = status) >= 0 && - (s = ((this instanceof CountedCompleter) ? - ForkJoinPool.common.externalHelpComplete( - (CountedCompleter<?>)this, 0) : - ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : - 0)) >= 0) { - while ((s = status) >= 0) { - if (STATUS.compareAndSet(this, s, s | SIGNAL)) { - synchronized (this) { - if (status >= 0) - wait(0L); - else - notifyAll(); + int s = tryExternalHelp(); + if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) { + synchronized (this) { + for (;;) { + if ((s = status) >= 0) + wait(0L); + else { + notifyAll(); + break; } } } } + else if (Thread.interrupted()) + throw new InterruptedException(); return s; } + /** + * Tries to help with tasks allowed for external callers. + * + * @return current status + */ + private int tryExternalHelp() { + int s; + return ((s = status) < 0 ? s: + (this instanceof CountedCompleter) ? + ForkJoinPool.common.externalHelpComplete( + (CountedCompleter<?>)this, 0) : + ForkJoinPool.common.tryExternalUnpush(this) ? + doExec() : 0); + } + /** * Implementation for join, get, quietlyJoin. Directly handles * only cases of already-completed, external wait, and @@ -475,7 +488,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } finally { lock.unlock(); } - s = setCompletion(EXCEPTIONAL); + s = abnormalCompletion(DONE | ABNORMAL | THROWN); } return s; } @@ -487,7 +500,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { */ private int setExceptionalCompletion(Throwable ex) { int s = recordExceptionalCompletion(ex); - if ((s & DONE_MASK) == EXCEPTIONAL) + if ((s & THROWN) != 0) internalPropagateException(ex); return s; } @@ -662,10 +675,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * Throws exception, if any, associated with the given status. */ private void reportException(int s) { - if (s == CANCELLED) - throw new CancellationException(); - if (s == EXCEPTIONAL) - rethrow(getThrowableException()); + rethrow((s & THROWN) != 0 ? getThrowableException() : + new CancellationException()); } // public methods @@ -707,7 +718,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { */ public final V join() { int s; - if ((s = doJoin() & DONE_MASK) != NORMAL) + if (((s = doJoin()) & ABNORMAL) != 0) reportException(s); return getRawResult(); } @@ -722,7 +733,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { */ public final V invoke() { int s; - if ((s = doInvoke() & DONE_MASK) != NORMAL) + if (((s = doInvoke()) & ABNORMAL) != 0) reportException(s); return getRawResult(); } @@ -747,9 +758,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) { int s1, s2; t2.fork(); - if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL) + if (((s1 = t1.doInvoke()) & ABNORMAL) != 0) t1.reportException(s1); - if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL) + if (((s2 = t2.doJoin()) & ABNORMAL) != 0) t2.reportException(s2); } @@ -779,7 +790,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } else if (i != 0) t.fork(); - else if (t.doInvoke() < NORMAL && ex == null) + else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null) ex = t.getException(); } for (int i = 1; i <= last; ++i) { @@ -787,7 +798,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { if (t != null) { if (ex != null) t.cancel(false); - else if (t.doJoin() < NORMAL) + else if ((t.doJoin() & ABNORMAL) != 0) ex = t.getException(); } } @@ -831,7 +842,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } else if (i != 0) t.fork(); - else if (t.doInvoke() < NORMAL && ex == null) + else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null) ex = t.getException(); } for (int i = 1; i <= last; ++i) { @@ -839,7 +850,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { if (t != null) { if (ex != null) t.cancel(false); - else if (t.doJoin() < NORMAL) + else if ((t.doJoin() & ABNORMAL) != 0) ex = t.getException(); } } @@ -876,7 +887,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @return {@code true} if this task is now cancelled */ public boolean cancel(boolean mayInterruptIfRunning) { - return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED; + int s = abnormalCompletion(DONE | ABNORMAL); + return (s & (ABNORMAL | THROWN)) == ABNORMAL; } public final boolean isDone() { @@ -884,7 +896,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } public final boolean isCancelled() { - return (status & DONE_MASK) == CANCELLED; + return (status & (ABNORMAL | THROWN)) == ABNORMAL; } /** @@ -893,7 +905,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @return {@code true} if this task threw an exception or was cancelled */ public final boolean isCompletedAbnormally() { - return status < NORMAL; + return (status & ABNORMAL) != 0; } /** @@ -904,7 +916,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * exception and was not cancelled */ public final boolean isCompletedNormally() { - return (status & DONE_MASK) == NORMAL; + return (status & (DONE | ABNORMAL)) == DONE; } /** @@ -915,9 +927,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @return the exception, or {@code null} if none */ public final Throwable getException() { - int s = status & DONE_MASK; - return ((s >= NORMAL) ? null : - (s == CANCELLED) ? new CancellationException() : + int s = status; + return ((s & ABNORMAL) == 0 ? null : + (s & THROWN) == 0 ? new CancellationException() : getThrowableException()); } @@ -961,7 +973,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { setExceptionalCompletion(rex); return; } - setCompletion(NORMAL); + setDone(); } /** @@ -973,7 +985,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * @since 1.8 */ public final void quietlyComplete() { - setCompletion(NORMAL); + setDone(); } /** @@ -990,11 +1002,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { public final V get() throws InterruptedException, ExecutionException { int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ? doJoin() : externalInterruptibleAwaitDone(); - if ((s &= DONE_MASK) == CANCELLED) - throw new CancellationException(); - if (s == EXCEPTIONAL) + if ((s & THROWN) != 0) throw new ExecutionException(getThrowableException()); - return getRawResult(); + else if ((s & ABNORMAL) != 0) + throw new CancellationException(); + else + return getRawResult(); } /** @@ -1034,7 +1047,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { while ((s = status) >= 0 && (ns = deadline - System.nanoTime()) > 0L) { if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L && - STATUS.compareAndSet(this, s, s | SIGNAL)) { + (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) { synchronized (this) { if (status >= 0) wait(ms); // OK to throw InterruptedException @@ -1046,15 +1059,13 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { } } if (s >= 0) - s = status; - if ((s &= DONE_MASK) != NORMAL) { - if (s == CANCELLED) - throw new CancellationException(); - if (s != EXCEPTIONAL) - throw new TimeoutException(); + throw new TimeoutException(); + else if ((s & THROWN) != 0) throw new ExecutionException(getThrowableException()); - } - return getRawResult(); + else if ((s & ABNORMAL) != 0) + throw new CancellationException(); + else + return getRawResult(); } /** @@ -1110,7 +1121,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { * setRawResult(null)}. */ public void reinitialize() { - if ((status & DONE_MASK) == EXCEPTIONAL) + if ((status & THROWN) != 0) clearExceptionalCompletion(); else status = 0; @@ -1327,8 +1338,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { */ public final short setForkJoinTaskTag(short newValue) { for (int s;;) { - if (STATUS.compareAndSet(this, s = status, - (s & ~SMASK) | (newValue & SMASK))) + if (STATUS.weakCompareAndSet(this, s = status, + (s & ~SMASK) | (newValue & SMASK))) return (short)s; } } @@ -1351,8 +1362,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable { for (int s;;) { if ((short)(s = status) != expect) return false; - if (STATUS.compareAndSet(this, s, - (s & ~SMASK) | (update & SMASK))) + if (STATUS.weakCompareAndSet(this, s, + (s & ~SMASK) | (update & SMASK))) return true; } } diff --git a/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java b/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java index 9b7251e6865..2a936940c96 100644 --- a/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java +++ b/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java @@ -105,8 +105,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase { /** * A taken submitted task is completed */ - public void testTake() - throws InterruptedException, ExecutionException { + public void testTake() throws Exception { CompletionService cs = new ExecutorCompletionService(cachedThreadPool); cs.submit(new StringTask()); Future f = cs.take(); @@ -127,8 +126,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase { /** * poll returns non-null when the returned task is completed */ - public void testPoll1() - throws InterruptedException, ExecutionException { + public void testPoll1() throws Exception { CompletionService cs = new ExecutorCompletionService(cachedThreadPool); assertNull(cs.poll()); cs.submit(new StringTask()); @@ -147,15 +145,15 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase { /** * timed poll returns non-null when the returned task is completed */ - public void testPoll2() - throws InterruptedException, ExecutionException { + public void testPoll2() throws Exception { CompletionService cs = new ExecutorCompletionService(cachedThreadPool); assertNull(cs.poll()); cs.submit(new StringTask()); long startTime = System.nanoTime(); Future f; - while ((f = cs.poll(SHORT_DELAY_MS, MILLISECONDS)) == null) { + while ((f = cs.poll(timeoutMillis(), MILLISECONDS)) == null) { + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); if (millisElapsedSince(startTime) > LONG_DELAY_MS) fail("timed out"); Thread.yield(); @@ -167,8 +165,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase { /** * poll returns null before the returned task is completed */ - public void testPollReturnsNull() - throws InterruptedException, ExecutionException { + public void testPollReturnsNullBeforeCompletion() throws Exception { CompletionService cs = new ExecutorCompletionService(cachedThreadPool); final CountDownLatch proceed = new CountDownLatch(1); cs.submit(new Callable() { public String call() throws Exception { @@ -188,29 +185,28 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase { /** * successful and failed tasks are both returned */ - public void testTaskAssortment() - throws InterruptedException, ExecutionException { + public void testTaskAssortment() throws Exception { CompletionService cs = new ExecutorCompletionService(cachedThreadPool); ArithmeticException ex = new ArithmeticException(); - for (int i = 0; i < 2; i++) { + final int rounds = 2; + for (int i = rounds; i--> 0; ) { cs.submit(new StringTask()); cs.submit(callableThrowing(ex)); cs.submit(runnableThrowing(ex), null); } int normalCompletions = 0; int exceptionalCompletions = 0; - for (int i = 0; i < 3 * 2; i++) { + for (int i = 3 * rounds; i--> 0; ) { try { - if (cs.take().get() == TEST_STRING) - normalCompletions++; - } - catch (ExecutionException expected) { - assertTrue(expected.getCause() instanceof ArithmeticException); + assertSame(TEST_STRING, cs.take().get()); + normalCompletions++; + } catch (ExecutionException expected) { + assertSame(ex, expected.getCause()); exceptionalCompletions++; } } - assertEquals(2 * 1, normalCompletions); - assertEquals(2 * 2, exceptionalCompletions); + assertEquals(1 * rounds, normalCompletions); + assertEquals(2 * rounds, exceptionalCompletions); assertNull(cs.poll()); } From 18e476899c32331ac54efe2ed6e51c848a1948f6 Mon Sep 17 00:00:00 2001 From: Srikanth Adayapalam <sadayapalam@openjdk.org> Date: Mon, 11 Dec 2017 06:58:14 +0530 Subject: [PATCH 162/165] 8182401: Verification error for enclosing instance capture inside super constructor invocation Reviewed-by: vromero --- .../sun/tools/javac/comp/LambdaToMethod.java | 38 +++++++++++ .../lambda/ImplicitEnclosingInstanceTest.java | 64 ++++++++++++++++++ .../lambda/InnerInstanceCreationTest.java | 65 +++++++++++++++++++ .../ImplicitEnclosingInstanceTest.java | 63 ++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java create mode 100644 test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java create mode 100644 test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index f041a5638ac..b4365e3365d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -510,6 +510,28 @@ public class LambdaToMethod extends TreeTranslator { } } + /** + * Translate instance creation expressions with implicit enclosing instances + * @param tree + */ + @Override + public void visitNewClass(JCNewClass tree) { + if (context == null || !analyzer.lambdaNewClassFilter(context, tree)) { + super.visitNewClass(tree); + } else { + int prevPos = make.pos; + try { + make.at(tree); + + LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; + tree = lambdaContext.translate(tree); + super.visitNewClass(tree); + } finally { + make.at(prevPos); + } + } + } + @Override public void visitVarDef(JCVariableDecl tree) { LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; @@ -984,6 +1006,7 @@ public class LambdaToMethod extends TreeTranslator { //create the instance creation expression //note that method reference syntax does not allow an explicit //enclosing class (so the enclosing class is null) + // but this may need to be patched up later with the proxy for the outer this JCNewClass newClass = make.NewClass(null, List.nil(), make.Type(tree.getQualifierExpression().type), @@ -2129,6 +2152,21 @@ public class LambdaToMethod extends TreeTranslator { return null; } + /* Translate away naked new instance creation expressions with implicit enclosing instances, + anchoring them to synthetic parameters that stand proxy for the qualified outer this handle. + */ + public JCNewClass translate(JCNewClass newClass) { + Assert.check(newClass.clazz.type.tsym.hasOuterInstance() && newClass.encl == null); + Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); + final Type enclosingType = newClass.clazz.type.getEnclosingType(); + if (m.containsKey(enclosingType.tsym)) { + Symbol tSym = m.get(enclosingType.tsym); + JCExpression encl = make.Ident(tSym).setType(enclosingType); + newClass.encl = encl; + } + return newClass; + } + /** * The translatedSym is not complete/accurate until the analysis is * finished. Once the analysis is finished, the translatedSym is diff --git a/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java b/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java new file mode 100644 index 00000000000..71d4fb824ce --- /dev/null +++ b/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, 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 8182401 + * @summary Verification error for enclosing instance capture inside super constructor invocation + * @run main ImplicitEnclosingInstanceTest + */ + +import java.util.function.Function; + +public class ImplicitEnclosingInstanceTest { + + static String cookie = "deadbeef"; + + static Object f(Function<String, Object> f) { + return f.apply("feed"); + } + + class S { + S(Object s) { + cookie += "face"; + } + } + + class A { + A(String s) { + cookie = s; + } + } + + class B extends S { + B() { + super(f(s->new A(s))); + } + } + + public static void main(String[] args) { + new ImplicitEnclosingInstanceTest().new B(); + if (!cookie.equals("feedface")) + throw new AssertionError("Incorrect cookie!"); + } +} \ No newline at end of file diff --git a/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java b/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java new file mode 100644 index 00000000000..6a56e460db8 --- /dev/null +++ b/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, 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 8182401 8178444 + * @summary Verification error for enclosing instance capture inside super constructor invocation + * @run main InnerInstanceCreationTest + */ + +import java.util.function.Supplier; + +public class InnerInstanceCreationTest { + + static String cookie = ""; + + public static void main(String[] args) { + new InnerInstanceCreationTest().new Producer(); + new InnerInstanceCreationTest().new Producer(0); + new InnerInstanceCreationTest().new Producer(""); + if (!cookie.equals("BlahBlahBlah")) + throw new AssertionError("Unexpected cookie"); + } + + class Inner { + Inner() { + cookie += "Blah"; + } + } + + class Producer { + Producer() { + this(Inner::new); + } + Producer(int x) { + this(() -> new Inner()); + } + Producer(String s) { + this(() -> InnerInstanceCreationTest.this.new Inner()); + } + Producer(Supplier<Object> supplier) { + supplier.get(); + } + } +} diff --git a/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java b/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java new file mode 100644 index 00000000000..a2c2a0c5d40 --- /dev/null +++ b/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 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 8182401 + * @summary Verification error for enclosing instance capture inside super constructor invocation + * @run main ImplicitEnclosingInstanceTest + */ + + +import java.util.function.Function; + +public class ImplicitEnclosingInstanceTest { + static String cookie = "deadbeef"; + static Object f(Function<String, Object> f) { + return f.apply("feed"); + } + + class S { + S(Object s) { + cookie += "face"; + } + } + + class A { + A(String s) { + cookie = s; + } + } + + class B extends S { + B() { + super(f(A::new)); + } + } + + public static void main(String[] args) { + new ImplicitEnclosingInstanceTest().new B(); + if (!cookie.equals("feedface")) + throw new AssertionError("Incorrect cookie!"); + } +} \ No newline at end of file From 03193f6b9bf9a289e005c020886f68aa718c492d Mon Sep 17 00:00:00 2001 From: Christoph Langer <clanger@openjdk.org> Date: Mon, 11 Dec 2017 08:20:05 +0100 Subject: [PATCH 163/165] 8192978: Missing checks and small fixes in jdwp library Reviewed-by: cjplummer, sspitsyn --- .../share/native/libjdwp/VirtualMachineImpl.c | 6 ++--- .../share/native/libjdwp/error_messages.c | 3 ++- .../share/native/libjdwp/error_messages.h | 22 +++++++++---------- .../share/native/libjdwp/eventHandler.c | 7 ++---- .../share/native/libjdwp/invoker.c | 3 +-- .../share/native/libjdwp/log_messages.c | 3 ++- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c index 8e1639c5e2e..767b1b3f777 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -839,7 +839,7 @@ writePaths(PacketOutputStream *out, char *string) { } pos = string; - for ( i = 0 ; i < npaths ; i++ ) { + for ( i = 0 ; i < npaths && pos != NULL; i++ ) { char *psPos; int plen; @@ -859,8 +859,6 @@ writePaths(PacketOutputStream *out, char *string) { jvmtiDeallocate(buf); } - - static jboolean classPaths(PacketInputStream *in, PacketOutputStream *out) { diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c index d6a08c12961..0cd00d667b2 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -71,6 +71,7 @@ vprint_message(FILE *fp, const char *prefix, const char *suffix, /* Fill buffer with single UTF-8 string */ (void)vsnprintf((char*)utf8buf, sizeof(utf8buf), format, ap); + utf8buf[sizeof(utf8buf) - 1] = 0; len = (int)strlen((char*)utf8buf); /* Convert to platform encoding (ignore errors, dangerous area) */ diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h index bf3765357f8..e25908c5592 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -47,7 +47,7 @@ const char * jdwpErrorText(jdwpError); #define THIS_FILE __FILE__ #endif -#define EXIT_ERROR(error,msg) \ +#define EXIT_ERROR(error, msg) \ { \ print_message(stderr, "JDWP exit error ", "\n", \ "%s(%d): %s [%s:%d]", \ @@ -56,21 +56,21 @@ const char * jdwpErrorText(jdwpError); debugInit_exit((jvmtiError)error, msg); \ } -#define JDI_ASSERT(expression) \ -do { \ - if (gdata && gdata->assertOn && !(expression)) { \ +#define JDI_ASSERT(expression) \ +do { \ + if (gdata && gdata->assertOn && !(expression)) { \ jdiAssertionFailed(THIS_FILE, __LINE__, #expression); \ - } \ + } \ } while (0) -#define JDI_ASSERT_MSG(expression, msg) \ -do { \ - if (gdata && gdata->assertOn && !(expression)) { \ +#define JDI_ASSERT_MSG(expression, msg) \ +do { \ + if (gdata && gdata->assertOn && !(expression)) { \ jdiAssertionFailed(THIS_FILE, __LINE__, msg); \ - } \ + } \ } while (0) -#define JDI_ASSERT_FAILED(msg) \ +#define JDI_ASSERT_FAILED(msg) \ jdiAssertionFailed(THIS_FILE, __LINE__, msg) void do_pause(void); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index 2fc3d1b82d3..ad4b773448d 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -471,11 +471,8 @@ synthesizeUnloadEvent(void *signatureVoid, void *envVoid) jbyte eventSessionID = currentSessionID; struct bag *eventBag = eventHelper_createEventBag(); - if (eventBag == NULL) { - /* TO DO: Report, but don't die - */ - JDI_ASSERT(eventBag != NULL); - } + /* TO DO: Report null error, but don't die */ + JDI_ASSERT(eventBag != NULL); /* Signature needs to last, so convert extra copy to * classname diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c index d3d533b14a5..8e2981d9fa3 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2017, 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 @@ -808,7 +808,6 @@ invoker_completeInvokeRequest(jthread thread) mustReleaseReturnValue = request->invokeType == INVOKE_CONSTRUCTOR || returnTypeTag(request->methodSignature) == JDWP_TAG(OBJECT) || returnTypeTag(request->methodSignature) == JDWP_TAG(ARRAY); - } /* diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c index a2607d1c568..287cebb9bc9 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, 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 @@ -187,6 +187,7 @@ log_message_end(const char *format, ...) /* Construct message string. */ va_start(ap, format); (void)vsnprintf(message, sizeof(message), format, ap); + message[sizeof(message) - 1] = 0; va_end(ap); get_time_stamp(datetime, sizeof(datetime)); From f9431b7d2620cf590ceca73652367321619c22ba Mon Sep 17 00:00:00 2001 From: Jan Lahoda <jlahoda@openjdk.org> Date: Fri, 8 Dec 2017 16:28:14 +0100 Subject: [PATCH 164/165] 8189782: com.sun.tools.javac.api.JavacTool.isSupportedOption misreports number of arguments consumed Returning the number of standalone arguments from com.sun.tools.javac.api.JavacTool.isSupportedOption. Reviewed-by: jjg --- .../com/sun/tools/javac/api/JavacTool.java | 2 +- .../com/sun/tools/javac/main/Option.java | 5 ++ .../javac/options/IsSupportedOptionTest.java | 77 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/options/IsSupportedOptionTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java index e0a2607c393..1c474b05c30 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java @@ -219,7 +219,7 @@ public final class JavacTool implements JavaCompiler { Set<Option> recognizedOptions = Option.getJavacToolOptions(); for (Option o : recognizedOptions) { if (o.matches(option)) { - return o.hasArg() ? 1 : 0; + return o.hasSeparateArg() ? 1 : 0; } } return -1; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index 548ee7ca01a..9210793710b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -1025,6 +1025,11 @@ public enum Option { return (argKind != ArgKind.NONE); } + public boolean hasSeparateArg() { + return getArgKind() == ArgKind.REQUIRED && + !primaryName.endsWith(":") && !primaryName.endsWith("="); + } + public boolean matches(String option) { for (String name: names) { if (matches(option, name)) diff --git a/test/langtools/tools/javac/options/IsSupportedOptionTest.java b/test/langtools/tools/javac/options/IsSupportedOptionTest.java new file mode 100644 index 00000000000..1dbf52d52d5 --- /dev/null +++ b/test/langtools/tools/javac/options/IsSupportedOptionTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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 8189782 + * @summary Test for isSupportedOption + * @modules java.compiler + * jdk.compiler + * @run main IsSupportedOptionTest + */ + +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; + +/** + * Tests for JavaCompiler.isSupportedOption method. + */ +public class IsSupportedOptionTest { + public static void main(String... args) throws Exception { + new IsSupportedOptionTest().run(); + } + + public void run() throws Exception { + JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); + check(tool, "-source", 1); + check(tool, "--add-modules", 1); + check(tool, "-verbose", 0); + check(tool, "-proc:none", 0); + check(tool, "-Xlint", 0); + check(tool, "-Xlint:unchecked", 0); + check(tool, "-Xdoclint", 0); + check(tool, "-Xdoclint:stats", 0); + check(tool, "-Xdoclint/package:foo", 0); + check(tool, "--debug:any", 0); + check(tool, "-g", 0); + check(tool, "-g:vars", 0); + check(tool, "-g:none", 0); + check(tool, "-ZZZ", -1); + check(tool, "-Afoobar", 0); + + try { + check(tool, null, -1); + throw new AssertionError("null was accepted without exception"); + } catch (NullPointerException e) { + } + } + + private void check(JavaCompiler tool, String option, int numArgs) { + System.err.println("check " + option); + int n = tool.isSupportedOption(option); + if (n != numArgs) { + throw new AssertionError("unexpected result for option: " + option + ": " + n); + } + } +} + From 390de696014b240396e3565bd653685a05ba86ec Mon Sep 17 00:00:00 2001 From: Jan Lahoda <jlahoda@openjdk.org> Date: Mon, 11 Dec 2017 18:33:53 +0100 Subject: [PATCH 165/165] 8189778: Jshell crash on tab for StringBuilder.append( Fixing handling of {@inheritDoc} in JShell's documentation. Reviewed-by: jjg, ksrini --- .../shellsupport/doc/JavadocHelper.java | 164 ++++++++++---- .../shellsupport/doc/JavadocHelperTest.java | 204 +++++++++++++++--- 2 files changed, 299 insertions(+), 69 deletions(-) diff --git a/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java b/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java index 0edc8458478..1f4b162e1fd 100644 --- a/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java +++ b/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java @@ -77,6 +77,7 @@ import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.VariableTree; +import com.sun.source.util.DocSourcePositions; import com.sun.source.util.DocTreePath; import com.sun.source.util.DocTreeScanner; import com.sun.source.util.DocTrees; @@ -194,6 +195,8 @@ public abstract class JavadocHelper implements AutoCloseable { String docComment = trees.getDocComment(el); if (docComment == null && element.getKind() == ElementKind.METHOD) { + //if a method does not have a javadoc, + //try to use javadoc from the methods overridden by this method: ExecutableElement executableElement = (ExecutableElement) element; Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator(); @@ -215,27 +218,65 @@ public abstract class JavadocHelper implements AutoCloseable { } } + if (docComment == null) + return null; + Pair<DocCommentTree, Integer> parsed = parseDocComment(task, docComment); DocCommentTree docCommentTree = parsed.fst; int offset = parsed.snd; IOException[] exception = new IOException[1]; - Map<int[], String> replace = new TreeMap<>((span1, span2) -> span2[0] - span1[0]); + Comparator<int[]> spanComp = + (span1, span2) -> span1[0] != span2[0] ? span2[0] - span1[0] + : span2[1] - span1[0]; + //spans in the docComment that should be replaced with the given Strings: + Map<int[], List<String>> replace = new TreeMap<>(spanComp); + DocSourcePositions sp = trees.getSourcePositions(); + //fill in missing elements and resolve {@inheritDoc} + //if an element is (silently) missing in the javadoc, a synthetic {@inheritDoc} + //is created for it. new DocTreeScanner<Void, Void>() { + /* enclosing doctree that may contain {@inheritDoc} (explicit or synthetic)*/ private Stack<DocTree> interestingParent = new Stack<>(); + /* current top-level DocCommentTree*/ private DocCommentTree dcTree; - private JavacTask inheritedJavacTask; - private TreePath inheritedTreePath; + /* javadoc from a super method from which we may copy elements.*/ private String inherited; + /* JavacTask from which inherited originates.*/ + private JavacTask inheritedJavacTask; + /* TreePath to the super method from which inherited originates.*/ + private TreePath inheritedTreePath; + /* Synthetic trees that contain {@inheritDoc} and + * texts which which they should be replaced.*/ private Map<DocTree, String> syntheticTrees = new IdentityHashMap<>(); - private long lastPos = 0; + /* Position on which the synthetic trees should be inserted.*/ + private long insertPos = offset; @Override @DefinedBy(Api.COMPILER_TREE) public Void visitDocComment(DocCommentTree node, Void p) { dcTree = node; interestingParent.push(node); try { - scan(node.getFirstSentence(), p); - scan(node.getBody(), p); + if (node.getFullBody().isEmpty()) { + //there is no body in the javadoc, add synthetic {@inheritDoc}, which + //will be automatically filled in visitInheritDoc: + DocCommentTree dc = parseDocComment(task, "{@inheritDoc}").fst; + syntheticTrees.put(dc, "*\n"); + interestingParent.push(dc); + boolean prevInSynthetic = inSynthetic; + try { + inSynthetic = true; + scan(dc.getFirstSentence(), p); + scan(dc.getBody(), p); + } finally { + inSynthetic = prevInSynthetic; + interestingParent.pop(); + } + } else { + scan(node.getFirstSentence(), p); + scan(node.getBody(), p); + } + //add missing @param, @throws and @return, augmented with {@inheritDoc} + //which will be resolved in visitInheritDoc: List<DocTree> augmentedBlockTags = new ArrayList<>(node.getBlockTags()); if (element.getKind() == ElementKind.METHOD) { ExecutableElement executableElement = (ExecutableElement) element; @@ -269,19 +310,19 @@ public abstract class JavadocHelper implements AutoCloseable { for (String missingParam : missingParams) { DocTree syntheticTag = parseBlockTag(task, "@param " + missingParam + " {@inheritDoc}"); - syntheticTrees.put(syntheticTag, "@param " + missingParam + " "); + syntheticTrees.put(syntheticTag, "@param " + missingParam + " *\n"); insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); } for (String missingThrow : missingThrows) { DocTree syntheticTag = parseBlockTag(task, "@throws " + missingThrow + " {@inheritDoc}"); - syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " "); + syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " *\n"); insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); } if (!hasReturn) { DocTree syntheticTag = parseBlockTag(task, "@return {@inheritDoc}"); - syntheticTrees.put(syntheticTag, "@return "); + syntheticTrees.put(syntheticTag, "@return *\n"); insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList); } } @@ -320,26 +361,32 @@ public abstract class JavadocHelper implements AutoCloseable { } @Override @DefinedBy(Api.COMPILER_TREE) public Void visitInheritDoc(InheritDocTree node, Void p) { + //replace (schedule replacement into the replace map) + //{@inheritDoc} with the corresponding text from an overridden method + + //first, fill in inherited, inheritedJavacTask and inheritedTreePath if not + //done yet: if (inherited == null) { try { if (element.getKind() == ElementKind.METHOD) { ExecutableElement executableElement = (ExecutableElement) element; - Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator(); - OUTER: for (Element sup : superTypes) { - for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) { - if (task.getElements().overrides(executableElement, supMethod, (TypeElement) executableElement.getEnclosingElement())) { - Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod); + Iterable<ExecutableElement> superMethods = + () -> superMethodsForInheritDoc(task, executableElement). + iterator(); + for (Element supMethod : superMethods) { + Pair<JavacTask, TreePath> source = + getSourceElement(task, supMethod); - if (source != null) { - String overriddenComment = getResolvedDocComment(source.fst, source.snd); + if (source != null) { + String overriddenComment = + getResolvedDocComment(source.fst, + source.snd); - if (overriddenComment != null) { - inheritedJavacTask = source.fst; - inheritedTreePath = source.snd; - inherited = overriddenComment; - break OUTER; - } - } + if (overriddenComment != null) { + inheritedJavacTask = source.fst; + inheritedTreePath = source.snd; + inherited = overriddenComment; + break; } } } @@ -357,6 +404,8 @@ public abstract class JavadocHelper implements AutoCloseable { DocCommentTree inheritedDocTree = parsed.fst; int offset = parsed.snd; List<List<? extends DocTree>> inheritedText = new ArrayList<>(); + //find the corresponding piece in the inherited javadoc + //(interesting parent keeps the enclosing tree): DocTree parent = interestingParent.peek(); switch (parent.getKind()) { case DOC_COMMENT: @@ -401,18 +450,29 @@ public abstract class JavadocHelper implements AutoCloseable { long end = Long.MIN_VALUE; for (DocTree t : inheritedText.get(0)) { - start = Math.min(start, trees.getSourcePositions().getStartPosition(null, inheritedDocTree, t) - offset); - end = Math.max(end, trees.getSourcePositions().getEndPosition(null, inheritedDocTree, t) - offset); + start = Math.min(start, + sp.getStartPosition(null, inheritedDocTree, t) - offset); + end = Math.max(end, + sp.getEndPosition(null, inheritedDocTree, t) - offset); } - String text = inherited.substring((int) start, (int) end); + String text = end >= 0 ? inherited.substring((int) start, (int) end) : ""; if (syntheticTrees.containsKey(parent)) { - replace.put(new int[] {(int) lastPos + 1, (int) lastPos}, "\n" + syntheticTrees.get(parent) + text); + //if the {@inheritDoc} is inside a synthetic tree, don't delete anything, + //but insert the required text + //(insertPos is the position at which new stuff should be added): + int[] span = new int[] {(int) insertPos, (int) insertPos}; + replace.computeIfAbsent(span, s -> new ArrayList<>()) + .add(syntheticTrees.get(parent).replace("*", text)); } else { - long inheritedStart = trees.getSourcePositions().getStartPosition(null, dcTree, node); - long inheritedEnd = trees.getSourcePositions().getEndPosition(null, dcTree, node); + //replace the {@inheritDoc} with the full text from + //the overridden method: + long inheritedStart = sp.getStartPosition(null, dcTree, node); + long inheritedEnd = sp.getEndPosition(null, dcTree, node); + int[] span = new int[] {(int) inheritedStart, (int) inheritedEnd}; - replace.put(new int[] {(int) inheritedStart, (int) inheritedEnd}, text); + replace.computeIfAbsent(span, s -> new ArrayList<>()) + .add(text); } } return super.visitInheritDoc(node, p); @@ -428,13 +488,31 @@ public abstract class JavadocHelper implements AutoCloseable { inSynthetic |= syntheticTrees.containsKey(tree); return super.scan(tree, p); } finally { - if (!inSynthetic) { - lastPos = trees.getSourcePositions().getEndPosition(null, dcTree, tree); + if (!inSynthetic && tree != null) { + //for nonsynthetic trees, preserve the ending position as the future + //insertPos (as future missing elements should be inserted behind + //this tree) + //if there is a newline immediately behind this tree, insert behind + //the newline: + long endPos = sp.getEndPosition(null, dcTree, tree); + if (endPos >= 0) { + if (endPos - offset + 1 < docComment.length() && + docComment.charAt((int) (endPos - offset + 1)) == '\n') { + endPos++; + } + if (endPos - offset < docComment.length()) { + insertPos = endPos + 1; + } else { + insertPos = endPos; + } + } } inSynthetic = prevInSynthetic; } } + /* Insert a synthetic tag (toInsert) into the list of tags at + * an appropriate position.*/ private void insertTag(List<DocTree> tags, DocTree toInsert, List<String> parameters, List<String> throwsTypes) { Comparator<DocTree> comp = (tag1, tag2) -> { if (tag1.getKind() == tag2.getKind()) { @@ -479,16 +557,30 @@ public abstract class JavadocHelper implements AutoCloseable { if (replace.isEmpty()) return docComment; + //do actually replace {@inheritDoc} with the new text (as scheduled by the visitor + //above): StringBuilder replacedInheritDoc = new StringBuilder(docComment); - for (Entry<int[], String> e : replace.entrySet()) { - replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset + 1); - replacedInheritDoc.insert(e.getKey()[0] - offset, e.getValue()); + for (Entry<int[], List<String>> e : replace.entrySet()) { + replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset); + replacedInheritDoc.insert(e.getKey()[0] - offset, + e.getValue().stream().collect(Collectors.joining(""))); } return replacedInheritDoc.toString(); } + /* Find methods from which the given method may inherit javadoc, in the proper order.*/ + private Stream<ExecutableElement> superMethodsForInheritDoc(JavacTask task, + ExecutableElement method) { + TypeElement type = (TypeElement) method.getEnclosingElement(); + + return this.superTypeForInheritDoc(task, type) + .flatMap(sup -> ElementFilter.methodsIn(sup.getEnclosedElements()).stream()) + .filter(supMethod -> task.getElements().overrides(method, supMethod, type)); + } + + /* Find types from which methods in type may inherit javadoc, in the proper order.*/ private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) { TypeElement clazz = (TypeElement) type; Stream<Element> result = interfaces(clazz); @@ -529,7 +621,7 @@ public abstract class JavadocHelper implements AutoCloseable { }; DocCommentTree tree = trees.getDocCommentTree(fo); int offset = (int) trees.getSourcePositions().getStartPosition(null, tree, tree); - offset += "<body>".length() + 1; + offset += "<body>".length(); return Pair.of(tree, offset); } catch (URISyntaxException ex) { throw new IllegalStateException(ex); diff --git a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java index 766857f94fb..48106face35 100644 --- a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java +++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java @@ -23,28 +23,37 @@ /* * @test - * @bug 8131019 8190552 + * @bug 8131019 8189778 8190552 * @summary Test JavadocHelper * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main * jdk.compiler/jdk.internal.shellsupport.doc * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask - * @run testng JavadocHelperTest + * @run testng/timeout=900/othervm JavadocHelperTest */ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import javax.lang.model.element.Element; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.ModuleElement.ExportsDirective; +import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticListener; @@ -70,15 +79,15 @@ public class JavadocHelperTest { "Top level. "); doTestJavadoc("", t -> getFirstMethod(t, "test.Super"), - " javadoc1A\n" + + " javadoc1\n" + "\n" + - " @param p1 param1A\n" + - " @param p2 param2A\n" + - " @param p3 param3A\n" + - " @throws IllegalStateException exc1A\n" + - " @throws IllegalArgumentException exc2A\n" + - " @throws IllegalAccessException exc3A\n" + - " @return valueA\n"); + " @param p1 param1\n" + + " @param p2 param2\n" + + " @param p3 param3\n" + + " @throws IllegalStateException exc1\n" + + " @throws IllegalArgumentException exc2\n" + + " @throws IllegalAccessException exc3\n" + + " @return value\n"); } private Element getFirstMethod(JavacTask task, String typeName) { @@ -90,15 +99,15 @@ public class JavadocHelperTest { public void testInheritNoJavadoc() throws Exception { doTestJavadoc("", getSubTest, - " javadoc1A\n" + + " javadoc1\n" + "\n" + - " @param p1 param1A\n" + - " @param p2 param2A\n" + - " @param p3 param3A\n" + - " @throws IllegalStateException exc1A\n" + - " @throws IllegalArgumentException exc2A\n" + - " @throws IllegalAccessException exc3A\n" + - " @return valueA\n"); + " @param p1 param1\n" + + " @param p2 param2\n" + + " @param p3 param3\n" + + " @throws IllegalStateException exc1\n" + + " @throws IllegalArgumentException exc2\n" + + " @throws IllegalAccessException exc3\n" + + " @return value\n"); } public void testInheritFull() throws Exception { @@ -140,7 +149,7 @@ public class JavadocHelperTest { " Prefix javadoc1 suffix.\n" + "\n" + " @param p1 prefix param1 suffix\n" + - "@param p2 param2\n" + + "@param p2 param2\n" + " @param p3 prefix param3 suffix\n" + " @throws IllegalStateException prefix exc1 suffix\n" + " @throws IllegalArgumentException prefix exc2 suffix\n" + @@ -161,8 +170,8 @@ public class JavadocHelperTest { " */\n", getSubTest, " Prefix javadoc1 suffix.\n" + - "@param p1 param1\n" + "\n" + + "@param p1 param1\n" + " @param p2 prefix param2 suffix\n" + " @param p3 prefix param3 suffix\n" + " @throws IllegalStateException prefix exc1 suffix\n" + @@ -189,7 +198,7 @@ public class JavadocHelperTest { " @param p2 prefix param2 suffix\n" + " @param p3 prefix param3 suffix\n" + " @throws IllegalStateException prefix exc1 suffix\n" + - "@throws java.lang.IllegalArgumentException exc2\n" + + "@throws java.lang.IllegalArgumentException exc2\n" + " @throws IllegalAccessException prefix exc3 suffix\n" + " @return prefix value suffix\n"); } @@ -214,11 +223,101 @@ public class JavadocHelperTest { " @throws IllegalStateException prefix exc1 suffix\n" + " @throws IllegalArgumentException prefix exc2 suffix\n" + " @throws IllegalAccessException prefix exc3 suffix\n" + - "@return value\n"); + "@return value\n"); } + public void testInheritAllButOne() throws Exception { + doTestJavadoc(" /**\n" + + " * @throws IllegalArgumentException {@inheritDoc}\n" + + " */\n", + getSubTest, + "javadoc1\n" + + "@param p1 param1\n" + + "@param p2 param2\n" + + "@param p3 param3\n" + + "@throws java.lang.IllegalStateException exc1\n" + + " @throws IllegalArgumentException exc2\n" + + "@throws java.lang.IllegalAccessException exc3\n" + + "@return value\n"); + } + + public void testInheritEmpty() throws Exception { + doTestJavadoc(" /**\n" + + " */\n", + " /**@param p1\n" + + " * @param p2\n" + + " * @param p3\n" + + " * @throws IllegalStateException\n" + + " * @throws IllegalArgumentException\n" + + " * @throws IllegalAccessException\n" + + " * @return\n" + + " */\n", + getSubTest, + "\n" + + "@param p1 \n" + + "@param p2 \n" + + "@param p3 \n" + + "@throws java.lang.IllegalStateException \n" + + "@throws java.lang.IllegalArgumentException \n" + + "@throws java.lang.IllegalAccessException \n" + + "@return \n"); + } + + public void testEmptyValue() throws Exception { + doTestJavadoc(" /**\n" + + " */\n", + " /**@param p1 {@value}\n" + + " * @param p2\n" + + " * @param p3\n" + + " * @throws IllegalStateException\n" + + " * @throws IllegalArgumentException\n" + + " * @throws IllegalAccessException\n" + + " * @return\n" + + " */\n", + getSubTest, + "\n" + + "@param p1 {@value}\n" + + "@param p2 \n" + + "@param p3 \n" + + "@throws java.lang.IllegalStateException \n" + + "@throws java.lang.IllegalArgumentException \n" + + "@throws java.lang.IllegalAccessException \n" + + "@return \n"); + } + + public void testShortComment() throws Exception { + doTestJavadoc(" /**Test.*/\n", + getSubTest, + "Test." + + "@param p1 param1\n" + + "@param p2 param2\n" + + "@param p3 param3\n" + + "@throws java.lang.IllegalStateException exc1\n" + + "@throws java.lang.IllegalArgumentException exc2\n" + + "@throws java.lang.IllegalAccessException exc3\n" + + "@return value\n"); + } private void doTestJavadoc(String origJavadoc, Function<JavacTask, Element> getElement, String expectedJavadoc) throws Exception { + doTestJavadoc(origJavadoc, + " /**\n" + + " * javadoc1\n" + + " *\n" + + " * @param p1 param1\n" + + " * @param p2 param2\n" + + " * @param p3 param3\n" + + " * @throws IllegalStateException exc1\n" + + " * @throws IllegalArgumentException exc2\n" + + " * @throws IllegalAccessException exc3\n" + + " * @return value\n" + + " */\n", + getElement, expectedJavadoc); + } + + private void doTestJavadoc(String origJavadoc, + String superJavadoc, + Function<JavacTask, Element> getElement, + String expectedJavadoc) throws Exception { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); String subClass = "package test;\n" + @@ -231,17 +330,7 @@ public class JavadocHelperTest { "/**Top level." + " */\n" + "public class Super {\n" + - " /**\n" + - " * javadoc1A\n" + - " *\n" + - " * @param p1 param1A\n" + - " * @param p2 param2A\n" + - " * @param p3 param3A\n" + - " * @throws IllegalStateException exc1A\n" + - " * @throws IllegalArgumentException exc2A\n" + - " * @throws IllegalAccessException exc3A\n" + - " * @return valueA\n" + - " */\n" + + superJavadoc + " public String test(int p1, int p2, int p3) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { return null;} \n" + "}\n"; @@ -293,4 +382,53 @@ public class JavadocHelperTest { } } + + public void testAllDocs() throws IOException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticListener<? super JavaFileObject> noErrors = d -> { + if (d.getKind() == Kind.ERROR) { + throw new AssertionError(d.getMessage(null)); + } + }; + + List<Path> sources = new ArrayList<>(); + Path home = Paths.get(System.getProperty("java.home")); + Path srcZip = home.resolve("lib").resolve("src.zip"); + if (Files.isReadable(srcZip)) { + URI uri = URI.create("jar:" + srcZip.toUri()); + try (FileSystem zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path root = zipFO.getRootDirectories().iterator().next(); + + //modular format: + try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) { + for (Path p : ds) { + if (Files.isDirectory(p)) { + sources.add(p); + } + } + } + try (StandardJavaFileManager fm = + compiler.getStandardFileManager(null, null, null)) { + JavacTask task = + (JavacTask) compiler.getTask(null, fm, noErrors, null, null, null); + task.getElements().getTypeElement("java.lang.Object"); + for (ModuleElement me : task.getElements().getAllModuleElements()) { + List<ExportsDirective> exports = + ElementFilter.exportsIn(me.getDirectives()); + for (ExportsDirective ed : exports) { + try (JavadocHelper helper = JavadocHelper.create(task, sources)) { + List<? extends Element> content = + ed.getPackage().getEnclosedElements(); + for (TypeElement clazz : ElementFilter.typesIn(content)) { + for (Element el : clazz.getEnclosedElements()) { + helper.getResolvedDocComment(el); + } + } + } + } + } + } + } + } + } }