Compare commits

...

73 Commits

Author SHA1 Message Date
Ravi Reddy
dbbaa9e632 Merge 2024-07-23 08:17:19 +00:00
Justin Lu
82220915e5 8331446: Improve deserialization support
Reviewed-by: naoto
Backport-of: 8e4a392832f83e16d521024505b52c96d0a993f2
2024-07-22 16:59:19 +00:00
luwang1103
cc687e87d9 8334239: Introduce macro for ubsan method/function exclusions
Backport-of: ff30240926
2024-07-22 08:36:17 +00:00
Dean Long
dfbb5e3ffa 8332644: Improve graph optimizations
Reviewed-by: kvn
Backport-of: 7c16d649a8118d2e7ee77cedba87e620c83294b4
2024-07-19 23:49:47 +00:00
Xiaolong Peng
9805cbbd25 8331411: Shenandoah: Reconsider spinning duration in ShenandoahLock
Reviewed-by: shade
Backport-of: 817edcb697
2024-07-19 15:19:58 +00:00
Shivangi Gupta
c5f294b439 8336301: test/jdk/java/nio/channels/AsyncCloseAndInterrupt.java leaves around a FIFO file upon test completion
Backport-of: ae9f318fc3
2024-07-17 12:13:38 +00:00
Ravi Reddy
39aab17724 Merge 2024-07-17 11:42:33 +00:00
luwang1103
e9d86d853f 8332818: ubsan: archiveHeapLoader.cpp:70:27: runtime error: applying non-zero offset 18446744073707454464 to null pointer
Backport-of: 6861766b63
2024-07-17 09:21:58 +00:00
luwang1103
821a3aa53e 8333887: ubsan: unsafe.cpp:247:13: runtime error: store to null pointer of type 'volatile int'
Backport-of: 0d3a3771c3
2024-07-17 09:20:01 +00:00
Vanitha B P
8883b15634 8325525: Create jtreg test case for JDK-8325203
Backport-of: 81a0d1ba03
2024-07-17 07:20:13 +00:00
Matthias Baesken
e4d80b1f6c 8334123: log the opening of Type 1 fonts
Backport-of: f8c657f671
2024-07-16 13:30:23 +00:00
Shivangi Gupta
4ca2e48acb 8299813: java/nio/channels/DatagramChannel/Disconnect.java fails with jtreg test timeout due to lost datagram
Backport-of: 49eb00da8d
2024-07-11 10:37:51 +00:00
Andrew John Hughes
5cfaec2df8 8335775: Remove extraneous 's' in comment of rawmonitor.cpp test file
Backport-of: ff49f677ee
2024-07-11 01:26:48 +00:00
Rob McKenna
13e53b8e3d Merge 2024-07-10 19:48:56 +00:00
Jorn Vernee
5b9ecb1786 8333886: Explicitly specify that asSlice and reinterpret return a memory segment backed by the same region of memory.
Reviewed-by: sundar
Backport-of: c80e2eb35c
2024-07-10 16:58:04 +00:00
Ravi Reddy
8f5c6e6b02 Merge 2024-07-10 14:32:14 +00:00
Kevin Walls
70ad622bc2 8335124: com/sun/management/ThreadMXBean/ThreadCpuTimeArray.java failed with CPU time out of expected range
Reviewed-by: cjplummer
Backport-of: 79a3554e1d
2024-07-09 19:39:25 +00:00
Liam Miller-Cushon
ca37a482cc 8334757: AssertionError: Missing type variable in where clause
Reviewed-by: jlahoda, vromero
Backport-of: babf6df7d9
2024-07-09 16:05:45 +00:00
Thomas Schatzl
ae10055b2c 8335824: Test gc/arguments/TestMinInitialErgonomics.java is timing out
Reviewed-by: kbarrett, ayang
Backport-of: 953c35eb5b
2024-07-09 08:12:38 +00:00
Jan Lahoda
4e52320979 8334433: jshell.exe runs an executable test.exe on startup
Reviewed-by: vromero
Backport-of: 99d2bbf767
2024-07-09 07:35:22 +00:00
Qizheng Xing
e0dad6d5ae 8335536: Fix assertion failure in IdealGraphPrinter when append is true
Backport-of: 6db4c6a772
2024-07-09 07:23:49 +00:00
Weibing Xiao
bbbfb7ff61 8334653: ISO 4217 Amendment 177 Update
Reviewed-by: naoto
Backport-of: 86b0cf259f
2024-07-08 13:00:34 +00:00
Chen Liang
2f60d36848 8335475: ClassBuilder incorrectly calculates max_locals in some cases
Reviewed-by: asotona
Backport-of: 1ef34c1833
2024-07-08 11:01:53 +00:00
Christoph Langer
b415b98139 8334297: (so) java/nio/channels/SocketChannel/OpenLeak.java should not depend on SecurityManager
Reviewed-by: mbaesken, dfuchs
Backport-of: 50bed6c67b
2024-07-06 20:01:16 +00:00
Erik Gahlin
90d5b5b4c4 8324089: Fix typo in the manual page for "jcmd" (man jcmd)
Reviewed-by: mgronlun
Backport-of: 0bb9c76288
2024-07-05 10:53:09 +00:00
Erik Gahlin
653c481d71 8335479: JFR: Missing documentation for -XX:StartFlightRecording
Reviewed-by: mgronlun
Backport-of: 68ffec9800
2024-07-05 10:17:50 +00:00
Erik Gahlin
10b28babe5 8322812: Manpage for jcmd is missing JFR.view command
Reviewed-by: mgronlun
Backport-of: 350f9c1947
2024-07-05 09:58:56 +00:00
Erik Österlund
d383365ea4 8334890: Missing unconditional cross modifying fence in nmethod entry barriers
Reviewed-by: aboldtch
Backport-of: c0604fb823
2024-07-05 07:54:43 +00:00
Neethu Prasad
2723ffa8ed 8333716: Shenandoah: Check for disarmed method before taking the nmethod lock
Reviewed-by: shade
Backport-of: 18e7d7b5e7
2024-07-05 05:19:17 +00:00
Sean Coffey
a4f938c623 8307383: Enhance DTLS connections
Backport-of: 362dbbaa952b3d4a5270c6bfae879a12e9bdf4d1
2024-07-04 15:27:37 +00:00
Vladimir Kozlov
b6d0ead93f 8335221: Some C2 intrinsics incorrectly assume that type argument is compile-time constant
Reviewed-by: thartmann
Backport-of: 166f9d9ac0
2024-07-02 17:04:13 +00:00
Emanuel Peter
272d11a389 8335390: C2 MergeStores: wrong result with Unsafe
Reviewed-by: thartmann
Backport-of: 9046d7aee3
2024-07-02 14:27:45 +00:00
Ravi Reddy
58dc4c7e0e Merge 2024-07-02 08:36:17 +00:00
Chris Plummer
9d744b0e04 8335134: Test com/sun/jdi/BreakpointOnClassPrepare.java timeout
Reviewed-by: dholmes
Backport-of: 4e8cbf884a
2024-07-01 22:43:47 +00:00
SendaoYan
4410cdc839 8334600: TEST java/net/MulticastSocket/IPMulticastIF.java fails on linux-aarch64
Reviewed-by: clanger
Backport-of: f23295ec1d
2024-07-01 15:48:32 +00:00
Sean Coffey
9a4fc097e0 Merge 2024-07-01 14:35:51 +00:00
Coleen Phillimore
7040de19bd 8333542: Breakpoint in parallel code does not work
Reviewed-by: cjplummer
Backport-of: b3bf31a0a0
2024-07-01 12:16:58 +00:00
Axel Boldt-Christmas
e5fbc631ca 8326820: Metadata artificially kept alive
Reviewed-by: stefank
Backport-of: 5909d54147
2024-07-01 10:24:28 +00:00
Daniel Fuchs
e78c682142 8333849: (dc) DatagramChannel send/receive fails with UOE if buffer backed by memory segment allocated from shared arena
Reviewed-by: alanb
Backport-of: e7dc76b577
2024-07-01 07:55:31 +00:00
Gui Cao
d494c21b9b 8333652: RISC-V: compiler/vectorapi/VectorGatherMaskFoldingTest.java fails when using RVV
Backport-of: ce5727df44
2024-06-29 11:32:09 +00:00
Vladimir Kozlov
87a29629e3 8335220: C2: Missing check for Opaque4 node in EscapeAnalysis
Reviewed-by: vlivanov
Backport-of: 9d986a013d
2024-06-29 02:20:07 +00:00
Alexey Ivanov
e2db30a534 6967482: TAB-key does not work in JTables after selecting details-view in JFileChooser
8166352: FilePane.createDetailsView() removes JTable TAB, SHIFT-TAB functionality

Backport-of: 711e723819
2024-06-28 13:59:29 +00:00
Erik Gahlin
32ed61572c 8334886: jdk/jfr/api/recording/time/TestTimeMultiple.java failed with RuntimeException: getStopTime() > afterStop
Reviewed-by: mgronlun
Backport-of: 0e6b0cbaaa
2024-06-27 22:07:32 +00:00
Vladimir Kozlov
62d0ee9cc0 8334421: assert(!oldbox->is_unbalanced()) failed: this should not be called for unbalanced region
Reviewed-by: shade, thartmann
Backport-of: 9c89f0861c
2024-06-27 19:29:18 +00:00
Matias Saavedra Silva
b5bf9a6605 8311208: Improve CDS Support
Reviewed-by: mschoene, ccheung, iklam
Backport-of: 2c60805bc380945a8266112c7b5aee0498cb7d5b
2024-06-27 18:01:07 +00:00
Anthony Scarpino
98fd657cfa 8326705: Test CertMsgCheck.java fails to find alert certificate_required
Reviewed-by: rhalade
Backport-of: 4ffc5e6077
2024-06-27 14:17:56 +00:00
Stefan Karlsson
d7b9454205 8324781: runtime/Thread/TestAlwaysPreTouchStacks.java failed with Expected a higher ratio between stack committed and reserved
8325218: gc/parallel/TestAlwaysPreTouchBehavior.java fails

Reviewed-by: tschatzl
Backport-of: 31e8debae6
2024-06-27 14:01:23 +00:00
nibjen
9449a53217 8332524: Instead of printing "TLSv1.3," it is showing "TLS13"
Backport-of: e681b4e9b3
2024-06-27 12:23:28 +00:00
Volodymyr Paprotski
b5fbdb2166 8333583: Crypto-XDH.generateSecret regression after JDK-8329538
Reviewed-by: thartmann, sviswanathan
Backport-of: f101e153ce
2024-06-26 15:24:19 +00:00
Tobias Hartmann
2086b0f070 8334629: [BACKOUT] PhaseIdealLoop::conditional_move is too conservative
Reviewed-by: chagedorn, qamai
Backport-of: 933eababf2
2024-06-26 10:49:44 +00:00
Aleksey Shipilev
d1510505c1 8334594: Generational ZGC: Deadlock after OopMap rewrites in 8331572
Reviewed-by: stefank
Backport-of: 05ff3185ed
2024-06-26 07:46:27 +00:00
Prasanta Sadhukhan
ae49182985 8334580: Deprecate no-arg constructor BasicSliderUI() for removal
Reviewed-by: aivanov
Backport-of: e527e1c32f
2024-06-26 02:59:02 +00:00
Sean Coffey
1ea6456172 8328726: Better Kerberos support
Backport-of: 7325899a11f17bf4516d39495a12796385e459ed
2024-06-25 11:41:04 +00:00
Erik Österlund
37ebecec88 8332717: ZGC: Division by zero in heuristics
Reviewed-by: shade
Backport-of: 2d4185f4f1
2024-06-25 07:57:09 +00:00
Sean Coffey
17858b2f07 Merge 2024-06-25 07:25:18 +00:00
Christoph Langer
08c7c38342 8222884: ConcurrentClassDescLookup.java times out intermittently
Reviewed-by: mdoerr
Backport-of: bd046d9b9e
2024-06-25 03:38:09 +00:00
Erik Gahlin
fa7521b29e 8323196: jdk/jfr/api/consumer/filestream/TestOrdered.java failed with "Events are not ordered! Reuse = false"
Reviewed-by: mgronlun
Backport-of: bdd96604ae
2024-06-24 17:53:14 +00:00
Prasanta Sadhukhan
fbcf6d9c4f 8334509: Cancelling PageDialog does not return the same PageFormat object
Reviewed-by: prr
Backport-of: 689cee3d09
2024-06-24 17:26:11 +00:00
Chen Liang
a124e6e5c7 8333748: javap crash - Fatal error: Unmatched bit position 0x2 for location CLASS
Reviewed-by: asotona
Backport-of: 7e55ed3b10
2024-06-24 12:57:37 +00:00
Vicente Romero
0779f0d668 8334762: [BACKOUT BACKPORT] Improve error for illegal early access from nested class
Reviewed-by: liach, mcimadamore
2024-06-24 12:38:19 +00:00
SendaoYan
bd66b6b6f9 8333358: java/io/IO/IO.java test fails intermittently
Reviewed-by: prappo
Backport-of: 1b1dba8082
2024-06-24 09:53:50 +00:00
Hannes Greule
3edf379b67 8334708: FFM: two javadoc problems
Reviewed-by: jpai
Backport-of: 72ca7bafcd
2024-06-24 06:30:12 +00:00
SendaoYan
10d81a337d 8334441: Mark tests in jdk_security_infra group as manual
Reviewed-by: clanger
Backport-of: 8e1d2b091c
2024-06-23 16:43:27 +00:00
Chen Liang
1dbad8058b 8333854: IllegalAccessError with proxies after JDK-8332457
Reviewed-by: asotona
Backport-of: 91bd85d65d
2024-06-21 22:33:23 +00:00
SendaoYan
215149310c 8334333: MissingResourceCauseTestRun.java fails if run by root
Reviewed-by: naoto
Backport-of: de8ee97718
2024-06-21 16:39:18 +00:00
Kevin Walls
23f2c97f4c 8333344: JMX attaching of Subject does not work when security manager not allowed
Reviewed-by: dfuchs
Backport-of: bcf4bb4882
2024-06-21 10:09:14 +00:00
Vicente Romero
e84e0cdf62 8334488: Improve error for illegal early access from nested class
Reviewed-by: mcimadamore
Backport-of: 7b3a96d570
2024-06-20 18:02:13 +00:00
Erik Gahlin
2243974d29 8304732: jdk/jfr/api/consumer/recordingstream/TestStop.java failed again with "Expected outer stream to have 3 events"
Reviewed-by: mgronlun
Backport-of: 856931d01f
2024-06-20 13:55:55 +00:00
Ravi Reddy
7e6693aeba 8328286: Enhance HTTP client
Backport-of: cf8dc79f392c8ec3414d8b36803f026852c4e386
2024-06-20 11:02:42 +00:00
Richard Reingruber
79dd575113 8334402: ProblemList test/hotspot/jtreg/compiler/c2/TestMergeStores.java on big endian platforms
Reviewed-by: kvn
2024-06-20 07:09:24 +00:00
Tobias Hartmann
63e95d8987 8328544: Improve handling of vectorization
Backport-of: b5174c9159fbffdf335ee6835267ba0e674cf432
2024-06-17 09:01:05 +00:00
Ravi Reddy
1a43190e41 Merge 2024-06-12 05:48:21 +00:00
Rob McKenna
90bf3a809a 8334041: Bump version numbers for 23.0.1
Reviewed-by: kcr, iris
2024-06-11 20:33:03 +00:00
220 changed files with 6135 additions and 1554 deletions

View File

@@ -1,7 +1,7 @@
[general]
project=jdk
project=jdk-updates
jbs=JDK
version=23
version=23.0.1
[checks]
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists

View File

@@ -28,12 +28,12 @@
DEFAULT_VERSION_FEATURE=23
DEFAULT_VERSION_INTERIM=0
DEFAULT_VERSION_UPDATE=0
DEFAULT_VERSION_UPDATE=1
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
DEFAULT_VERSION_DATE=2024-09-17
DEFAULT_VERSION_DATE=2024-10-15
DEFAULT_VERSION_CLASSFILE_MAJOR=67 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_VERSION_DOCS_API_SINCE=11

View File

@@ -778,7 +778,7 @@ public class FieldGen {
result.appendLine("}");
result.appendLine("@Override");
result.appendLine("protected int mult(long[] a, long[] b, long[] r) {");
result.appendLine("protected void mult(long[] a, long[] b, long[] r) {");
result.incrIndent();
for (int i = 0; i < 2 * params.getNumLimbs() - 1; i++) {
result.appendIndent();
@@ -804,9 +804,6 @@ public class FieldGen {
}
}
result.append(");\n");
result.appendIndent();
result.append("return 0;");
result.appendLine();
result.decrIndent();
result.appendLine("}");
@@ -836,7 +833,7 @@ public class FieldGen {
// }
// }
result.appendLine("@Override");
result.appendLine("protected int square(long[] a, long[] r) {");
result.appendLine("protected void square(long[] a, long[] r) {");
result.incrIndent();
for (int i = 0; i < 2 * params.getNumLimbs() - 1; i++) {
result.appendIndent();
@@ -877,9 +874,6 @@ public class FieldGen {
}
}
result.append(");\n");
result.appendIndent();
result.append("return 0;");
result.appendLine();
result.decrIndent();
result.appendLine("}");

View File

@@ -1828,10 +1828,12 @@ enum Nf {
// Vector unordered indexed load instructions
INSN( vluxei8_v, 0b0000111, 0b000, 0b01, 0b0);
INSN(vluxei32_v, 0b0000111, 0b110, 0b01, 0b0);
INSN(vluxei64_v, 0b0000111, 0b111, 0b01, 0b0);
// Vector unordered indexed store instructions
INSN( vsuxei8_v, 0b0100111, 0b000, 0b01, 0b0);
INSN(vsuxei32_v, 0b0100111, 0b110, 0b01, 0b0);
INSN(vsuxei64_v, 0b0100111, 0b111, 0b01, 0b0);
#undef INSN

View File

@@ -4795,12 +4795,11 @@ instruct vcountTrailingZeros(vReg dst, vReg src) %{
// ------------------------------ Vector Load Gather ---------------------------
instruct gather_load(vReg dst, indirect mem, vReg idx) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 4 ||
type2aelembytes(Matcher::vector_element_basic_type(n)) == 8);
instruct gather_loadS(vReg dst, indirect mem, vReg idx) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 4);
match(Set dst (LoadVectorGather mem idx));
effect(TEMP_DEF dst);
format %{ "gather_load $dst, $mem, $idx" %}
format %{ "gather_loadS $dst, $mem, $idx" %}
ins_encode %{
__ vmv1r_v(as_VectorRegister($dst$$reg), as_VectorRegister($idx$$reg));
BasicType bt = Matcher::vector_element_basic_type(this);
@@ -4813,12 +4812,28 @@ instruct gather_load(vReg dst, indirect mem, vReg idx) %{
ins_pipe(pipe_slow);
%}
instruct gather_load_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 4 ||
type2aelembytes(Matcher::vector_element_basic_type(n)) == 8);
instruct gather_loadD(vReg dst, indirect mem, vReg idx) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 8);
match(Set dst (LoadVectorGather mem idx));
effect(TEMP_DEF dst);
format %{ "gather_loadD $dst, $mem, $idx" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
Assembler::SEW sew = Assembler::elemtype_to_sew(bt);
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vzext_vf2(as_VectorRegister($dst$$reg), as_VectorRegister($idx$$reg));
__ vsll_vi(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), (int)sew);
__ vluxei64_v(as_VectorRegister($dst$$reg), as_Register($mem$$base),
as_VectorRegister($dst$$reg));
%}
ins_pipe(pipe_slow);
%}
instruct gather_loadS_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 4);
match(Set dst (LoadVectorGatherMasked mem (Binary idx v0)));
effect(TEMP_DEF dst, TEMP tmp);
format %{ "gather_load_masked $dst, $mem, $idx, $v0\t# KILL $tmp" %}
format %{ "gather_loadS_masked $dst, $mem, $idx, $v0\t# KILL $tmp" %}
ins_encode %{
__ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
BasicType bt = Matcher::vector_element_basic_type(this);
@@ -4833,14 +4848,32 @@ instruct gather_load_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, vR
ins_pipe(pipe_slow);
%}
instruct gather_loadD_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n)) == 8);
match(Set dst (LoadVectorGatherMasked mem (Binary idx v0)));
effect(TEMP_DEF dst, TEMP tmp);
format %{ "gather_loadD_masked $dst, $mem, $idx, $v0\t# KILL $tmp" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
Assembler::SEW sew = Assembler::elemtype_to_sew(bt);
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vzext_vf2(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
__ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew);
__ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
as_VectorRegister($dst$$reg));
__ vluxei64_v(as_VectorRegister($dst$$reg), as_Register($mem$$base),
as_VectorRegister($tmp$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// ------------------------------ Vector Store Scatter -------------------------
instruct scatter_store(indirect mem, vReg src, vReg idx, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 4 ||
type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 8);
instruct scatter_storeS(indirect mem, vReg src, vReg idx, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 4);
match(Set mem (StoreVectorScatter mem (Binary src idx)));
effect(TEMP tmp);
format %{ "scatter_store $mem, $idx, $src\t# KILL $tmp" %}
format %{ "scatter_storeS $mem, $idx, $src\t# KILL $tmp" %}
ins_encode %{
__ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
BasicType bt = Matcher::vector_element_basic_type(this, $src);
@@ -4853,12 +4886,28 @@ instruct scatter_store(indirect mem, vReg src, vReg idx, vReg tmp) %{
ins_pipe(pipe_slow);
%}
instruct scatter_store_masked(indirect mem, vReg src, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 4 ||
type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 8);
instruct scatter_storeD(indirect mem, vReg src, vReg idx, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 8);
match(Set mem (StoreVectorScatter mem (Binary src idx)));
effect(TEMP tmp);
format %{ "scatter_storeD $mem, $idx, $src\t# KILL $tmp" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this, $src);
Assembler::SEW sew = Assembler::elemtype_to_sew(bt);
__ vsetvli_helper(bt, Matcher::vector_length(this, $src));
__ vzext_vf2(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
__ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew);
__ vsuxei64_v(as_VectorRegister($src$$reg), as_Register($mem$$base),
as_VectorRegister($tmp$$reg));
%}
ins_pipe(pipe_slow);
%}
instruct scatter_storeS_masked(indirect mem, vReg src, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 4);
match(Set mem (StoreVectorScatterMasked mem (Binary src (Binary idx v0))));
effect(TEMP tmp);
format %{ "scatter_store_masked $mem, $idx, $src, $v0\t# KILL $tmp" %}
format %{ "scatter_storeS_masked $mem, $idx, $src, $v0\t# KILL $tmp" %}
ins_encode %{
__ vmv1r_v(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
BasicType bt = Matcher::vector_element_basic_type(this, $src);
@@ -4871,6 +4920,23 @@ instruct scatter_store_masked(indirect mem, vReg src, vReg idx, vRegMask_V0 v0,
ins_pipe(pipe_slow);
%}
instruct scatter_storeD_masked(indirect mem, vReg src, vReg idx, vRegMask_V0 v0, vReg tmp) %{
predicate(type2aelembytes(Matcher::vector_element_basic_type(n->in(3)->in(1))) == 8);
match(Set mem (StoreVectorScatterMasked mem (Binary src (Binary idx v0))));
effect(TEMP tmp);
format %{ "scatter_storeD_masked $mem, $idx, $src, $v0\t# KILL $tmp" %}
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this, $src);
Assembler::SEW sew = Assembler::elemtype_to_sew(bt);
__ vsetvli_helper(bt, Matcher::vector_length(this, $src));
__ vzext_vf2(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
__ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew);
__ vsuxei64_v(as_VectorRegister($src$$reg), as_Register($mem$$base),
as_VectorRegister($tmp$$reg), Assembler::v0_t);
%}
ins_pipe(pipe_slow);
%}
// ------------------------------ Populate Index to a Vector -------------------
instruct populateindex(vReg dst, iRegIorL2I src1, iRegIorL2I src2, vReg tmp) %{

View File

@@ -249,7 +249,6 @@ address StubGenerator::generate_intpoly_montgomeryMult_P256() {
const Register tmp = r9;
montgomeryMultiply(aLimbs, bLimbs, rLimbs, tmp, _masm);
__ mov64(rax, 0x1); // Return 1 (Fig. 5, Step 6 [1] skipped in montgomeryMultiply)
__ leave();
__ ret(0);

View File

@@ -34,6 +34,7 @@
#include "memory/iterator.inline.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "sanitizers/ub.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/copy.hpp"
@@ -61,6 +62,7 @@ ptrdiff_t ArchiveHeapLoader::_mapped_heap_delta = 0;
// Every mapped region is offset by _mapped_heap_delta from its requested address.
// See FileMapInfo::heap_region_requested_address().
ATTRIBUTE_NO_UBSAN
void ArchiveHeapLoader::init_mapped_heap_info(address mapped_heap_bottom, ptrdiff_t delta, int dumptime_oop_shift) {
assert(!_mapped_heap_relocation_initialized, "only once");
if (!UseCompressedOops) {

View File

@@ -241,9 +241,14 @@ LockedClassesDo::~LockedClassesDo() {
// Iterating over the CLDG needs to be locked because
// unloading can remove entries concurrently soon.
template <bool keep_alive = true>
class ClassLoaderDataGraphIteratorBase : public StackObj {
// unloading can remove entries concurrently.
// This iterator does not keep the CLD alive.
// Any CLD OopHandles (modules, mirrors, resolved refs)
// resolved must be treated as no keepalive. And requires
// that its CLD's holder is kept alive if they escape the
// caller's safepoint or ClassLoaderDataGraph_lock
// critical section.
class ClassLoaderDataGraph::ClassLoaderDataGraphIterator : public StackObj {
ClassLoaderData* _next;
Thread* _thread;
HandleMark _hm; // clean up handles when this is done.
@@ -251,12 +256,8 @@ class ClassLoaderDataGraphIteratorBase : public StackObj {
// unless verifying at a safepoint.
public:
ClassLoaderDataGraphIteratorBase() : _next(ClassLoaderDataGraph::_head), _thread(Thread::current()), _hm(_thread) {
if (keep_alive) {
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
} else {
assert_at_safepoint();
}
ClassLoaderDataGraphIterator() : _next(ClassLoaderDataGraph::_head), _thread(Thread::current()), _hm(_thread) {
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
}
ClassLoaderData* get_next() {
@@ -266,10 +267,6 @@ public:
cld = cld->next();
}
if (cld != nullptr) {
if (keep_alive) {
// Keep cld that is being returned alive.
Handle(_thread, cld->holder());
}
_next = cld->next();
} else {
_next = nullptr;
@@ -278,9 +275,6 @@ public:
}
};
using ClassLoaderDataGraphIterator = ClassLoaderDataGraphIteratorBase<true /* keep_alive */>;
using ClassLoaderDataGraphIteratorNoKeepAlive = ClassLoaderDataGraphIteratorBase<false /* keep_alive */>;
void ClassLoaderDataGraph::loaded_cld_do(CLDClosure* cl) {
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData* cld = iter.get_next()) {
@@ -288,13 +282,6 @@ void ClassLoaderDataGraph::loaded_cld_do(CLDClosure* cl) {
}
}
void ClassLoaderDataGraph::loaded_cld_do_no_keepalive(CLDClosure* cl) {
ClassLoaderDataGraphIteratorNoKeepAlive iter;
while (ClassLoaderData* cld = iter.get_next()) {
cl->do_cld(cld);
}
}
// These functions assume that the caller has locked the ClassLoaderDataGraph_lock
// if they are not calling the function from a safepoint.
void ClassLoaderDataGraph::classes_do(KlassClosure* klass_closure) {
@@ -318,6 +305,16 @@ void ClassLoaderDataGraph::methods_do(void f(Method*)) {
}
}
void ClassLoaderDataGraph::modules_do_keepalive(void f(ModuleEntry*)) {
assert_locked_or_safepoint(Module_lock);
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData* cld = iter.get_next()) {
// Keep the holder alive.
(void)cld->holder();
cld->modules_do(f);
}
}
void ClassLoaderDataGraph::modules_do(void f(ModuleEntry*)) {
assert_locked_or_safepoint(Module_lock);
ClassLoaderDataGraphIterator iter;
@@ -334,9 +331,11 @@ void ClassLoaderDataGraph::packages_do(void f(PackageEntry*)) {
}
}
void ClassLoaderDataGraph::loaded_classes_do(KlassClosure* klass_closure) {
void ClassLoaderDataGraph::loaded_classes_do_keepalive(KlassClosure* klass_closure) {
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData* cld = iter.get_next()) {
// Keep the holder alive.
(void)cld->holder();
cld->loaded_classes_do(klass_closure);
}
}
@@ -346,7 +345,7 @@ void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) {
}
void ClassLoaderDataGraph::verify_dictionary() {
ClassLoaderDataGraphIteratorNoKeepAlive iter;
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData* cld = iter.get_next()) {
if (cld->dictionary() != nullptr) {
cld->dictionary()->verify();
@@ -354,26 +353,28 @@ void ClassLoaderDataGraph::verify_dictionary() {
}
}
#define FOR_ALL_DICTIONARY(X) ClassLoaderDataGraphIterator iter; \
while (ClassLoaderData* X = iter.get_next()) \
if (X->dictionary() != nullptr)
void ClassLoaderDataGraph::print_dictionary(outputStream* st) {
FOR_ALL_DICTIONARY(cld) {
st->print("Dictionary for ");
cld->print_value_on(st);
st->cr();
cld->dictionary()->print_on(st);
st->cr();
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData *cld = iter.get_next()) {
if (cld->dictionary() != nullptr) {
st->print("Dictionary for ");
cld->print_value_on(st);
st->cr();
cld->dictionary()->print_on(st);
st->cr();
}
}
}
void ClassLoaderDataGraph::print_table_statistics(outputStream* st) {
FOR_ALL_DICTIONARY(cld) {
ResourceMark rm; // loader_name_and_id
stringStream tempst;
tempst.print("System Dictionary for %s class loader", cld->loader_name_and_id());
cld->dictionary()->print_table_statistics(st, tempst.freeze());
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData *cld = iter.get_next()) {
if (cld->dictionary() != nullptr) {
ResourceMark rm; // loader_name_and_id
stringStream tempst;
tempst.print("System Dictionary for %s class loader", cld->loader_name_and_id());
cld->dictionary()->print_table_statistics(st, tempst.freeze());
}
}
}
@@ -550,7 +551,7 @@ Klass* ClassLoaderDataGraphKlassIteratorAtomic::next_klass() {
}
void ClassLoaderDataGraph::verify() {
ClassLoaderDataGraphIteratorNoKeepAlive iter;
ClassLoaderDataGraphIterator iter;
while (ClassLoaderData* cld = iter.get_next()) {
cld->verify();
}

View File

@@ -37,10 +37,10 @@ class ClassLoaderDataGraph : public AllStatic {
friend class ClassLoaderDataGraphMetaspaceIterator;
friend class ClassLoaderDataGraphKlassIteratorAtomic;
friend class ClassLoaderDataGraphKlassIteratorStatic;
template <bool keep_alive>
friend class ClassLoaderDataGraphIteratorBase;
friend class VMStructs;
private:
class ClassLoaderDataGraphIterator;
// All CLDs (except unlinked CLDs) can be reached by walking _head->_next->...
static ClassLoaderData* volatile _head;
@@ -71,8 +71,12 @@ class ClassLoaderDataGraph : public AllStatic {
static void roots_cld_do(CLDClosure* strong, CLDClosure* weak);
static void always_strong_cld_do(CLDClosure* cl);
// Iteration through CLDG not by GC.
// All the do suffixed functions do not keep the CLD alive. Any CLD OopHandles
// (modules, mirrors, resolved refs) resolved must be treated as no keepalive.
// And requires that its CLD's holder is kept alive if they escape the
// caller's safepoint or ClassLoaderDataGraph_lock critical section.
// The do_keepalive suffixed functions will keep all CLDs alive.
static void loaded_cld_do(CLDClosure* cl);
static void loaded_cld_do_no_keepalive(CLDClosure* cl);
// klass do
// Walking classes through the ClassLoaderDataGraph include array classes. It also includes
// classes that are allocated but not loaded, classes that have errors, and scratch classes
@@ -81,9 +85,10 @@ class ClassLoaderDataGraph : public AllStatic {
static void classes_do(KlassClosure* klass_closure);
static void classes_do(void f(Klass* const));
static void methods_do(void f(Method*));
static void modules_do_keepalive(void f(ModuleEntry*));
static void modules_do(void f(ModuleEntry*));
static void packages_do(void f(PackageEntry*));
static void loaded_classes_do(KlassClosure* klass_closure);
static void loaded_classes_do_keepalive(KlassClosure* klass_closure);
static void classes_unloading_do(void f(Klass* const));
static bool do_unloading();

View File

@@ -165,7 +165,7 @@ void ClassLoaderStatsClosure::addEmptyParents(oop cl) {
void ClassLoaderStatsVMOperation::doit() {
ClassLoaderStatsClosure clsc (_out);
ClassLoaderDataGraph::loaded_cld_do_no_keepalive(&clsc);
ClassLoaderDataGraph::loaded_cld_do(&clsc);
clsc.print();
}

View File

@@ -788,6 +788,7 @@ int java_lang_Class::_class_loader_offset;
int java_lang_Class::_module_offset;
int java_lang_Class::_protection_domain_offset;
int java_lang_Class::_component_mirror_offset;
int java_lang_Class::_init_lock_offset;
int java_lang_Class::_signers_offset;
int java_lang_Class::_name_offset;
int java_lang_Class::_source_file_offset;
@@ -911,6 +912,12 @@ void java_lang_Class::initialize_mirror_fields(Klass* k,
Handle protection_domain,
Handle classData,
TRAPS) {
// Allocate a simple java object for a lock.
// This needs to be a java object because during class initialization
// it can be held across a java call.
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK);
set_init_lock(mirror(), r);
// Set protection domain also
set_protection_domain(mirror(), protection_domain());
@@ -1132,6 +1139,10 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
if (!k->is_array_klass()) {
// - local static final fields with initial values were initialized at dump time
// create the init_lock
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_(false));
set_init_lock(mirror(), r);
if (protection_domain.not_null()) {
set_protection_domain(mirror(), protection_domain());
}
@@ -1196,6 +1207,15 @@ oop java_lang_Class::component_mirror(oop java_class) {
return java_class->obj_field(_component_mirror_offset);
}
oop java_lang_Class::init_lock(oop java_class) {
assert(_init_lock_offset != 0, "must be set");
return java_class->obj_field(_init_lock_offset);
}
void java_lang_Class::set_init_lock(oop java_class, oop init_lock) {
assert(_init_lock_offset != 0, "must be set");
java_class->obj_field_put(_init_lock_offset, init_lock);
}
objArrayOop java_lang_Class::signers(oop java_class) {
assert(_signers_offset != 0, "must be set");
return (objArrayOop)java_class->obj_field(_signers_offset);
@@ -1415,12 +1435,18 @@ void java_lang_Class::compute_offsets() {
InstanceKlass* k = vmClasses::Class_klass();
CLASS_FIELDS_DO(FIELD_COMPUTE_OFFSET);
// Init lock is a C union with component_mirror. Only instanceKlass mirrors have
// init_lock and only ArrayKlass mirrors have component_mirror. Since both are oops
// GC treats them the same.
_init_lock_offset = _component_mirror_offset;
CLASS_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET);
}
#if INCLUDE_CDS
void java_lang_Class::serialize_offsets(SerializeClosure* f) {
f->do_bool(&_offsets_computed);
f->do_u4((u4*)&_init_lock_offset);
CLASS_FIELDS_DO(FIELD_SERIALIZE_OFFSET);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -226,6 +226,7 @@ class java_lang_Class : AllStatic {
static int _static_oop_field_count_offset;
static int _protection_domain_offset;
static int _init_lock_offset;
static int _signers_offset;
static int _class_loader_offset;
static int _module_offset;
@@ -240,6 +241,7 @@ class java_lang_Class : AllStatic {
static GrowableArray<Klass*>* _fixup_mirror_list;
static GrowableArray<Klass*>* _fixup_module_field_list;
static void set_init_lock(oop java_class, oop init_lock);
static void set_protection_domain(oop java_class, oop protection_domain);
static void set_class_loader(oop java_class, oop class_loader);
static void set_component_mirror(oop java_class, oop comp_mirror);
@@ -292,6 +294,10 @@ class java_lang_Class : AllStatic {
// Support for embedded per-class oops
static oop protection_domain(oop java_class);
static oop init_lock(oop java_class);
static void clear_init_lock(oop java_class) {
set_init_lock(java_class, nullptr);
}
static oop component_mirror(oop java_class);
static objArrayOop signers(oop java_class);
static void set_signers(oop java_class, objArrayOop signers);

View File

@@ -177,7 +177,7 @@ class SystemDictionary : AllStatic {
static void classes_do(MetaspaceClosure* it);
// Iterate over all methods in all klasses
// Will not keep metadata alive. See ClassLoaderDataGraph::methods_do.
static void methods_do(void f(Method*));
// Garbage collection support

View File

@@ -32,6 +32,7 @@
#include "classfile/stackMapTableFormat.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp"
#include "classfile/vmClasses.hpp"
#include "classfile/vmSymbols.hpp"
@@ -212,6 +213,11 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
exception_name == vmSymbols::java_lang_ClassFormatError())) {
log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name());
log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name());
// Exclude any classes that fail over during dynamic dumping
if (CDSConfig::is_dumping_dynamic_archive()) {
SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping");
SystemDictionaryShared::set_excluded(klass);
}
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
exception_message = message_buffer;
exception_name = inference_verify(

View File

@@ -529,8 +529,8 @@ class methodHandle;
/* support for sun.security.util.math.intpoly.MontgomeryIntegerPolynomialP256 */ \
do_class(sun_security_util_math_intpoly_MontgomeryIntegerPolynomialP256, "sun/security/util/math/intpoly/MontgomeryIntegerPolynomialP256") \
do_intrinsic(_intpoly_montgomeryMult_P256, sun_security_util_math_intpoly_MontgomeryIntegerPolynomialP256, intPolyMult_name, intPolyMult_signature, F_R) \
do_name(intPolyMult_name, "mult") \
do_signature(intPolyMult_signature, "([J[J[J)I") \
do_name(intPolyMult_name, "multImpl") \
do_signature(intPolyMult_signature, "([J[J[J)V") \
\
do_class(sun_security_util_math_intpoly_IntegerPolynomial, "sun/security/util/math/intpoly/IntegerPolynomial") \
do_intrinsic(_intpoly_assign, sun_security_util_math_intpoly_IntegerPolynomial, intPolyAssign_name, intPolyAssign_signature, F_S) \

View File

@@ -557,6 +557,7 @@ class SerializeClosure;
template(bool_array_signature, "[Z") \
template(byte_array_signature, "[B") \
template(char_array_signature, "[C") \
template(int_array_signature, "[I") \
template(runnable_signature, "Ljava/lang/Runnable;") \
template(continuation_signature, "Ljdk/internal/vm/Continuation;") \
template(continuationscope_signature, "Ljdk/internal/vm/ContinuationScope;") \

View File

@@ -172,13 +172,9 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) {
nmethod* nm = cb->as_nmethod();
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
if (!bs_nm->is_armed(nm)) {
return 0;
}
assert(!nm->is_osr_method(), "Should not reach here");
// Called upon first entry after being armed
bool may_enter = bs_nm->nmethod_entry_barrier(nm);
assert(!nm->is_osr_method() || may_enter, "OSR nmethods should always be entrant after migration");
// In case a concurrent thread disarmed the nmethod, we need to ensure the new instructions
// are made visible, by using a cross modify fence. Note that this is synchronous cross modifying
@@ -188,11 +184,11 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) {
// it can be made conditional on the nmethod_patching_type.
OrderAccess::cross_modify_fence();
// Diagnostic option to force deoptimization 1 in 3 times. It is otherwise
// Diagnostic option to force deoptimization 1 in 10 times. It is otherwise
// a very rare event.
if (DeoptimizeNMethodBarriersALot) {
if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) {
static volatile uint32_t counter=0;
if (Atomic::add(&counter, 1u) % 3 == 0) {
if (Atomic::add(&counter, 1u) % 10 == 0) {
may_enter = false;
}
}
@@ -205,15 +201,6 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) {
}
bool BarrierSetNMethod::nmethod_osr_entry_barrier(nmethod* nm) {
// This check depends on the invariant that all nmethods that are deoptimized / made not entrant
// are NOT disarmed.
// This invariant is important because a method can be deoptimized after the method have been
// resolved / looked up by OSR by another thread. By not deoptimizing them we guarantee that
// a deoptimized method will always hit the barrier and come to the same conclusion - deoptimize
if (!is_armed(nm)) {
return true;
}
assert(nm->is_osr_method(), "Should not reach here");
log_trace(nmethod, barrier)("Running osr nmethod entry barrier: " PTR_FORMAT, p2i(nm));
bool result = nmethod_entry_barrier(nm);

View File

@@ -132,7 +132,7 @@ bool VM_GC_Operation::doit_prologue() {
void VM_GC_Operation::doit_epilogue() {
// GC thread root traversal likely used OopMapCache a lot, which
// might have created lots of old entries. Trigger the cleanup now.
OopMapCache::trigger_cleanup();
OopMapCache::try_trigger_cleanup();
if (Universe::has_reference_pending_list()) {
Heap_lock->notify_all();
}

View File

@@ -36,13 +36,19 @@
#include "runtime/threadWXSetters.inline.hpp"
bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
if (!is_armed(nm)) {
// Some other thread got here first and healed the oops
// and disarmed the nmethod. No need to continue.
return true;
}
ShenandoahReentrantLock* lock = ShenandoahNMethod::lock_for_nmethod(nm);
assert(lock != nullptr, "Must be");
ShenandoahReentrantLocker locker(lock);
if (!is_armed(nm)) {
// Some other thread got here first and healed the oops
// and disarmed the nmethod.
// Some other thread managed to complete while we were
// waiting for lock. No need to continue.
return true;
}

View File

@@ -32,40 +32,49 @@
#include "runtime/javaThread.hpp"
#include "runtime/os.inline.hpp"
// These are inline variants of Thread::SpinAcquire with optional blocking in VM.
class ShenandoahNoBlockOp : public StackObj {
public:
ShenandoahNoBlockOp(JavaThread* java_thread) {
assert(java_thread == nullptr, "Should not pass anything");
}
};
void ShenandoahLock::contended_lock(bool allow_block_for_safepoint) {
Thread* thread = Thread::current();
if (allow_block_for_safepoint && thread->is_Java_thread()) {
contended_lock_internal<ThreadBlockInVM>(JavaThread::cast(thread));
contended_lock_internal<true>(JavaThread::cast(thread));
} else {
contended_lock_internal<ShenandoahNoBlockOp>(nullptr);
contended_lock_internal<false>(nullptr);
}
}
template<typename BlockOp>
template<bool ALLOW_BLOCK>
void ShenandoahLock::contended_lock_internal(JavaThread* java_thread) {
int ctr = 0;
int yields = 0;
assert(!ALLOW_BLOCK || java_thread != nullptr, "Must have a Java thread when allowing block.");
// Spin this much on multi-processor, do not spin on multi-processor.
int ctr = os::is_MP() ? 0xFF : 0;
// Apply TTAS to avoid more expensive CAS calls if the lock is still held by other thread.
while (Atomic::load(&_state) == locked ||
Atomic::cmpxchg(&_state, unlocked, locked) != unlocked) {
if ((++ctr & 0xFFF) == 0) {
BlockOp block(java_thread);
if (yields > 5) {
os::naked_short_sleep(1);
if (ctr > 0 && !SafepointSynchronize::is_synchronizing()) {
// Lightly contended, spin a little if no safepoint is pending.
SpinPause();
ctr--;
} else if (ALLOW_BLOCK) {
ThreadBlockInVM block(java_thread);
if (SafepointSynchronize::is_synchronizing()) {
// If safepoint is pending, we want to block and allow safepoint to proceed.
// Normally, TBIVM above would block us in its destructor.
//
// But that blocking only happens when TBIVM knows the thread poll is armed.
// There is a window between announcing a safepoint and arming the thread poll
// during which trying to continuously enter TBIVM is counter-productive.
// Under high contention, we may end up going in circles thousands of times.
// To avoid it, we wait here until local poll is armed and then proceed
// to TBVIM exit for blocking. We do not SpinPause, but yield to let
// VM thread to arm the poll sooner.
while (SafepointSynchronize::is_synchronizing() &&
!SafepointMechanism::local_poll_armed(java_thread)) {
os::naked_yield();
}
} else {
os::naked_yield();
yields++;
}
} else {
SpinPause();
os::naked_yield();
}
}
}

View File

@@ -37,20 +37,22 @@ private:
shenandoah_padding(0);
volatile LockState _state;
shenandoah_padding(1);
volatile Thread* _owner;
Thread* volatile _owner;
shenandoah_padding(2);
template<typename BlockOp>
template<bool ALLOW_BLOCK>
void contended_lock_internal(JavaThread* java_thread);
public:
ShenandoahLock() : _state(unlocked), _owner(nullptr) {};
void lock(bool allow_block_for_safepoint) {
assert(Atomic::load(&_owner) != Thread::current(), "reentrant locking attempt, would deadlock");
// Try to lock fast, or dive into contended lock handling.
if (Atomic::cmpxchg(&_state, unlocked, locked) != unlocked) {
if ((allow_block_for_safepoint && SafepointSynchronize::is_synchronizing()) ||
(Atomic::cmpxchg(&_state, unlocked, locked) != unlocked)) {
// 1. Java thread, and there is a pending safepoint. Dive into contended locking
// immediately without trying anything else, and block.
// 2. Fast lock fails, dive into contended lock handling.
contended_lock(allow_block_for_safepoint);
}

View File

@@ -44,7 +44,7 @@ void VM_ShenandoahOperation::doit_epilogue() {
assert(!ShenandoahHeap::heap()->has_gc_state_changed(), "GC State was not synchronized to java threads.");
// GC thread root traversal likely used OopMapCache a lot, which
// might have created lots of old entries. Trigger the cleanup now.
OopMapCache::trigger_cleanup();
OopMapCache::try_trigger_cleanup();
}
bool VM_ShenandoahReferenceOperation::doit_prologue() {

View File

@@ -134,7 +134,7 @@ public:
// GC thread root traversal likely used OopMapCache a lot, which
// might have created lots of old entries. Trigger the cleanup now.
OopMapCache::trigger_cleanup();
OopMapCache::try_trigger_cleanup();
}
bool gc_locked() const {

View File

@@ -524,6 +524,10 @@ static bool rule_major_allocation_rate(const ZDirectorStats& stats) {
}
static double calculate_young_to_old_worker_ratio(const ZDirectorStats& stats) {
if (!stats._old_stats._cycle._is_time_trustable) {
return 1.0;
}
const double young_gc_time = gc_time(stats._young_stats);
const double old_gc_time = gc_time(stats._old_stats);
const size_t reclaimed_per_young_gc = stats._young_stats._stat_heap._reclaimed_avg;

View File

@@ -456,7 +456,7 @@ public:
// GC thread root traversal likely used OopMapCache a lot, which
// might have created lots of old entries. Trigger the cleanup now.
OopMapCache::trigger_cleanup();
OopMapCache::try_trigger_cleanup();
}
bool success() const {

View File

@@ -1810,7 +1810,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
// the interpreter or runtime performs a serialized check of
// the relevant ResolvedIndyEntry::method field. This is done by the caller
// of this method, via CPC::set_dynamic_call, which uses
// a lock to do the final serialization of updates
// an ObjectLocker to do the final serialization of updates
// to ResolvedIndyEntry state, including method.
// Log dynamic info to CDS classlist.

View File

@@ -592,10 +592,13 @@ bool OopMapCache::has_cleanup_work() {
return Atomic::load(&_old_entries) != nullptr;
}
void OopMapCache::trigger_cleanup() {
if (has_cleanup_work()) {
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
void OopMapCache::try_trigger_cleanup() {
// See we can take the lock for the notification without blocking.
// This allows triggering the cleanup from GC paths, that can hold
// the service lock for e.g. oop iteration in service thread.
if (has_cleanup_work() && Service_lock->try_lock_without_rank_check()) {
Service_lock->notify_all();
Service_lock->unlock();
}
}

View File

@@ -183,8 +183,8 @@ class OopMapCache : public CHeapObj<mtClass> {
// Check if we need to clean up old entries
static bool has_cleanup_work();
// Request cleanup if work is needed
static void trigger_cleanup();
// Request cleanup if work is needed and notification is currently possible
static void try_trigger_cleanup();
// Clean up the old entries
static void cleanup();

View File

@@ -30,6 +30,7 @@
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/recorder/repository/jfrChunk.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/repository/jfrChunkRotation.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
@@ -425,3 +426,7 @@ JVM_END
JVM_ENTRY_NO_ENV(void, jfr_unregister_stack_filter(JNIEnv* env, jclass jvm, jlong id))
JfrStackFilterRegistry::remove(id);
JVM_END
NO_TRANSITION(jlong, jfr_nanos_now(JNIEnv* env, jclass jvm))
return JfrChunk::nanos_now();
NO_TRANSITION_END

View File

@@ -165,6 +165,8 @@ jlong JNICALL jfr_register_stack_filter(JNIEnv* env, jclass jvm, jobjectArray cl
jlong JNICALL jfr_unregister_stack_filter(JNIEnv* env, jclass jvm, jlong id);
jlong JNICALL jfr_nanos_now(JNIEnv* env, jclass jvm);
#ifdef __cplusplus
}
#endif

View File

@@ -100,7 +100,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
(char*)"hostTotalSwapMemory", (char*)"()J", (void*) jfr_host_total_swap_memory,
(char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss,
(char*)"registerStackFilter", (char*)"([Ljava/lang/String;[Ljava/lang/String;)J", (void*)jfr_register_stack_filter,
(char*)"unregisterStackFilter", (char*)"(J)V", (void*)jfr_unregister_stack_filter
(char*)"unregisterStackFilter", (char*)"(J)V", (void*)jfr_unregister_stack_filter,
(char*)"nanosNow", (char*)"()J", (void*)jfr_nanos_now
};
const size_t method_array_length = sizeof(method) / sizeof(JNINativeMethod);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* 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 @@ static const u2 JFR_VERSION_MAJOR = 2;
static const u2 JFR_VERSION_MINOR = 1;
// strictly monotone
static jlong nanos_now() {
jlong JfrChunk::nanos_now() {
static jlong last = 0;
jlong seconds;
@@ -47,8 +47,6 @@ static jlong nanos_now() {
const jlong now = seconds * 1000000000 + nanos;
if (now > last) {
last = now;
} else {
++last;
}
return last;
}
@@ -147,7 +145,7 @@ void JfrChunk::update_start_ticks() {
}
void JfrChunk::update_start_nanos() {
const jlong now = nanos_now();
const jlong now = JfrChunk::nanos_now();
assert(now >= _start_nanos, "invariant");
assert(now >= _last_update_nanos, "invariant");
_start_nanos = _last_update_nanos = now;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,8 @@ const u1 PAD = 0;
class JfrChunk : public JfrCHeapObj {
friend class JfrChunkWriter;
friend class JfrChunkHeadWriter;
public:
static jlong nanos_now();
private:
char* _path;
int64_t _start_ticks;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,13 +47,13 @@ class JfrIntrinsicSupport : AllStatic {
#define JFR_HAVE_INTRINSICS
#define JFR_TEMPLATES(template) \
template(jdk_jfr_internal_HiddenWait, "jdk/jfr/internal/HiddenWait") \
template(jdk_jfr_internal_JVM, "jdk/jfr/internal/JVM") \
template(jdk_jfr_internal_event_EventWriterFactory, "jdk/jfr/internal/event/EventWriterFactory") \
template(jdk_jfr_internal_event_EventConfiguration_signature, "Ljdk/jfr/internal/event/EventConfiguration;") \
template(getEventWriter_signature, "()Ljdk/jfr/internal/event/EventWriter;") \
template(eventConfiguration_name, "eventConfiguration") \
template(commit_name, "commit") \
template(jfr_chunk_rotation_monitor, "jdk/jfr/internal/JVM$ChunkRotationMonitor") \
#define JFR_INTRINSICS(do_intrinsic, do_class, do_name, do_signature, do_alias) \
do_intrinsic(_counterTime, jdk_jfr_internal_JVM, counterTime_name, void_long_signature, F_SN) \

View File

@@ -55,6 +55,7 @@
#include "runtime/atomic.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/macros.hpp"
@@ -173,7 +174,7 @@ void ConstantPoolCache::set_direct_or_vtable_call(Bytecodes::Code invoke_code,
}
if (invoke_code == Bytecodes::_invokestatic) {
assert(method->method_holder()->is_initialized() ||
method->method_holder()->is_init_thread(JavaThread::current()),
method->method_holder()->is_reentrant_initialization(JavaThread::current()),
"invalid class initialization state for invoke_static");
if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
@@ -268,11 +269,20 @@ ResolvedMethodEntry* ConstantPoolCache::set_method_handle(int method_index, cons
// A losing writer waits on the lock until the winner writes the method and leaves
// the lock, so that when the losing writer returns, he can use the linked
// cache entry.
// Lock fields to write
Bytecodes::Code invoke_code = Bytecodes::_invokehandle;
MutexLocker ml(constant_pool()->pool_holder()->init_monitor());
ResolvedMethodEntry* method_entry = resolved_method_entry_at(method_index);
JavaThread* current = JavaThread::current();
objArrayHandle resolved_references(current, constant_pool()->resolved_references());
// Use the resolved_references() lock for this cpCache entry.
// resolved_references are created for all classes with Invokedynamic, MethodHandle
// or MethodType constant pool cache entries.
assert(resolved_references() != nullptr,
"a resolved_references array should have been created for this class");
ObjectLocker ol(resolved_references, current);
ResolvedMethodEntry* method_entry = resolved_method_entry_at(method_index);
if (method_entry->is_resolved(invoke_code)) {
return method_entry;
}
@@ -310,7 +320,6 @@ ResolvedMethodEntry* ConstantPoolCache::set_method_handle(int method_index, cons
// Store appendix, if any.
if (has_appendix) {
const int appendix_index = method_entry->resolved_references_index();
objArrayOop resolved_references = constant_pool()->resolved_references();
assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob");
assert(resolved_references->obj_at(appendix_index) == nullptr, "init just once");
resolved_references->obj_at_put(appendix_index, appendix());
@@ -555,7 +564,14 @@ bool ConstantPoolCache::save_and_throw_indy_exc(
assert(PENDING_EXCEPTION->is_a(vmClasses::LinkageError_klass()),
"No LinkageError exception");
MutexLocker ml(THREAD, cpool->pool_holder()->init_monitor());
// Use the resolved_references() lock for this cpCache entry.
// resolved_references are created for all classes with Invokedynamic, MethodHandle
// or MethodType constant pool cache entries.
JavaThread* current = THREAD;
objArrayHandle resolved_references(current, cpool->resolved_references());
assert(resolved_references() != nullptr,
"a resolved_references array should have been created for this class");
ObjectLocker ol(resolved_references, current);
// if the indy_info is resolved or the indy_resolution_failed flag is set then another
// thread either succeeded in resolving the method or got a LinkageError
@@ -578,11 +594,21 @@ bool ConstantPoolCache::save_and_throw_indy_exc(
oop ConstantPoolCache::set_dynamic_call(const CallInfo &call_info, int index) {
ResourceMark rm;
MutexLocker ml(constant_pool()->pool_holder()->init_monitor());
// Use the resolved_references() lock for this cpCache entry.
// resolved_references are created for all classes with Invokedynamic, MethodHandle
// or MethodType constant pool cache entries.
JavaThread* current = JavaThread::current();
constantPoolHandle cp(current, constant_pool());
objArrayHandle resolved_references(current, cp->resolved_references());
assert(resolved_references() != nullptr,
"a resolved_references array should have been created for this class");
ObjectLocker ol(resolved_references, current);
assert(index >= 0, "Indy index must be positive at this point");
if (resolved_indy_entry_at(index)->method() != nullptr) {
return constant_pool()->resolved_reference_from_indy(index);
return cp->resolved_reference_from_indy(index);
}
if (resolved_indy_entry_at(index)->resolution_failed()) {
@@ -590,9 +616,7 @@ oop ConstantPoolCache::set_dynamic_call(const CallInfo &call_info, int index) {
// resolution. Ignore our success and throw their exception.
guarantee(index >= 0, "Invalid indy index");
int encoded_index = ResolutionErrorTable::encode_indy_index(index);
JavaThread* THREAD = JavaThread::current(); // For exception macros.
constantPoolHandle cp(THREAD, constant_pool());
ConstantPool::throw_resolution_error(cp, encoded_index, THREAD);
ConstantPool::throw_resolution_error(cp, encoded_index, current);
return nullptr;
}
@@ -616,7 +640,6 @@ oop ConstantPoolCache::set_dynamic_call(const CallInfo &call_info, int index) {
if (has_appendix) {
const int appendix_index = resolved_indy_entry_at(index)->resolved_references_index();
objArrayOop resolved_references = constant_pool()->resolved_references();
assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob");
assert(resolved_references->obj_at(appendix_index) == nullptr, "init just once");
resolved_references->obj_at_put(appendix_index, appendix());

View File

@@ -86,6 +86,7 @@
#include "runtime/orderAccess.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/reflection.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/threads.hpp"
#include "services/classLoadingService.hpp"
#include "services/finalizerService.hpp"
@@ -497,9 +498,6 @@ Array<int>* InstanceKlass::create_new_default_vtable_indices(int len, TRAPS) {
return vtable_indices;
}
static Monitor* create_init_monitor(const char* name) {
return new Monitor(Mutex::safepoint, name);
}
InstanceKlass::InstanceKlass() {
assert(CDSConfig::is_dumping_static_archive() || CDSConfig::is_using_archive(), "only for CDS");
@@ -517,7 +515,6 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind, Refe
_nest_host_index(0),
_init_state(allocated),
_reference_type(reference_type),
_init_monitor(create_init_monitor("InstanceKlassInitMonitor_lock")),
_init_thread(nullptr)
{
set_vtable_length(parser.vtable_size());
@@ -745,6 +742,28 @@ objArrayOop InstanceKlass::signers() const {
return java_lang_Class::signers(java_mirror());
}
oop InstanceKlass::init_lock() const {
// return the init lock from the mirror
oop lock = java_lang_Class::init_lock(java_mirror());
// Prevent reordering with any access of initialization state
OrderAccess::loadload();
assert(lock != nullptr || !is_not_initialized(), // initialized or in_error state
"only fully initialized state can have a null lock");
return lock;
}
// Set the initialization lock to null so the object can be GC'ed. Any racing
// threads to get this lock will see a null lock and will not lock.
// That's okay because they all check for initialized state after getting
// the lock and return.
void InstanceKlass::fence_and_clear_init_lock() {
// make sure previous stores are all done, notably the init_state.
OrderAccess::storestore();
java_lang_Class::clear_init_lock(java_mirror());
assert(!is_not_initialized(), "class must be initialized now");
}
// See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization
// process. The step comments refers to the procedure described in that section.
// Note: implementation moved to static method to expose the this pointer.
@@ -772,49 +791,6 @@ void InstanceKlass::link_class(TRAPS) {
}
}
void InstanceKlass::check_link_state_and_wait(JavaThread* current) {
MonitorLocker ml(current, _init_monitor);
bool debug_logging_enabled = log_is_enabled(Debug, class, init);
// Another thread is linking this class, wait.
while (is_being_linked() && !is_init_thread(current)) {
if (debug_logging_enabled) {
ResourceMark rm(current);
log_debug(class, init)("Thread \"%s\" waiting for linking of %s by thread \"%s\"",
current->name(), external_name(), init_thread_name());
}
ml.wait();
}
// This thread is recursively linking this class, continue
if (is_being_linked() && is_init_thread(current)) {
if (debug_logging_enabled) {
ResourceMark rm(current);
log_debug(class, init)("Thread \"%s\" recursively linking %s",
current->name(), external_name());
}
return;
}
// If this class wasn't linked already, set state to being_linked
if (!is_linked()) {
if (debug_logging_enabled) {
ResourceMark rm(current);
log_debug(class, init)("Thread \"%s\" linking %s",
current->name(), external_name());
}
set_init_state(being_linked);
set_init_thread(current);
} else {
if (debug_logging_enabled) {
ResourceMark rm(current);
log_debug(class, init)("Thread \"%s\" found %s already linked",
current->name(), external_name());
}
}
}
// Called to verify that a class can link during initialization, without
// throwing a VerifyError.
bool InstanceKlass::link_class_or_fail(TRAPS) {
@@ -893,8 +869,9 @@ bool InstanceKlass::link_class_impl(TRAPS) {
// verification & rewriting
{
LockLinkState init_lock(this, jt);
HandleMark hm(THREAD);
Handle h_init_lock(THREAD, init_lock());
ObjectLocker ol(h_init_lock, jt);
// rewritten will have been set if loader constraint error found
// on an earlier link attempt
// don't verify or rewrite if already rewritten
@@ -952,7 +929,21 @@ bool InstanceKlass::link_class_impl(TRAPS) {
// In case itable verification is ever added.
// itable().verify(tty, true);
#endif
set_initialization_state_and_notify(linked, THREAD);
if (UseVtableBasedCHA && Universe::is_fully_initialized()) {
DeoptimizationScope deopt_scope;
{
// Now mark all code that assumes the class is not linked.
// Set state under the Compile_lock also.
MutexLocker ml(THREAD, Compile_lock);
set_init_state(linked);
CodeCache::mark_dependents_on(&deopt_scope, this);
}
// Perform the deopt handshake outside Compile_lock.
deopt_scope.deoptimize_marked();
} else {
set_init_state(linked);
}
if (JvmtiExport::should_post_class_prepare()) {
JvmtiExport::post_class_prepare(THREAD, this);
}
@@ -1082,7 +1073,6 @@ void InstanceKlass::initialize_impl(TRAPS) {
DTRACE_CLASSINIT_PROBE(required, -1);
bool wait = false;
bool throw_error = false;
JavaThread* jt = THREAD;
@@ -1091,24 +1081,27 @@ void InstanceKlass::initialize_impl(TRAPS) {
// refer to the JVM book page 47 for description of steps
// Step 1
{
MonitorLocker ml(jt, _init_monitor);
Handle h_init_lock(THREAD, init_lock());
ObjectLocker ol(h_init_lock, jt);
// Step 2
while (is_being_initialized() && !is_init_thread(jt)) {
// If we were to use wait() instead of waitInterruptibly() then
// we might end up throwing IE from link/symbol resolution sites
// that aren't expected to throw. This would wreak havoc. See 6320309.
while (is_being_initialized() && !is_reentrant_initialization(jt)) {
if (debug_logging_enabled) {
ResourceMark rm(jt);
log_debug(class, init)("Thread \"%s\" waiting for initialization of %s by thread \"%s\"",
jt->name(), external_name(), init_thread_name());
}
wait = true;
jt->set_class_to_be_initialized(this);
ml.wait();
ol.wait_uninterruptibly(jt);
jt->set_class_to_be_initialized(nullptr);
}
// Step 3
if (is_being_initialized() && is_init_thread(jt)) {
if (is_being_initialized() && is_reentrant_initialization(jt)) {
if (debug_logging_enabled) {
ResourceMark rm(jt);
log_debug(class, init)("Thread \"%s\" recursively initializing %s",
@@ -1136,7 +1129,19 @@ void InstanceKlass::initialize_impl(TRAPS) {
log_debug(class, init)("Thread \"%s\" found %s is in error state",
jt->name(), external_name());
}
throw_error = true;
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
ResourceMark rm(THREAD);
Handle cause(THREAD, get_initialization_error(THREAD));
stringStream ss;
ss.print("Could not initialize class %s", external_name());
if (cause.is_null()) {
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), ss.as_string());
} else {
THROW_MSG_CAUSE(vmSymbols::java_lang_NoClassDefFoundError(),
ss.as_string(), cause);
}
} else {
// Step 6
@@ -1150,22 +1155,6 @@ void InstanceKlass::initialize_impl(TRAPS) {
}
}
// Throw error outside lock
if (throw_error) {
DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);
ResourceMark rm(THREAD);
Handle cause(THREAD, get_initialization_error(THREAD));
stringStream ss;
ss.print("Could not initialize class %s", external_name());
if (cause.is_null()) {
THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), ss.as_string());
} else {
THROW_MSG_CAUSE(vmSymbols::java_lang_NoClassDefFoundError(),
ss.as_string(), cause);
}
}
// Step 7
// Next, if C is a class rather than an interface, initialize it's super class and super
// interfaces.
@@ -1223,7 +1212,7 @@ void InstanceKlass::initialize_impl(TRAPS) {
// Step 9
if (!HAS_PENDING_EXCEPTION) {
set_initialization_state_and_notify(fully_initialized, THREAD);
set_initialization_state_and_notify(fully_initialized, CHECK);
debug_only(vtable().verify(tty, true);)
}
else {
@@ -1256,43 +1245,26 @@ void InstanceKlass::initialize_impl(TRAPS) {
}
void InstanceKlass::set_initialization_state_and_notify(ClassState state, JavaThread* current) {
MonitorLocker ml(current, _init_monitor);
if (state == linked && UseVtableBasedCHA && Universe::is_fully_initialized()) {
DeoptimizationScope deopt_scope;
{
// Now mark all code that assumes the class is not linked.
// Set state under the Compile_lock also.
MutexLocker ml(current, Compile_lock);
set_init_thread(nullptr); // reset _init_thread before changing _init_state
set_init_state(state);
CodeCache::mark_dependents_on(&deopt_scope, this);
}
// Perform the deopt handshake outside Compile_lock.
deopt_scope.deoptimize_marked();
void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) {
Handle h_init_lock(THREAD, init_lock());
if (h_init_lock() != nullptr) {
ObjectLocker ol(h_init_lock, THREAD);
set_init_thread(nullptr); // reset _init_thread before changing _init_state
set_init_state(state);
fence_and_clear_init_lock();
ol.notify_all(CHECK);
} else {
assert(h_init_lock() != nullptr, "The initialization state should never be set twice");
set_init_thread(nullptr); // reset _init_thread before changing _init_state
set_init_state(state);
}
ml.notify_all();
}
// Update hierarchy. This is done before the new klass has been added to the SystemDictionary. The Compile_lock
// is grabbed, to ensure that the compiler is not using the class hierarchy.
void InstanceKlass::add_to_hierarchy(JavaThread* current) {
void InstanceKlass::add_to_hierarchy_impl(JavaThread* current) {
assert(!SafepointSynchronize::is_at_safepoint(), "must NOT be at safepoint");
// In case we are not using CHA based vtables we need to make sure the loaded
// deopt is completed before anyone links this class.
// Linking is done with _init_monitor held, by loading and deopting with it
// held we make sure the deopt is completed before linking.
if (!UseVtableBasedCHA) {
init_monitor()->lock();
}
DeoptimizationScope deopt_scope;
{
MutexLocker ml(current, Compile_lock);
@@ -1314,12 +1286,26 @@ void InstanceKlass::add_to_hierarchy(JavaThread* current) {
}
// Perform the deopt handshake outside Compile_lock.
deopt_scope.deoptimize_marked();
}
if (!UseVtableBasedCHA) {
init_monitor()->unlock();
void InstanceKlass::add_to_hierarchy(JavaThread* current) {
if (UseVtableBasedCHA || !Universe::is_fully_initialized()) {
add_to_hierarchy_impl(current);
} else {
// In case we are not using CHA based vtables we need to make sure the loaded
// deopt is completed before anyone links this class.
// Linking is done with init_lock held, by loading and deopting with it
// held we make sure the deopt is completed before linking.
Handle h_init_lock(current, init_lock());
ObjectLocker ol(h_init_lock, current);
add_to_hierarchy_impl(current);
// This doesn't need a notify because the wait is only on the class initialization path.
}
}
InstanceKlass* InstanceKlass::implementor() const {
InstanceKlass* volatile* ik = adr_implementor();
if (ik == nullptr) {
@@ -2586,7 +2572,6 @@ void InstanceKlass::remove_unshareable_info() {
_nest_host = nullptr;
init_shared_package_entry();
_dep_context_last_cleaned = 0;
_init_monitor = nullptr;
remove_unshareable_flags();
}
@@ -2690,9 +2675,6 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation()) {
set_is_value_based();
}
// restore the monitor
_init_monitor = create_init_monitor("InstanceKlassInitMonitorRestored_lock");
}
// Check if a class or any of its supertypes has a version older than 50.
@@ -2788,9 +2770,6 @@ void InstanceKlass::release_C_heap_structures(bool release_sub_metadata) {
methods_do(method_release_C_heap_structures);
}
// Destroy the init_monitor
delete _init_monitor;
// Deallocate oop map cache
if (_oop_map_cache != nullptr) {
delete _oop_map_cache;
@@ -3482,7 +3461,7 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le
#define BULLET " - "
static const char* state_names[] = {
"allocated", "loaded", "being_linked", "linked", "being_initialized", "fully_initialized", "initialization_error"
"allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error"
};
static void print_vtable(intptr_t* start, int len, outputStream* st) {
@@ -4132,17 +4111,13 @@ void JNIid::verify(Klass* holder) {
}
void InstanceKlass::set_init_state(ClassState state) {
if (state > loaded) {
assert_lock_strong(_init_monitor);
}
#ifdef ASSERT
bool good_state = is_shared() ? (_init_state <= state)
: (_init_state < state);
bool link_failed = _init_state == being_linked && state == loaded;
assert(good_state || state == allocated || link_failed, "illegal state transition");
assert(good_state || state == allocated, "illegal state transition");
#endif
assert(_init_thread == nullptr, "should be cleared before state change");
Atomic::store(&_init_state, state);
_init_state = state;
}
#if INCLUDE_JVMTI

View File

@@ -152,7 +152,6 @@ class InstanceKlass: public Klass {
enum ClassState : u1 {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
being_linked, // currently running verifier and rewriter
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successful final state)
@@ -226,14 +225,20 @@ class InstanceKlass: public Klass {
volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change
// _is_marked_dependent can be set concurrently, thus cannot be part of the
// _misc_flags.
bool _is_marked_dependent; // used for marking during flushing and deoptimization
// Class states are defined as ClassState (see above).
// Place the _init_state here to utilize the unused 2-byte after
// _idnum_allocated_count.
volatile ClassState _init_state; // state of class
u1 _reference_type; // reference type
u1 _reference_type; // reference type
// State is set either at parse time or while executing, atomically to not disturb other state
InstanceKlassFlags _misc_flags;
Monitor* _init_monitor; // mutual exclusion to _init_state and _init_thread.
JavaThread* volatile _init_thread; // Pointer to current thread doing initialization (to handle recursive initialization)
OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily)
@@ -497,41 +502,23 @@ public:
TRAPS);
JavaThread* init_thread() { return Atomic::load(&_init_thread); }
// We can safely access the name as long as we hold the _init_monitor.
const char* init_thread_name() {
assert(_init_monitor->owned_by_self(), "Must hold _init_monitor here");
return init_thread()->name_raw();
}
public:
// initialization state
bool is_loaded() const { return init_state() >= loaded; }
bool is_linked() const { return init_state() >= linked; }
bool is_being_linked() const { return init_state() == being_linked; }
bool is_initialized() const { return init_state() == fully_initialized; }
bool is_not_initialized() const { return init_state() < being_initialized; }
bool is_being_initialized() const { return init_state() == being_initialized; }
bool is_in_error_state() const { return init_state() == initialization_error; }
bool is_init_thread(JavaThread *thread) { return thread == init_thread(); }
ClassState init_state() const { return Atomic::load(&_init_state); }
bool is_loaded() const { return _init_state >= loaded; }
bool is_linked() const { return _init_state >= linked; }
bool is_initialized() const { return _init_state == fully_initialized; }
bool is_not_initialized() const { return _init_state < being_initialized; }
bool is_being_initialized() const { return _init_state == being_initialized; }
bool is_in_error_state() const { return _init_state == initialization_error; }
bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; }
ClassState init_state() const { return _init_state; }
const char* init_state_name() const;
bool is_rewritten() const { return _misc_flags.rewritten(); }
class LockLinkState : public StackObj {
InstanceKlass* _ik;
JavaThread* _current;
public:
LockLinkState(InstanceKlass* ik, JavaThread* current) : _ik(ik), _current(current) {
ik->check_link_state_and_wait(current);
}
~LockLinkState() {
if (!_ik->is_linked()) {
// Reset to loaded if linking failed.
_ik->set_initialization_state_and_notify(loaded, _current);
}
}
};
// is this a sealed class
bool is_sealed() const;
@@ -829,7 +816,7 @@ public:
// initialization
void call_class_initializer(TRAPS);
void set_initialization_state_and_notify(ClassState state, JavaThread* current);
void set_initialization_state_and_notify(ClassState state, TRAPS);
// OopMapCache support
OopMapCache* oop_map_cache() { return _oop_map_cache; }
@@ -841,6 +828,10 @@ public:
void set_jni_ids(JNIid* ids) { _jni_ids = ids; }
JNIid* jni_id_for(int offset);
private:
void add_to_hierarchy_impl(JavaThread* current);
public:
// maintenance of deoptimization dependencies
inline DependencyContext dependencies();
void mark_dependent_nmethods(DeoptimizationScope* deopt_scope, KlassDepChange& changes);
@@ -1055,7 +1046,7 @@ public:
public:
u2 idnum_allocated_count() const { return _idnum_allocated_count; }
private:
private:
// initialization state
void set_init_state(ClassState state);
void set_rewritten() { _misc_flags.set_rewritten(true); }
@@ -1072,6 +1063,12 @@ public:
jmethodID update_jmethod_id(jmethodID* jmeths, Method* method, int idnum);
public:
// Lock for (1) initialization; (2) access to the ConstantPool of this class.
// Must be one per class and it has to be a VM internal object so java code
// cannot lock it (like the mirror).
// It has to be an object not a Mutex because it's held through java calls.
oop init_lock() const;
// Returns the array class for the n'th dimension
virtual ArrayKlass* array_klass(int n, TRAPS);
virtual ArrayKlass* array_klass_or_null(int n);
@@ -1081,10 +1078,9 @@ public:
virtual ArrayKlass* array_klass_or_null();
static void clean_initialization_error_table();
Monitor* init_monitor() const { return _init_monitor; }
private:
void check_link_state_and_wait(JavaThread* current);
void fence_and_clear_init_lock();
bool link_class_impl (TRAPS);
bool verify_code (TRAPS);
void initialize_impl (TRAPS);

View File

@@ -1950,6 +1950,22 @@ bool AbstractLockNode::find_unlocks_for_region(const RegionNode* region, LockNod
}
// Check that all locks/unlocks associated with object come from balanced regions.
bool AbstractLockNode::is_balanced() {
Node* obj = obj_node();
for (uint j = 0; j < obj->outcnt(); j++) {
Node* n = obj->raw_out(j);
if (n->is_AbstractLock() &&
n->as_AbstractLock()->obj_node()->eqv_uncast(obj)) {
BoxLockNode* n_box = n->as_AbstractLock()->box_node()->as_BoxLock();
if (n_box->is_unbalanced()) {
return false;
}
}
}
return true;
}
const char* AbstractLockNode::_kind_names[] = {"Regular", "NonEscObj", "Coarsened", "Nested"};
const char * AbstractLockNode::kind_as_string() const {
@@ -2056,6 +2072,8 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
int unlocks = 0;
if (Verbose) {
tty->print_cr("=== Locks coarsening ===");
tty->print("Obj: ");
obj_node()->dump();
}
for (int i = 0; i < lock_ops.length(); i++) {
AbstractLockNode* lock = lock_ops.at(i);
@@ -2064,6 +2082,8 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
else
unlocks++;
if (Verbose) {
tty->print("Box %d: ", i);
box_node()->dump();
tty->print(" %d: ", i);
lock->dump();
}

View File

@@ -1154,6 +1154,10 @@ public:
void set_coarsened() { _kind = Coarsened; set_eliminated_lock_counter(); }
void set_nested() { _kind = Nested; set_eliminated_lock_counter(); }
// Check that all locks/unlocks associated with object come from balanced regions.
// They can become unbalanced after coarsening optimization or on OSR entry.
bool is_balanced();
// locking does not modify its arguments
virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase){ return false; }

View File

@@ -574,18 +574,23 @@ bool ConnectionGraph::can_reduce_check_users(Node* n, uint nesting) const {
// CmpP/N used by the If controlling the cast.
if (use->in(0)->is_IfTrue() || use->in(0)->is_IfFalse()) {
Node* iff = use->in(0)->in(0);
if (iff->Opcode() == Op_If && iff->in(1)->is_Bool() && iff->in(1)->in(1)->is_Cmp()) {
// We may have Opaque4 node between If and Bool nodes.
// Bail out in such case - we need to preserve Opaque4 for correct
// processing predicates after loop opts.
bool can_reduce = (iff->Opcode() == Op_If) && iff->in(1)->is_Bool() && iff->in(1)->in(1)->is_Cmp();
if (can_reduce) {
Node* iff_cmp = iff->in(1)->in(1);
int opc = iff_cmp->Opcode();
if ((opc == Op_CmpP || opc == Op_CmpN) && !can_reduce_cmp(n, iff_cmp)) {
can_reduce = (opc == Op_CmpP || opc == Op_CmpN) && can_reduce_cmp(n, iff_cmp);
}
if (!can_reduce) {
#ifndef PRODUCT
if (TraceReduceAllocationMerges) {
tty->print_cr("Can NOT reduce Phi %d on invocation %d. CastPP %d doesn't have simple control.", n->_idx, _invocation, use->_idx);
n->dump(5);
}
#endif
return false;
if (TraceReduceAllocationMerges) {
tty->print_cr("Can NOT reduce Phi %d on invocation %d. CastPP %d doesn't have simple control.", n->_idx, _invocation, use->_idx);
n->dump(5);
}
#endif
return false;
}
}
}
@@ -651,7 +656,12 @@ Node* ConnectionGraph::specialize_cmp(Node* base, Node* curr_ctrl) {
if (curr_ctrl == nullptr || curr_ctrl->is_Region()) {
con = _igvn->zerocon(t->basic_type());
} else {
Node* curr_cmp = curr_ctrl->in(0)->in(1)->in(1); // true/false -> if -> bool -> cmp
// can_reduce_check_users() verified graph: true/false -> if -> bool -> cmp
assert(curr_ctrl->in(0)->Opcode() == Op_If, "unexpected node %s", curr_ctrl->in(0)->Name());
Node* bol = curr_ctrl->in(0)->in(1);
assert(bol->is_Bool(), "unexpected node %s", bol->Name());
Node* curr_cmp = bol->in(1);
assert(curr_cmp->Opcode() == Op_CmpP || curr_cmp->Opcode() == Op_CmpN, "unexpected node %s", curr_cmp->Name());
con = curr_cmp->in(1)->is_Con() ? curr_cmp->in(1) : curr_cmp->in(2);
}
@@ -3501,12 +3511,11 @@ bool ConnectionGraph::not_global_escape(Node *n) {
// and locked code region (identified by BoxLockNode) is balanced:
// all compiled code paths have corresponding Lock/Unlock pairs.
bool ConnectionGraph::can_eliminate_lock(AbstractLockNode* alock) {
BoxLockNode* box = alock->box_node()->as_BoxLock();
if (!box->is_unbalanced() && not_global_escape(alock->obj_node())) {
if (alock->is_balanced() && not_global_escape(alock->obj_node())) {
if (EliminateNestedLocks) {
// We can mark whole locking region as Local only when only
// one object is used for locking.
box->set_local();
alock->box_node()->as_BoxLock()->set_local();
}
return true;
}

View File

@@ -143,21 +143,24 @@ void IdealGraphPrinter::init(const char* file_name, bool use_multiple_files, boo
_depth = 0;
_current_method = nullptr;
_network_stream = nullptr;
_append = append;
if (file_name != nullptr) {
init_file_stream(file_name, use_multiple_files, append);
init_file_stream(file_name, use_multiple_files);
} else {
init_network_stream();
}
_xml = new (mtCompiler) xmlStream(_output);
if (!append) {
if (!_append) {
head(TOP_ELEMENT);
}
}
// Destructor, close file or network stream
IdealGraphPrinter::~IdealGraphPrinter() {
tail(TOP_ELEMENT);
if (!_append) {
tail(TOP_ELEMENT);
}
// tty->print_cr("Walk time: %d", (int)_walk_time.milliseconds());
// tty->print_cr("Output time: %d", (int)_output_time.milliseconds());
@@ -860,10 +863,10 @@ void IdealGraphPrinter::print(const char *name, Node *node) {
_xml->flush();
}
void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files, bool append) {
void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multiple_files) {
ThreadCritical tc;
if (use_multiple_files && _file_count != 0) {
assert(!append, "append should only be used for debugging with a single file");
assert(!_append, "append should only be used for debugging with a single file");
ResourceMark rm;
stringStream st;
const char* dot = strrchr(file_name, '.');
@@ -875,10 +878,10 @@ void IdealGraphPrinter::init_file_stream(const char* file_name, bool use_multipl
}
_output = new (mtCompiler) fileStream(st.as_string(), "w");
} else {
_output = new (mtCompiler) fileStream(file_name, append ? "a" : "w");
_output = new (mtCompiler) fileStream(file_name, _append ? "a" : "w");
}
if (use_multiple_files) {
assert(!append, "append should only be used for debugging with a single file");
assert(!_append, "append should only be used for debugging with a single file");
_file_count++;
}
}
@@ -909,9 +912,16 @@ void IdealGraphPrinter::update_compiled_method(ciMethod* current_method) {
assert(C != nullptr, "must already be set");
if (current_method != _current_method) {
// If a different method, end the old and begin with the new one.
end_method();
_current_method = nullptr;
begin_method();
if (_append) {
// Do not call `end_method` if we are appending, just update `_current_method`,
// because `begin_method` is not called in the constructor in append mode.
_current_method = current_method;
} else {
// End the old method and begin a new one.
// Don't worry about `_current_method`, `end_method` will clear it.
end_method();
begin_method();
}
}
}

View File

@@ -96,6 +96,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
bool _traverse_outs;
Compile *C;
double _max_freq;
bool _append;
void print_method(ciMethod* method, int bci, InlineTree* tree);
void print_inline_tree(InlineTree* tree);
@@ -118,7 +119,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
void head(const char *name);
void text(const char *s);
void init(const char* file_name, bool use_multiple_files, bool append);
void init_file_stream(const char* file_name, bool use_multiple_files, bool append);
void init_file_stream(const char* file_name, bool use_multiple_files);
void init_network_stream();
IdealGraphPrinter();
~IdealGraphPrinter();

View File

@@ -5463,42 +5463,72 @@ void LibraryCallKit::create_new_uncommon_trap(CallStaticJavaNode* uncommon_trap_
uncommon_trap_call->set_req(0, top()); // not used anymore, kill it
}
// Common checks for array sorting intrinsics arguments.
// Returns `true` if checks passed.
bool LibraryCallKit::check_array_sort_arguments(Node* elementType, Node* obj, BasicType& bt) {
// check address of the class
if (elementType == nullptr || elementType->is_top()) {
return false; // dead path
}
const TypeInstPtr* elem_klass = gvn().type(elementType)->isa_instptr();
if (elem_klass == nullptr) {
return false; // dead path
}
// java_mirror_type() returns non-null for compile-time Class constants only
ciType* elem_type = elem_klass->java_mirror_type();
if (elem_type == nullptr) {
return false;
}
bt = elem_type->basic_type();
// Disable the intrinsic if the CPU does not support SIMD sort
if (!Matcher::supports_simd_sort(bt)) {
return false;
}
// check address of the array
if (obj == nullptr || obj->is_top()) {
return false; // dead path
}
const TypeAryPtr* obj_t = _gvn.type(obj)->isa_aryptr();
if (obj_t == nullptr || obj_t->elem() == Type::BOTTOM) {
return false; // failed input validation
}
return true;
}
//------------------------------inline_array_partition-----------------------
bool LibraryCallKit::inline_array_partition() {
address stubAddr = StubRoutines::select_array_partition_function();
if (stubAddr == nullptr) {
return false; // Intrinsic's stub is not implemented on this platform
}
assert(callee()->signature()->size() == 9, "arrayPartition has 8 parameters (one long)");
Node* elementType = null_check(argument(0));
// no receiver because it is a static method
Node* elementType = argument(0);
Node* obj = argument(1);
Node* offset = argument(2);
Node* offset = argument(2); // long
Node* fromIndex = argument(4);
Node* toIndex = argument(5);
Node* indexPivot1 = argument(6);
Node* indexPivot2 = argument(7);
// PartitionOperation: argument(8) is ignored
Node* pivotIndices = nullptr;
BasicType bt = T_ILLEGAL;
if (!check_array_sort_arguments(elementType, obj, bt)) {
return false;
}
null_check(obj);
// If obj is dead, only null-path is taken.
if (stopped()) {
return true;
}
// Set the original stack and the reexecute bit for the interpreter to reexecute
// the bytecode that invokes DualPivotQuicksort.partition() if deoptimization happens.
{ PreserveReexecuteState preexecs(this);
jvms()->set_should_reexecute(true);
const TypeInstPtr* elem_klass = gvn().type(elementType)->isa_instptr();
ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type();
BasicType bt = elem_type->basic_type();
// Disable the intrinsic if the CPU does not support SIMD sort
if (!Matcher::supports_simd_sort(bt)) {
return false;
}
address stubAddr = nullptr;
stubAddr = StubRoutines::select_array_partition_function();
// stub not loaded
if (stubAddr == nullptr) {
return false;
}
// get the address of the array
const TypeAryPtr* obj_t = _gvn.type(obj)->isa_aryptr();
if (obj_t == nullptr || obj_t->elem() == Type::BOTTOM ) {
return false; // failed input validation
}
Node* obj_adr = make_unsafe_address(obj, offset);
// create the pivotIndices array of type int and size = 2
@@ -5531,31 +5561,29 @@ bool LibraryCallKit::inline_array_partition() {
//------------------------------inline_array_sort-----------------------
bool LibraryCallKit::inline_array_sort() {
address stubAddr = StubRoutines::select_arraysort_function();
if (stubAddr == nullptr) {
return false; // Intrinsic's stub is not implemented on this platform
}
assert(callee()->signature()->size() == 7, "arraySort has 6 parameters (one long)");
Node* elementType = null_check(argument(0));
// no receiver because it is a static method
Node* elementType = argument(0);
Node* obj = argument(1);
Node* offset = argument(2);
Node* offset = argument(2); // long
Node* fromIndex = argument(4);
Node* toIndex = argument(5);
// SortOperation: argument(6) is ignored
const TypeInstPtr* elem_klass = gvn().type(elementType)->isa_instptr();
ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type();
BasicType bt = elem_type->basic_type();
// Disable the intrinsic if the CPU does not support SIMD sort
if (!Matcher::supports_simd_sort(bt)) {
BasicType bt = T_ILLEGAL;
if (!check_array_sort_arguments(elementType, obj, bt)) {
return false;
}
address stubAddr = nullptr;
stubAddr = StubRoutines::select_arraysort_function();
//stub not loaded
if (stubAddr == nullptr) {
return false;
}
// get address of the array
const TypeAryPtr* obj_t = _gvn.type(obj)->isa_aryptr();
if (obj_t == nullptr || obj_t->elem() == Type::BOTTOM ) {
return false; // failed input validation
null_check(obj);
// If obj is dead, only null-path is taken.
if (stopped()) {
return true;
}
Node* obj_adr = make_unsafe_address(obj, offset);
@@ -7554,8 +7582,6 @@ bool LibraryCallKit::inline_intpoly_montgomeryMult_P256() {
OptoRuntime::intpoly_montgomeryMult_P256_Type(),
stubAddr, stubName, TypePtr::BOTTOM,
a_start, b_start, r_start);
Node* result = _gvn.transform(new ProjNode(call, TypeFunc::Parms));
set_result(result);
return true;
}

View File

@@ -279,6 +279,7 @@ class LibraryCallKit : public GraphKit {
JVMState* arraycopy_restore_alloc_state(AllocateArrayNode* alloc, int& saved_reexecute_sp);
void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms_before_guards, int saved_reexecute_sp,
uint new_idx);
bool check_array_sort_arguments(Node* elementType, Node* obj, BasicType& bt);
bool inline_array_sort();
bool inline_array_partition();
typedef enum { LS_get_add, LS_get_set, LS_cmp_swap, LS_cmp_swap_weak, LS_cmp_exchange } LoadStoreKind;

View File

@@ -46,7 +46,7 @@ private:
Eliminated // All lock/unlock in region were eliminated
} _kind;
#ifdef ASSERT
#ifndef PRODUCT
const char* _kind_name[6] = {
"Regular",
"Local",
@@ -124,7 +124,9 @@ public:
#ifndef PRODUCT
virtual void format( PhaseRegAlloc *, outputStream *st ) const;
virtual void dump_spec(outputStream *st) const { st->print(" Lock %d",_slot); }
virtual void dump_spec(outputStream *st) const {
st->print(" Lock slot: %d, Kind: %s", _slot, _kind_name[(int)_kind]);
}
#endif
};

View File

@@ -2571,7 +2571,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
const TypeInt* init_t = phase->type(in(Init) )->is_int();
const TypeInt* limit_t = phase->type(in(Limit))->is_int();
int stride_p;
jlong stride_p;
jlong lim, ini;
julong max;
if (stride_con > 0) {
@@ -2580,10 +2580,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) {
ini = init_t->_lo;
max = (julong)max_jint;
} else {
stride_p = -stride_con;
stride_p = -(jlong)stride_con;
lim = init_t->_hi;
ini = limit_t->_lo;
max = (julong)min_jint;
max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000
}
julong range = lim - ini + stride_p;
if (range <= max) {

View File

@@ -795,18 +795,25 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) {
// Avoid duplicated float compare.
if (phis > 1 && (cmp_op == Op_CmpF || cmp_op == Op_CmpD)) return nullptr;
// Ignore cost if CMOVE can be moved outside the loop.
if (used_inside_loop && cost >= ConditionalMoveLimit) {
return nullptr;
float infrequent_prob = PROB_UNLIKELY_MAG(3);
// Ignore cost and blocks frequency if CMOVE can be moved outside the loop.
if (used_inside_loop) {
if (cost >= ConditionalMoveLimit) return nullptr; // Too much goo
// BlockLayoutByFrequency optimization moves infrequent branch
// from hot path. No point in CMOV'ing in such case (110 is used
// instead of 100 to take into account not exactness of float value).
if (BlockLayoutByFrequency) {
infrequent_prob = MAX2(infrequent_prob, (float)BlockLayoutMinDiamondPercentage/110.0f);
}
}
// Check for highly predictable branch. No point in CMOV'ing if
// we are going to predict accurately all the time.
constexpr float infrequent_prob = PROB_UNLIKELY_MAG(2);
if (C->use_cmove() && (cmp_op == Op_CmpF || cmp_op == Op_CmpD)) {
//keep going
} else if (iff->_prob < infrequent_prob || iff->_prob > (1.0f - infrequent_prob)) {
} else if (iff->_prob < infrequent_prob ||
iff->_prob > (1.0f - infrequent_prob))
return nullptr;
}
// --------------
// Now replace all Phis with CMOV's

View File

@@ -2045,7 +2045,7 @@ void PhaseMacroExpand::mark_eliminated_box(Node* box, Node* obj) {
//-----------------------mark_eliminated_locking_nodes-----------------------
void PhaseMacroExpand::mark_eliminated_locking_nodes(AbstractLockNode *alock) {
if (alock->box_node()->as_BoxLock()->is_unbalanced()) {
if (!alock->is_balanced()) {
return; // Can't do any more elimination for this locking region
}
if (EliminateNestedLocks) {

View File

@@ -2984,6 +2984,9 @@ StoreNode* MergePrimitiveArrayStores::run() {
type2aelembytes(bt) != _store->memory_size()) {
return nullptr;
}
if (_store->is_unsafe_access()) {
return nullptr;
}
// The _store must be the "last" store in a chain. If we find a use we could merge with
// then that use or a store further down is the "last" store.
@@ -3017,11 +3020,13 @@ bool MergePrimitiveArrayStores::is_compatible_store(const StoreNode* other_store
int opc = _store->Opcode();
assert(opc == Op_StoreB || opc == Op_StoreC || opc == Op_StoreI, "precondition");
assert(_store->adr_type()->isa_aryptr() != nullptr, "must be array store");
assert(!_store->is_unsafe_access(), "no unsafe accesses");
if (other_store == nullptr ||
_store->Opcode() != other_store->Opcode() ||
other_store->adr_type() == nullptr ||
other_store->adr_type()->isa_aryptr() == nullptr) {
other_store->adr_type()->isa_aryptr() == nullptr ||
other_store->is_unsafe_access()) {
return false;
}

View File

@@ -1414,8 +1414,8 @@ const TypeFunc* OptoRuntime::intpoly_montgomeryMult_P256_Type() {
// result type needed
fields = TypeTuple::fields(1);
fields[TypeFunc::Parms + 0] = TypeInt::INT; // carry bits in output
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms+1, fields);
fields[TypeFunc::Parms + 0] = nullptr; // void
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
return TypeFunc::make(domain, range);
}
@@ -1434,7 +1434,7 @@ const TypeFunc* OptoRuntime::intpoly_assign_Type() {
// result type needed
fields = TypeTuple::fields(1);
fields[TypeFunc::Parms + 0] = NULL; // void
fields[TypeFunc::Parms + 0] = nullptr; // void
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
return TypeFunc::make(domain, range);
}

View File

@@ -417,6 +417,10 @@ VPointer::VPointer(const MemNode* mem, const VLoop& vloop,
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
_has_int_index_after_convI2L(false),
_int_index_after_convI2L_offset(0),
_int_index_after_convI2L_invar(nullptr),
_int_index_after_convI2L_scale(0),
_nstack(nstack), _analyze_only(analyze_only), _stack_idx(0)
#ifndef PRODUCT
, _tracer(vloop.is_trace_pointer_analysis())
@@ -496,6 +500,11 @@ VPointer::VPointer(const MemNode* mem, const VLoop& vloop,
return;
}
if (!is_safe_to_use_as_simple_form(base, adr)) {
assert(!valid(), "does not have simple form");
return;
}
_base = base;
_adr = adr;
assert(valid(), "Usable");
@@ -509,6 +518,10 @@ VPointer::VPointer(VPointer* p) :
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
_has_int_index_after_convI2L(false),
_int_index_after_convI2L_offset(0),
_int_index_after_convI2L_invar(nullptr),
_int_index_after_convI2L_scale(0),
_nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx)
#ifndef PRODUCT
, _tracer(p->_tracer._is_trace_alignment)
@@ -531,6 +544,354 @@ int VPointer::invar_factor() const {
return 1;
}
// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency
// (i.e. which loads/stores can be packed) based on the simple form:
//
// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
//
// However, we parse the compound-long-int form:
//
// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
// int_index = int_offset + int_invar + int_scale * iv
//
// In general, the simple and the compound-long-int form do not always compute the same pointer
// at runtime. For example, the simple form would give a different result due to an overflow
// in the int_index.
//
// Example:
// For both forms, we have:
// iv = 0
// scale = 1
//
// We now account the offset and invar once to the long part and once to the int part:
// Pointer 1 (long offset and long invar):
// long_offset = min_int
// long_invar = min_int
// int_offset = 0
// int_invar = 0
//
// Pointer 2 (int offset and int invar):
// long_offset = 0
// long_invar = 0
// int_offset = min_int
// int_invar = min_int
//
// This gives us the following pointers:
// Compound-long-int form pointers:
// Form:
// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv)
//
// Pointers:
// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0)
// = adr + min_int + min_int
// = adr - 2^32
//
// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0)
// = adr + ConvI2L(min_int + min_int)
// = adr + 0
// = adr
//
// Simple form pointers:
// Form:
// s_pointer = adr + offset + invar + scale * ConvI2L(iv)
// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv)
//
// Pointers:
// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0
// = adr + min_int + min_int
// = adr - 2^32
// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0
// = adr + min_int + min_int
// = adr - 2^32
//
// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical.
//
// Hence, we need to determine in which cases it is safe to make decisions based on the simple
// form, rather than the compound-long-int form. If we cannot prove that using the simple form
// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid VPointer,
// and the associated memop cannot be vectorized.
bool VPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const {
#ifndef _LP64
// On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the
// parsed pointer form is always the simple form, with int operations:
//
// pointer = adr + offset + invar + scale * iv
//
assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv");
return true;
#else
// Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no
// int_index overflow. This implies that the conversion to long can be done separately:
//
// ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv)
//
// And hence, the simple form is guaranteed to be identical to the compound-long-int form at
// runtime and the VPointer is safe/valid to be used.
const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr();
if (ary_ptr_t != nullptr) {
if (!_mem->is_unsafe_access()) {
return true;
}
}
// We did not find the int_index. Just to be safe, reject this VPointer.
if (!_has_int_index_after_convI2L) {
return false;
}
int int_offset = _int_index_after_convI2L_offset;
Node* int_invar = _int_index_after_convI2L_invar;
int int_scale = _int_index_after_convI2L_scale;
int long_scale = _scale / int_scale;
// If "int_index = iv", then the simple form is identical to the compound-long-int form.
//
// int_index = int_offset + int_invar + int_scale * iv
// = 0 0 1 * iv
// = iv
if (int_offset == 0 && int_invar == nullptr && int_scale == 1) {
return true;
}
// Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge":
//
// pointer1 = adr + ConvI2L(int_index1)
// pointer2 = adr + ConvI2L(int_index2)
//
// int_index1 = max_int + 0 = max_int -> very close to but before the overflow
// int_index2 = max_int + 1 = min_int -> just enough to get the overflow
//
// When looking at the difference of pointer1 and pointer2, we notice that it is very large
// (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is
// an actual out-of-bounds access at runtime. These would normally be prevented by range checks
// at runtime. However, if the access was done by using Unsafe, where range checks are omitted,
// then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to
// do anything, including changing the behavior.
//
// If we can set the right conditions, we have a guarantee that an overflow is either impossible
// (no overflow or range checks preventing that) or undefined behavior. In both cases, we are
// safe to do a vectorization.
//
// Approach: We want to prove a lower bound for the distance between these two pointers, and an
// upper bound for the size of a memory object. We can derive such an upper bound for
// arrays. We know they have at most 2^31 elements. If we know the size of the elements
// in bytes, we have:
//
// array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes
// >= array_size_in_bytes (ARR)
//
// If some small difference "delta" leads to an int_index overflow, we know that the
// int_index1 before overflow must have been close to max_int, and the int_index2 after
// the overflow must be close to min_int:
//
// pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1)
// =approx adr + long_offset + long_invar + long_scale * max_int
//
// pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2)
// =approx adr + long_offset + long_invar + long_scale * min_int
//
// We realize that the pointer difference is very large:
//
// difference =approx long_scale * 2^32
//
// Hence, if we set the right condition for long_scale and array_element_size_in_bytes,
// we can prove that an overflow is impossible (or would imply undefined behaviour).
//
// We must now take this intuition, and develop a rigorous proof. We start by stating the problem
// more precisely, with the help of some definitions and the Statement we are going to prove.
//
// Definition:
// Two VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()),
// iff all of these conditions apply for the simple form:
// 1) Both VPointers are valid.
// 2) The adr are identical, or both are array bases of different arrays.
// 3) They have identical scale.
// 4) They have identical invar.
// 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF)
//
// For the Vectorization Optimization, we pair-wise compare VPointers and determine if they are:
// 1) "not comparable":
// We do not optimize them (assume they alias, not assume adjacency).
//
// Whenever we chose this option based on the simple form, it is also correct based on the
// compound-long-int form, since we make no optimizations based on it.
//
// 2) "comparable" with different array bases at runtime:
// We assume they do not alias (remove memory edges), but not assume adjacency.
//
// Whenever we have two different array bases for the simple form, we also have different
// array bases for the compound-long-form. Since VPointers provably point to different
// memory objects, they can never alias.
//
// 3) "comparable" with the same base address:
// We compute the relative pointer difference, and based on the load/store size we can
// compute aliasing and adjacency.
//
// We must find a condition under which the pointer difference of the simple form is
// identical to the pointer difference of the compound-long-form. We do this with the
// Statement below, which we then proceed to prove.
//
// Statement:
// If two VPointers satisfy these 3 conditions:
// 1) They are "comparable".
// 2) They have the same base address.
// 3) Their long_scale is a multiple of the array element size in bytes:
//
// abs(long_scale) % array_element_size_in_bytes = 0 (A)
//
// Then their pointer difference of the simple form is identical to the pointer difference
// of the compound-long-int form.
//
// More precisely:
// Such two VPointers by definition have identical adr, invar, and scale.
// Their simple form is:
//
// s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1)
// s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2)
//
// Thus, the pointer difference of the simple forms collapses to the difference in offsets:
//
// s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C)
//
// Their compound-long-int form for these VPointer is:
//
// c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1)
// int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2)
//
// c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3)
// int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4)
//
// And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2):
//
// offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5)
// offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6)
//
// invar = long_invar1 + long_scale1 * ConvI2L(int_invar1)
// = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7)
//
// scale = long_scale1 * ConvI2L(int_scale1)
// = long_scale2 * ConvI2L(int_scale2) (D8)
//
// The pointer difference of the compound-long-int form is defined as:
//
// c_difference = c_pointer1 - c_pointer2
//
// Thus, the statement claims that for the two VPointer we have:
//
// s_difference = c_difference (Statement)
//
// We prove the Statement with the help of a Lemma:
//
// Lemma:
// There is some integer x, such that:
//
// c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma)
//
// From condition (DIFF), we can derive:
//
// abs(s_difference) < 2^31 (E)
//
// Assuming the Lemma, we prove the Statement:
// If "x = 0" (intuitively: the int_index does not overflow), then:
// c_difference = s_difference
// and hence the simple form computes the same pointer difference as the compound-long-int form.
// If "x != 0" (intuitively: the int_index overflows), then:
// abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32)
// >= array_element_size_in_bytes * 2^32 - abs(s_difference)
// -- apply (E) --
// > array_element_size_in_bytes * 2^32 - 2^31
// >= array_element_size_in_bytes * 2^31
// -- apply (ARR) --
// >= max_possible_array_size_in_bytes
// >= array_size_in_bytes
//
// This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size.
// Thus, at least one of the two pointers must be outside of the array bounds. But we can assume
// that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence,
// we are allowed to do anything. We can also "safely" use the simple form in this case even though
// it might not match the compound-long-int form at runtime.
// QED Statement.
//
// We must now prove the Lemma.
//
// ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that:
//
// ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F)
//
// It follows, that there is an integer y1 such that:
//
// ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv)
// -- apply (F) --
// = ConvI2L(int_offset1)
// + ConvI2L(int_invar1)
// + ConvI2L(int_scale1) * ConvI2L(iv)
// + y1 * 2^32 (G)
//
// Thus, we can write the compound-long-int form (D1) as:
//
// c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1)
// -- apply (G) --
// = adr
// + long_offset1
// + long_invar1
// + long_scale1 * ConvI2L(int_offset1)
// + long_scale1 * ConvI2L(int_invar1)
// + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv)
// + long_scale1 * y1 * 2^32 (H)
//
// And we can write the simple form as:
//
// s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv)
// -- apply (D5, D7, D8) --
// = adr
// + long_offset1
// + long_scale1 * ConvI2L(int_offset1)
// + long_invar1
// + long_scale1 * ConvI2L(int_invar1)
// + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K)
//
// We now compute the pointer difference between the simple (K) and compound-long-int form (H).
// Most terms cancel out immediately:
//
// sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L)
//
// Rearranging the equation (L), we get:
//
// c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M)
//
// And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer
// x1, such that (M) implies:
//
// c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N)
//
// With an analogue equation for c_pointer2, we can now compute the pointer difference for
// the compound-long-int form:
//
// c_difference = c_pointer1 - c_pointer2
// -- apply (N) --
// = s_pointer1 + array_element_size_in_bytes * x1 * 2^32
// -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32)
// -- where "x = x1 - x2" --
// = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32
// -- apply (C) --
// = s_difference + array_element_size_in_bytes * x * 2^32
// QED Lemma.
if (ary_ptr_t != nullptr) {
BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type();
if (is_java_primitive(array_element_bt)) {
int array_element_size_in_bytes = type2aelembytes(array_element_bt);
if (abs(long_scale) % array_element_size_in_bytes == 0) {
return true;
}
}
}
// General case: we do not know if it is safe to use the simple form.
return false;
#endif
}
bool VPointer::is_loop_member(Node* n) const {
Node* n_c = phase()->get_ctrl(n);
return lpt()->is_member(phase()->get_loop(n_c));
@@ -633,6 +994,37 @@ bool VPointer::scaled_iv(Node* n) {
NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);)
return true;
}
} else if (opc == Op_ConvI2L && !has_iv()) {
// So far we have not found the iv yet, and are about to enter a ConvI2L subgraph,
// which may be the int index (that might overflow) for the memory access, of the form:
//
// int_index = int_offset + int_invar + int_scale * iv
//
// If we simply continue parsing with the current VPointer, then the int_offset and
// int_invar simply get added to the long offset and invar. But for the checks in
// VPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the
// int_index. Thus, we must parse it explicitly here. For this, we use a temporary
// VPointer, to pattern match the int_index sub-expression of the address.
NOT_PRODUCT(Tracer::Depth dddd;)
VPointer tmp(this);
NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);)
if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) {
// We successfully matched an integer index, of the form:
// int_index = int_offset + int_invar + int_scale * iv
_has_int_index_after_convI2L = true;
_int_index_after_convI2L_offset = tmp._offset;
_int_index_after_convI2L_invar = tmp._invar;
_int_index_after_convI2L_scale = tmp._scale;
}
// Now parse it again for the real VPointer. This makes sure that the int_offset, int_invar,
// and int_scale are properly added to the final VPointer's offset, invar, and scale.
if (scaled_iv_plus_offset(n->in(1))) {
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
return true;
}
} else if (opc == Op_ConvI2L || opc == Op_CastII) {
if (scaled_iv_plus_offset(n->in(1))) {
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
@@ -649,8 +1041,17 @@ bool VPointer::scaled_iv(Node* n) {
if (tmp.scaled_iv_plus_offset(n->in(1))) {
int scale = n->in(2)->get_int();
// Accumulate scale.
_scale = tmp._scale << scale;
_offset += tmp._offset << scale;
// Accumulate offset.
int shifted_offset = 0;
if (!try_LShiftI_no_overflow(tmp._offset, scale, shifted_offset)) {
return false; // shift overflow.
}
if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) {
return false; // add overflow.
}
// Accumulate invar.
if (tmp._invar != nullptr) {
BasicType bt = tmp._invar->bottom_type()->basic_type();
assert(bt == T_INT || bt == T_LONG, "");
@@ -659,6 +1060,13 @@ bool VPointer::scaled_iv(Node* n) {
_debug_invar_scale = n->in(2);
#endif
}
// Forward info about the int_index:
_has_int_index_after_convI2L = tmp._has_int_index_after_convI2L;
_int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset;
_int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar;
_int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale;
NOT_PRODUCT(_tracer.scaled_iv_9(n, _scale, _offset, _invar);)
return true;
}
@@ -676,7 +1084,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
int opc = n->Opcode();
if (opc == Op_ConI) {
_offset += negate ? -(n->get_int()) : n->get_int();
if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) {
return false; // add/sub overflow.
}
NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);)
return true;
} else if (opc == Op_ConL) {
@@ -685,7 +1095,9 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (t->higher_equal(TypeLong::INT)) {
jlong loff = n->get_long();
jint off = (jint)loff;
_offset += negate ? -off : loff;
if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) {
return false; // add/sub overflow.
}
NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);)
return true;
}
@@ -700,11 +1112,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_AddI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
_offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) {
return false; // add/sub overflow.
}
NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, negate, _offset);)
return true;
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
_offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
return false; // add/sub overflow.
}
maybe_add_to_invar(n->in(2), negate);
NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);)
return true;
@@ -713,11 +1129,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_SubI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
_offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int();
if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) {
return false; // add/sub overflow.
}
NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, negate, _offset);)
return true;
} else if (n->in(1)->is_Con() && invariant(n->in(2))) {
_offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int();
if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) {
return false; // add/sub overflow.
}
maybe_add_to_invar(n->in(2), !negate);
NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);)
return true;
@@ -807,6 +1227,44 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) {
_invar = register_if_new(add);
}
bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) {
jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2));
jint int_offset = java_add( offset1, offset2);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) {
jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2));
jint int_offset = java_subtract( offset1, offset2);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
bool VPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) {
if (is_sub) {
return try_SubI_no_overflow(offset1, offset2, result);
} else {
return try_AddI_no_overflow(offset1, offset2, result);
}
}
bool VPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) {
jlong long_offset = java_shift_left((jlong)(offset), shift);
jint int_offset = java_shift_left( offset, shift);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
#ifndef PRODUCT
// Function for printing the fields of a VPointer
void VPointer::print() const {

View File

@@ -667,13 +667,51 @@ private:
// A vectorization pointer (VPointer) has information about an address for
// dependence checking and vector alignment. It's usually bound to a memory
// operation in a counted loop for vectorizable analysis.
//
// We parse and represent pointers of the simple form:
//
// pointer = adr + offset + invar + scale * ConvI2L(iv)
//
// Where:
//
// adr: the base address of an array (base = adr)
// OR
// some address to off-heap memory (base = TOP)
//
// offset: a constant offset
// invar: a runtime variable, which is invariant during the loop
// scale: scaling factor
// iv: loop induction variable
//
// But more precisely, we parse the composite-long-int form:
//
// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv)
//
// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index)
// int_index = int_offset + int_invar + int_scale * iv
//
// However, for aliasing and adjacency checks (e.g. VPointer::cmp()) we always use the simple form to make
// decisions. Hence, we must make sure to only create a "valid" VPointer if the optimisations based on the
// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on
// if the int_index overflows, but the precise conditions are given in VPointer::is_safe_to_use_as_simple_form().
//
// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv)
// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv)
//
// scale = long_scale * ConvI2L(int_scale)
// offset = long_offset + long_scale * ConvI2L(int_offset)
// invar = long_invar + long_scale * ConvI2L(int_invar)
//
// pointer = adr + offset + invar + scale * ConvI2L(iv)
//
class VPointer : public ArenaObj {
protected:
const MemNode* _mem; // My memory reference node
const VLoop& _vloop;
Node* _base; // null if unsafe nonheap reference
Node* _adr; // address pointer
// Components of the simple form:
Node* _base; // Base address of an array OR null if some off-heap memory.
Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer.
int _scale; // multiplier for iv (in bytes), 0 if no loop iv
int _offset; // constant offset (in bytes)
@@ -684,6 +722,13 @@ class VPointer : public ArenaObj {
Node* _debug_invar_scale; // multiplier for invariant
#endif
// The int_index components of the compound-long-int form. Used to decide if it is safe to use the
// simple form rather than the compound-long-int form that was parsed.
bool _has_int_index_after_convI2L;
int _int_index_after_convI2L_offset;
Node* _int_index_after_convI2L_invar;
int _int_index_after_convI2L_scale;
Node_Stack* _nstack; // stack used to record a vpointer trace of variants
bool _analyze_only; // Used in loop unrolling only for vpointer trace
uint _stack_idx; // Used in loop unrolling only for vpointer trace
@@ -723,6 +768,8 @@ class VPointer : public ArenaObj {
VPointer(VPointer* p);
NONCOPYABLE(VPointer);
bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const;
public:
bool valid() const { return _adr != nullptr; }
bool has_iv() const { return _scale != 0; }
@@ -748,10 +795,43 @@ class VPointer : public ArenaObj {
return _invar == q._invar;
}
// We compute if and how two VPointers can alias at runtime, i.e. if the two addressed regions of memory can
// ever overlap. There are essentially 3 relevant return states:
// - NotComparable: Synonymous to "unknown aliasing".
// We have no information about how the two VPointers can alias. They could overlap, refer
// to another location in the same memory object, or point to a completely different object.
// -> Memory edge required. Aliasing unlikely but possible.
//
// - Less / Greater: Synonymous to "never aliasing".
// The two VPointers may point into the same memory object, but be non-aliasing (i.e. we
// know both address regions inside the same memory object, but these regions are non-
// overlapping), or the VPointers point to entirely different objects.
// -> No memory edge required. Aliasing impossible.
//
// - Equal: Synonymous to "overlap, or point to different memory objects".
// The two VPointers either overlap on the same memory object, or point to two different
// memory objects.
// -> Memory edge required. Aliasing likely.
//
// In a future refactoring, we can simplify to two states:
// - NeverAlias: instead of Less / Greater
// - MayAlias: instead of Equal / NotComparable
//
// Two VPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply:
// 1) Both are valid, i.e. expressible in the compound-long-int or simple form.
// 2) The adr are identical, or both are array bases of different arrays.
// 3) They have identical scale.
// 4) They have identical invar.
// 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31.
int cmp(const VPointer& q) const {
if (valid() && q.valid() &&
(_adr == q._adr || (_base == _adr && q._base == q._adr)) &&
_scale == q._scale && invar_equals(q)) {
jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset));
jlong max_diff = (jlong)1 << 31;
if (difference >= max_diff) {
return NotComparable;
}
bool overlap = q._offset < _offset + memory_size() &&
_offset < q._offset + q.memory_size();
return overlap ? Equal : (_offset < q._offset ? Less : Greater);
@@ -851,6 +931,11 @@ class VPointer : public ArenaObj {
void maybe_add_to_invar(Node* new_invar, bool negate);
static bool try_AddI_no_overflow(int offset1, int offset2, int& result);
static bool try_SubI_no_overflow(int offset1, int offset2, int& result);
static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result);
static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result);
Node* register_if_new(Node* n) const;
};

View File

@@ -2339,7 +2339,7 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
}
// Iterate over all the modules loaded to the system.
ClassLoaderDataGraph::modules_do(&do_module);
ClassLoaderDataGraph::modules_do_keepalive(&do_module);
jint len = _tbl->length();
guarantee(len > 0, "at least one module must be present");

View File

@@ -105,7 +105,7 @@ JvmtiGetLoadedClasses::getLoadedClasses(JvmtiEnv *env, jint* classCountPtr, jcla
// Iterate through all classes in ClassLoaderDataGraph
// and collect them using the LoadedClassesClosure
MutexLocker mcld(ClassLoaderDataGraph_lock);
ClassLoaderDataGraph::loaded_classes_do(&closure);
ClassLoaderDataGraph::loaded_classes_do_keepalive(&closure);
}
return closure.get_result(env, classCountPtr, classesPtr);

View File

@@ -55,6 +55,7 @@
#include "runtime/threadSMR.hpp"
#include "runtime/vmOperations.hpp"
#include "runtime/vm_version.hpp"
#include "sanitizers/ub.hpp"
#include "services/threadService.hpp"
#include "utilities/align.hpp"
#include "utilities/copy.hpp"
@@ -242,6 +243,9 @@ public:
return normalize_for_read(*addr());
}
// we use this method at some places for writing to 0 e.g. to cause a crash;
// ubsan does not know that this is the desired behavior
ATTRIBUTE_NO_UBSAN
void put(T x) {
GuardUnsafeAccess guard(_thread);
*addr() = normalize_for_write(x);

View File

@@ -1442,7 +1442,7 @@ bool ObjectMonitor::check_owner(TRAPS) {
static inline bool is_excluded(const Klass* monitor_klass) {
assert(monitor_klass != nullptr, "invariant");
NOT_JFR_RETURN_(false);
JFR_ONLY(return vmSymbols::jfr_chunk_rotation_monitor() == monitor_klass->name();)
JFR_ONLY(return vmSymbols::jdk_jfr_internal_HiddenWait() == monitor_klass->name();)
}
static void post_monitor_wait_event(EventJavaMonitorWait* event,

View File

@@ -1333,7 +1333,7 @@ methodHandle SharedRuntime::resolve_helper(bool is_virtual, bool is_optimized, T
if (invoke_code == Bytecodes::_invokestatic) {
assert(callee_method->method_holder()->is_initialized() ||
callee_method->method_holder()->is_init_thread(current),
callee_method->method_holder()->is_reentrant_initialization(current),
"invalid class initialization state for invoke_static");
if (!VM_Version::supports_fast_class_init_checks() && callee_method->needs_clinit_barrier()) {
// In order to keep class initialization check, do not patch call

View File

@@ -821,6 +821,16 @@ int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
return ret_code;
}
void ObjectSynchronizer::waitUninterruptibly(Handle obj, jlong millis, TRAPS) {
if (millis < 0) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_wait)->wait(millis, false, THREAD);
}
void ObjectSynchronizer::notify(Handle obj, TRAPS) {
JavaThread* current = THREAD;

View File

@@ -119,6 +119,11 @@ public:
static bool quick_notify(oopDesc* obj, JavaThread* current, bool All);
static bool quick_enter(oop obj, JavaThread* current, BasicLock* Lock);
// Special internal-use-only method for use by JVM infrastructure
// that needs to wait() on a java-level object but that can't risk
// throwing unexpected InterruptedExecutionExceptions.
static void waitUninterruptibly(Handle obj, jlong Millis, TRAPS);
// Inflate light weight monitor to heavy weight monitor
static ObjectMonitor* inflate(Thread* current, oop obj, const InflateCause cause);
// Used to inflate a monitor as if it was done from the thread JavaThread.
@@ -225,6 +230,7 @@ class ObjectLocker : public StackObj {
// Monitor behavior
void wait(TRAPS) { ObjectSynchronizer::wait(_obj, 0, CHECK); } // wait forever
void wait_uninterruptibly(TRAPS) { ObjectSynchronizer::waitUninterruptibly(_obj, 0, CHECK); } // wait forever
void notify_all(TRAPS) { ObjectSynchronizer::notifyall(_obj, CHECK); }
};

View File

@@ -205,8 +205,9 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
Klass* k = obj->klass();
st->print_cr("\t- %s <" INTPTR_FORMAT "> (a %s)", "parking to wait for ", p2i(obj), k->external_name());
}
else if (thread()->osthread()->get_state() == CONDVAR_WAIT) {
// We are waiting on the native class initialization monitor.
else if (thread()->osthread()->get_state() == OBJECT_WAIT) {
// We are waiting on an Object monitor but Object.wait() isn't the
// top-frame, so we should be waiting on a Class initialization monitor.
InstanceKlass* k = thread()->class_to_be_initialized();
if (k != nullptr) {
st->print_cr("\t- waiting on the Class initialization monitor for %s", k->external_name());

View File

@@ -246,6 +246,7 @@
nonstatic_field(InstanceKlass, _nonstatic_oop_map_size, int) \
volatile_nonstatic_field(InstanceKlass, _init_state, InstanceKlass::ClassState) \
volatile_nonstatic_field(InstanceKlass, _init_thread, JavaThread*) \
nonstatic_field(InstanceKlass, _is_marked_dependent, bool) \
nonstatic_field(InstanceKlass, _itable_len, int) \
nonstatic_field(InstanceKlass, _nest_host_index, u2) \
nonstatic_field(InstanceKlass, _reference_type, u1) \
@@ -2163,7 +2164,6 @@
\
declare_constant(InstanceKlass::allocated) \
declare_constant(InstanceKlass::loaded) \
declare_constant(InstanceKlass::being_linked) \
declare_constant(InstanceKlass::linked) \
declare_constant(InstanceKlass::being_initialized) \
declare_constant(InstanceKlass::fully_initialized) \

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please 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_SANITIZERS_UB_HPP
#define SHARE_SANITIZERS_UB_HPP
// ATTRIBUTE_NO_UBSAN
//
// Function attribute which informs the compiler to disable UBSan checks in the
// following function or method.
// Useful if the function or method is known to do something special or even 'dangerous', for
// example causing desired signals/crashes.
#if defined(__clang__) || defined(__GNUC__)
#define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined")))
#endif
#ifndef ATTRIBUTE_NO_UBSAN
#define ATTRIBUTE_NO_UBSAN
#endif
#endif // SHARE_SANITIZERS_UB_HPP

View File

@@ -1089,6 +1089,14 @@ u4 DumperSupport::get_static_fields_size(InstanceKlass* ik, u2& field_count) {
}
}
// Also provide a pointer to the init_lock if present, so there aren't unreferenced int[0]
// arrays.
oop init_lock = ik->init_lock();
if (init_lock != nullptr) {
field_count++;
size += sizeof(address);
}
// We write the value itself plus a name and a one byte type tag per field.
return checked_cast<u4>(size + field_count * (sizeof(address) + 1));
}
@@ -1126,6 +1134,14 @@ void DumperSupport::dump_static_fields(AbstractDumpWriter* writer, Klass* k) {
prev = prev->previous_versions();
}
}
// Add init lock to the end if the class is not yet initialized
oop init_lock = ik->init_lock();
if (init_lock != nullptr) {
writer->write_symbolID(vmSymbols::init_lock_name()); // name
writer->write_u1(sig2tag(vmSymbols::int_array_signature())); // type
writer->write_objectID(init_lock);
}
}
// dump the raw values of the instance fields of the given object

View File

@@ -60,6 +60,7 @@
#include "runtime/vmOperations.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_version.hpp"
#include "sanitizers/ub.hpp"
#include "utilities/debug.hpp"
#include "utilities/decoder.hpp"
#include "utilities/defaultStream.hpp"
@@ -2086,9 +2087,7 @@ typedef void (*voidfun_t)();
// compared to one generated with raise (asynchronous vs synchronous). See JDK-8065895.
volatile int sigfpe_int = 0;
#if defined(__clang__) || defined(__GNUC__)
__attribute__((no_sanitize("undefined")))
#endif
ATTRIBUTE_NO_UBSAN
static void ALWAYSINLINE crash_with_sigfpe() {
// generate a native synchronous SIGFPE where possible;

View File

@@ -222,7 +222,7 @@ import java.util.stream.Stream;
* <pre>
* MemoryLayout.structLayout(
* ValueLayout.JAVA_INT.withName("x"),
* MemoryLayout.paddingLayout(32),
* MemoryLayout.paddingLayout(4),
* ValueLayout.JAVA_LONG.withName("y")
* );
* </pre>

View File

@@ -369,7 +369,7 @@ import jdk.internal.foreign.layout.UnionLayoutImpl;
* int size = ...
* MemorySegment points = ...
* for (int i = 0 ; i < size ; i++) {
* ... POINT_ARR_X.get(segment, 0L, (long)i) ...
* ... POINT_ARR_X.get(points, 0L, (long)i) ...
* }
* }
*

View File

@@ -630,6 +630,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* {@snippet lang=java :
* asSlice(offset, newSize, 1);
* }
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @see #asSlice(long, long, long)
*
@@ -646,6 +649,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* Returns a slice of this memory segment, at the given offset, with the provided
* alignment constraint. The returned segment's address is the address of this
* segment plus the given offset; its size is specified by the given argument.
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @param offset The new segment base offset (relative to the address of this segment),
* specified in bytes
@@ -670,6 +676,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* {@snippet lang=java :
* asSlice(offset, layout.byteSize(), layout.byteAlignment());
* }
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @see #asSlice(long, long, long)
*
@@ -693,6 +702,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* {@snippet lang=java :
* asSlice(offset, byteSize() - offset);
* }
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @see #asSlice(long, long)
*
@@ -706,6 +718,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
/**
* Returns a new memory segment that has the same address and scope as this segment,
* but with the provided size.
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @param newSize the size of the returned segment
* @return a new memory segment that has the same address and scope as
@@ -741,6 +756,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* That is, the cleanup action receives a segment that is associated with the global
* scope, and is accessible from any thread. The size of the segment accepted by the
* cleanup action is {@link #byteSize()}.
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @apiNote The cleanup action (if present) should take care not to leak the received
* segment to external clients that might access the segment after its
@@ -786,6 +804,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
* That is, the cleanup action receives a segment that is associated with the global
* scope, and is accessible from any thread. The size of the segment accepted by the
* cleanup action is {@code newSize}.
* <p>
* The returned memory segment shares a region of backing memory with this segment.
* Hence, no memory will be allocated or freed by this method.
*
* @apiNote The cleanup action (if present) should take care not to leak the received
* segment to external clients that might access the segment after its

View File

@@ -39,7 +39,6 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import jdk.internal.constant.ConstantUtils;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.ReferenceClassDescImpl;
import sun.security.action.GetBooleanAction;
@@ -47,10 +46,7 @@ import sun.security.action.GetBooleanAction;
import static java.lang.classfile.ClassFile.*;
import java.lang.classfile.attribute.StackMapFrameInfo;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.constant.ConstantDescs;
import static java.lang.constant.ConstantDescs.*;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc;
/**
* ProxyGenerator contains the code to generate a dynamic proxy class
@@ -65,7 +61,10 @@ final class ProxyGenerator {
ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS);
private static final ClassDesc
CD_ClassLoader = ReferenceClassDescImpl.ofValidated("Ljava/lang/ClassLoader;"),
CD_Class_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Class;"),
CD_ClassNotFoundException = ReferenceClassDescImpl.ofValidated("Ljava/lang/ClassNotFoundException;"),
CD_NoClassDefFoundError = ReferenceClassDescImpl.ofValidated("Ljava/lang/NoClassDefFoundError;"),
CD_IllegalAccessException = ReferenceClassDescImpl.ofValidated("Ljava/lang/IllegalAccessException;"),
CD_InvocationHandler = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/InvocationHandler;"),
CD_Method = ReferenceClassDescImpl.ofValidated("Ljava/lang/reflect/Method;"),
@@ -81,8 +80,9 @@ final class ProxyGenerator {
MTD_void_String = MethodTypeDescImpl.ofValidated(CD_void, CD_String),
MTD_void_Throwable = MethodTypeDescImpl.ofValidated(CD_void, CD_Throwable),
MTD_Class = MethodTypeDescImpl.ofValidated(CD_Class),
MTD_Class_array = MethodTypeDescImpl.ofValidated(CD_Class_array),
MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, ConstantDescs.CD_String, CD_Class_array),
MTD_Class_String_boolean_ClassLoader = MethodTypeDescImpl.ofValidated(CD_Class, CD_String, CD_boolean, CD_ClassLoader),
MTD_ClassLoader = MethodTypeDescImpl.ofValidated(CD_ClassLoader),
MTD_Method_String_Class_array = MethodTypeDescImpl.ofValidated(CD_Method, CD_String, CD_Class_array),
MTD_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup),
MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDescImpl.ofValidated(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
MTD_Object_Object_Method_ObjectArray = MethodTypeDescImpl.ofValidated(CD_Object, CD_Object, CD_Method, CD_Object_array),
@@ -107,34 +107,33 @@ final class ProxyGenerator {
"jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
/* Preloaded ProxyMethod objects for methods in java.lang.Object */
private static final ProxyMethod HASH_CODE_METHOD;
private static final ProxyMethod EQUALS_METHOD;
private static final ProxyMethod TO_STRING_METHOD;
private static final Method OBJECT_HASH_CODE_METHOD;
private static final Method OBJECT_EQUALS_METHOD;
private static final Method OBJECT_TO_STRING_METHOD;
static {
try {
HASH_CODE_METHOD = new ProxyMethod(Object.class.getMethod("hashCode"));
EQUALS_METHOD = new ProxyMethod(Object.class.getMethod("equals", Object.class));
TO_STRING_METHOD = new ProxyMethod(Object.class.getMethod("toString"));
OBJECT_HASH_CODE_METHOD = Object.class.getMethod("hashCode");
OBJECT_EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
OBJECT_TO_STRING_METHOD = Object.class.getMethod("toString");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private final ConstantPoolBuilder cp;
private final List<StackMapFrameInfo.VerificationTypeInfo> throwableStack;
private final List<StackMapFrameInfo.VerificationTypeInfo> classLoaderLocal, throwableStack;
private final NameAndTypeEntry exInit;
private final ClassEntry object, proxy, ute;
private final ClassEntry objectCE, proxyCE, uteCE, classCE;
private final FieldRefEntry handlerField;
private final InterfaceMethodRefEntry invoke;
private final MethodRefEntry uteInit;
private final DirectMethodHandleDesc bsm;
private final InterfaceMethodRefEntry invocationHandlerInvoke;
private final MethodRefEntry uteInit, classGetMethod, classForName, throwableGetMessage;
/**
* Name of proxy class
* ClassEntry for this proxy class
*/
private ClassEntry classEntry;
private final ClassEntry thisClassCE;
/**
* Proxy interfaces
@@ -153,6 +152,12 @@ final class ProxyGenerator {
*/
private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>();
/**
* Ordinal of next ProxyMethod object added to proxyMethods.
* Indexes are reserved for hashcode(0), equals(1), toString(2).
*/
private int proxyMethodCount = 3;
/**
* Construct a ProxyGenerator to generate a proxy class with the
* specified name and for the given interfaces.
@@ -163,18 +168,23 @@ final class ProxyGenerator {
private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
int accessFlags) {
this.cp = ConstantPoolBuilder.of();
this.classEntry = cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(className));
this.thisClassCE = cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(className));
this.interfaces = interfaces;
this.accessFlags = accessFlags;
this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_Throwable)));
var throwable = cp.classEntry(CD_Throwable);
this.classLoaderLocal = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_ClassLoader)));
this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(throwable));
this.exInit = cp.nameAndTypeEntry(INIT_NAME, MTD_void_String);
this.object = cp.classEntry(CD_Object);
this.proxy = cp.classEntry(CD_Proxy);
this.handlerField = cp.fieldRefEntry(proxy, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler));
this.invoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray);
this.ute = cp.classEntry(CD_UndeclaredThrowableException);
this.uteInit = cp.methodRefEntry(ute, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable));
this.bsm = ConstantDescs.ofConstantBootstrap(classEntry.asSymbol(), "$getMethod", CD_Method, CD_Class, CD_String, CD_MethodType);
this.objectCE = cp.classEntry(CD_Object);
this.proxyCE = cp.classEntry(CD_Proxy);
this.classCE = cp.classEntry(CD_Class);
this.handlerField = cp.fieldRefEntry(proxyCE, cp.nameAndTypeEntry(NAME_HANDLER_FIELD, CD_InvocationHandler));
this.invocationHandlerInvoke = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray);
this.uteCE = cp.classEntry(CD_UndeclaredThrowableException);
this.uteInit = cp.methodRefEntry(uteCE, cp.nameAndTypeEntry(INIT_NAME, MTD_void_Throwable));
this.classGetMethod = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("getMethod", MTD_Method_String_Class_array));
this.classForName = cp.methodRefEntry(classCE, cp.nameAndTypeEntry("forName", MTD_Class_String_boolean_ClassLoader));
this.throwableGetMessage = cp.methodRefEntry(throwable, cp.nameAndTypeEntry("getMessage", MTD_String));
}
/**
@@ -441,9 +451,9 @@ final class ProxyGenerator {
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(HASH_CODE_METHOD);
addProxyMethod(EQUALS_METHOD);
addProxyMethod(TO_STRING_METHOD);
addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, "m0"));
addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, "m1"));
addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, "m2"));
/*
* Accumulate all of the methods from the proxy interfaces.
@@ -464,20 +474,23 @@ final class ProxyGenerator {
checkReturnTypes(sigmethods);
}
return CF_CONTEXT.build(classEntry, cp, clb -> {
clb.withSuperclass(proxy);
return CF_CONTEXT.build(thisClassCE, cp, clb -> {
clb.withSuperclass(proxyCE);
clb.withFlags(accessFlags);
clb.withInterfaces(toClassEntries(cp, interfaces));
generateConstructor(clb);
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
clb.withField(pm.methodFieldName, CD_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
// Generate code for proxy method
pm.generateMethod(this, clb);
pm.generateMethod(clb);
}
}
generateBootstrapMethod(clb);
generateStaticInitializer(clb);
generateLookupAccessor(clb);
});
}
@@ -520,7 +533,7 @@ final class ProxyGenerator {
}
}
sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
exceptionTypes, fromClass));
exceptionTypes, fromClass, "m" + proxyMethodCount++));
}
/**
@@ -542,32 +555,56 @@ final class ProxyGenerator {
clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
.aload(0)
.aload(1)
.invokespecial(cp.methodRefEntry(proxy, cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler)))
.invokespecial(cp.methodRefEntry(proxyCE,
cp.nameAndTypeEntry(INIT_NAME, MTD_void_InvocationHandler)))
.return_());
}
/**
* Generate CONDY bootstrap method for the proxy class to retrieve {@link Method} instances.
* Generate the class initializer.
* Discussion: Currently, for Proxy to work with SecurityManager,
* we rely on the parameter classes of the methods to be computed
* from Proxy instead of via user code paths like bootstrap method
* lazy evaluation. That might change if we can pass in the live
* Method objects directly..
*/
private void generateBootstrapMethod(ClassBuilder clb) {
clb.withMethodBody(bsm.methodName(), bsm.invocationType(), ClassFile.ACC_PRIVATE | ClassFile.ACC_STATIC, cob -> {
cob.aload(3) //interface Class
.aload(4) //interface method name String
.aload(5) //interface MethodType
.invokevirtual(CD_MethodType, "parameterArray", MTD_Class_array)
.invokevirtual(ConstantDescs.CD_Class, "getMethod", MTD_Method_String_Class_array)
.areturn();
Label failLabel = cob.newBoundLabel();
ClassEntry nsme = cp.classEntry(CD_NoSuchMethodError);
cob.exceptionCatch(cob.startLabel(), failLabel, failLabel, CD_NoSuchMethodException)
.new_(nsme)
private void generateStaticInitializer(ClassBuilder clb) {
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
// Put ClassLoader at local variable index 0, used by
// Class.forName(String, boolean, ClassLoader) calls
cob.ldc(thisClassCE)
.invokevirtual(cp.methodRefEntry(classCE,
cp.nameAndTypeEntry("getClassLoader", MTD_ClassLoader)))
.astore(0);
var ts = cob.newBoundLabel();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
pm.codeFieldInitialization(cob);
}
}
cob.return_();
var c1 = cob.newBoundLabel();
var nsmError = cp.classEntry(CD_NoSuchMethodError);
cob.exceptionCatch(ts, c1, c1, CD_NoSuchMethodException)
.new_(nsmError)
.dup_x1()
.swap()
.invokevirtual(cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String))
.invokespecial(cp.methodRefEntry(nsme, exInit))
.athrow()
.with(StackMapTableAttribute.of(List.of(
StackMapFrameInfo.of(failLabel, List.of(), throwableStack))));
.invokevirtual(throwableGetMessage)
.invokespecial(cp.methodRefEntry(nsmError, exInit))
.athrow();
var c2 = cob.newBoundLabel();
var ncdfError = cp.classEntry(CD_NoClassDefFoundError);
cob.exceptionCatch(ts, c1, c2, CD_ClassNotFoundException)
.new_(ncdfError)
.dup_x1()
.swap()
.invokevirtual(throwableGetMessage)
.invokespecial(cp.methodRefEntry(ncdfError, exInit))
.athrow();
cob.with(StackMapTableAttribute.of(List.of(
StackMapFrameInfo.of(c1, classLoaderLocal, throwableStack),
StackMapFrameInfo.of(c2, classLoaderLocal, throwableStack))));
});
}
@@ -587,7 +624,7 @@ final class ProxyGenerator {
ClassEntry iae = cp.classEntry(CD_IllegalAccessException);
cob.aload(cob.parameterSlot(0))
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("lookupClass", MTD_Class)))
.ldc(proxy)
.ldc(proxyCE)
.if_acmpne(failLabel)
.aload(cob.parameterSlot(0))
.invokevirtual(cp.methodRefEntry(mhl, cp.nameAndTypeEntry("hasFullPrivilegeAccess", MTD_boolean)))
@@ -613,24 +650,29 @@ final class ProxyGenerator {
* being generated: a method whose implementation will encode and
* dispatch invocations to the proxy instance's invocation handler.
*/
private static class ProxyMethod {
private class ProxyMethod {
private final Method method;
private final String shortSignature;
private final Class<?> fromClass;
private final Class<?>[] parameterTypes;
private final Class<?> returnType;
private final String methodFieldName;
private Class<?>[] exceptionTypes;
private final FieldRefEntry methodField;
private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
Class<?> returnType, Class<?>[] exceptionTypes,
Class<?> fromClass) {
Class<?> fromClass, String methodFieldName) {
this.method = method;
this.shortSignature = sig;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
this.exceptionTypes = exceptionTypes;
this.fromClass = fromClass;
this.methodFieldName = methodFieldName;
this.methodField = cp.fieldRefEntry(thisClassCE,
cp.nameAndTypeEntry(methodFieldName, CD_Method));
}
/**
@@ -639,17 +681,16 @@ final class ProxyGenerator {
* @param method The method for which to create a proxy
* @param methodFieldName the fieldName to generate
*/
private ProxyMethod(Method method) {
private ProxyMethod(Method method, String methodFieldName) {
this(method, method.toShortSignature(),
method.getSharedParameterTypes(), method.getReturnType(),
method.getSharedExceptionTypes(), method.getDeclaringClass());
method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
}
/**
* Generate this method, including the code and exception table entry.
*/
private void generateMethod(ProxyGenerator pg, ClassBuilder clb) {
var cp = pg.cp;
private void generateMethod(ClassBuilder clb) {
var pTypes = new ClassDesc[parameterTypes.length];
for (int i = 0; i < pTypes.length; i++) {
pTypes[i] = toClassDesc(parameterTypes[i]);
@@ -661,17 +702,14 @@ final class ProxyGenerator {
clb.withMethod(method.getName(), desc, accessFlags, mb ->
mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes))))
.withCode(cob -> {
cob.aload(0)
.getfield(pg.handlerField)
.aload(0)
.ldc(DynamicConstantDesc.of(pg.bsm,
toClassDesc(fromClass),
method.getName(),
desc));
cob.aload(cob.receiverSlot())
.getfield(handlerField)
.aload(cob.receiverSlot())
.getstatic(methodField);
if (parameterTypes.length > 0) {
// Create an array and fill with the parameters converting primitives to wrappers
cob.loadConstant(parameterTypes.length)
.anewarray(pg.object);
.anewarray(objectCE);
for (int i = 0; i < parameterTypes.length; i++) {
cob.dup()
.loadConstant(i);
@@ -682,7 +720,7 @@ final class ProxyGenerator {
cob.aconst_null();
}
cob.invokeinterface(pg.invoke);
cob.invokeinterface(invocationHandlerInvoke);
if (returnType == void.class) {
cob.pop()
@@ -698,14 +736,14 @@ final class ProxyGenerator {
cob.athrow(); // just rethrow the exception
var c2 = cob.newBoundLabel();
cob.exceptionCatchAll(cob.startLabel(), c1, c2)
.new_(pg.ute)
.new_(uteCE)
.dup_x1()
.swap()
.invokespecial(pg.uteInit)
.invokespecial(uteInit)
.athrow()
.with(StackMapTableAttribute.of(List.of(
StackMapFrameInfo.of(c1, List.of(), pg.throwableStack),
StackMapFrameInfo.of(c2, List.of(), pg.throwableStack))));
StackMapFrameInfo.of(c1, List.of(), throwableStack),
StackMapFrameInfo.of(c2, List.of(), throwableStack))));
}
}));
}
@@ -720,7 +758,7 @@ final class ProxyGenerator {
if (type.isPrimitive()) {
cob.loadLocal(TypeKind.from(type).asLoadable(), slot);
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
cob.invokestatic(prim.wrapperMethodRef(cob.constantPool()));
cob.invokestatic(prim.wrapperMethodRef(cp));
} else {
cob.aload(slot);
}
@@ -736,7 +774,7 @@ final class ProxyGenerator {
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
cob.checkcast(prim.wrapperClass)
.invokevirtual(prim.unwrapMethodRef(cob.constantPool()))
.invokevirtual(prim.unwrapMethodRef(cp))
.return_(TypeKind.from(type).asLoadable());
} else {
cob.checkcast(toClassDesc(type))
@@ -744,6 +782,57 @@ final class ProxyGenerator {
}
}
/**
* Generate code for initializing the static field that stores
* the Method object for this proxy method. A class loader is
* anticipated at local variable index 0.
* The generated code must be run in an AccessController.doPrivileged
* block if a SecurityManager is present, as otherwise the code
* cannot pass {@code null} ClassLoader to forName.
*/
private void codeFieldInitialization(CodeBuilder cob) {
var cp = cob.constantPool();
codeClassForName(cob, fromClass);
cob.ldc(method.getName())
.loadConstant(parameterTypes.length)
.anewarray(classCE);
// Construct an array with the parameter types mapping primitives to Wrapper types
for (int i = 0; i < parameterTypes.length; i++) {
cob.dup()
.loadConstant(i);
if (parameterTypes[i].isPrimitive()) {
PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]);
cob.getstatic(prim.typeFieldRef(cp));
} else {
codeClassForName(cob, parameterTypes[i]);
}
cob.aastore();
}
// lookup the method
cob.invokevirtual(classGetMethod)
.putstatic(methodField);
}
/*
* =============== Code Generation Utility Methods ===============
*/
/**
* Generate code to invoke the Class.forName with the name of the given
* class to get its Class object at runtime. The code is written to
* the supplied stream. Note that the code generated by this method
* may cause the checked ClassNotFoundException to be thrown. A class
* loader is anticipated at local variable index 0.
*/
private void codeClassForName(CodeBuilder cob, Class<?> cl) {
cob.ldc(cl.getName())
.iconst_0() // false
.aload(0)// classLoader
.invokestatic(classForName);
}
@Override
public String toString() {
return method.toShortString();
@@ -810,5 +899,9 @@ final class ProxyGenerator {
public MethodRefEntry unwrapMethodRef(ConstantPoolBuilder cp) {
return cp.methodRefEntry(wrapperClass, unwrapMethodName, unwrapMethodType);
}
public FieldRefEntry typeFieldRef(ConstantPoolBuilder cp) {
return cp.fieldRefEntry(wrapperClass, "TYPE", CD_Class);
}
}
}

View File

@@ -253,6 +253,15 @@ to determine the proxy that should be used for connecting to a given URI.</P>
</OL>
<P>The channel binding tokens generated are of the type "tls-server-end-point" as defined in
RFC 5929.</P>
<LI><P><B>{@systemProperty jdk.http.maxHeaderSize}</B> (default: 393216 or 384kB)<BR>
This is the maximum header field section size that a client is prepared to accept.
This is computed as the sum of the size of the uncompressed header name, plus
the size of the uncompressed header value, plus an overhead of 32 bytes for
each field section line. If a peer sends a field section that exceeds this
size a {@link java.net.ProtocolException ProtocolException} will be raised.
This applies to all versions of the HTTP protocol. A value of zero or a negative
value means no limit. If left unspecified, the default value is 393216 bytes.
</UL>
<P>All these properties are checked only once at startup.</P>
<a id="AddressCache"></a>

View File

@@ -41,6 +41,7 @@ package java.text;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1172,6 +1173,8 @@ public class MessageFormat extends Format {
maximumArgumentNumber = argumentNumbers[i];
}
}
// Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX
Object[] resultArray = new Object[maximumArgumentNumber + 1];
int patternOffset = 0;
@@ -1450,6 +1453,9 @@ public class MessageFormat extends Format {
* @serial
*/
private int[] argumentNumbers = new int[INITIAL_FORMATS];
// Implementation limit for ArgumentIndex pattern element. Valid indices must
// be less than this value
private static final int MAX_ARGUMENT_INDEX = 10000;
/**
* One less than the number of entries in {@code offsets}. Can also be thought of
@@ -1630,6 +1636,11 @@ public class MessageFormat extends Format {
+ argumentNumber);
}
if (argumentNumber >= MAX_ARGUMENT_INDEX) {
throw new IllegalArgumentException(
argumentNumber + " exceeds the ArgumentIndex implementation limit");
}
// resize format information arrays if necessary
if (offsetNumber >= formats.length) {
int newLength = formats.length * 2;
@@ -1997,24 +2008,53 @@ public class MessageFormat extends Format {
*/
@java.io.Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
boolean isValid = maxOffset >= -1
&& formats.length > maxOffset
&& offsets.length > maxOffset
&& argumentNumbers.length > maxOffset;
ObjectInputStream.GetField fields = in.readFields();
if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets")
|| fields.defaulted("formats") || fields.defaulted("locale")
|| fields.defaulted("pattern") || fields.defaulted("maxOffset")){
throw new InvalidObjectException("Stream has missing data");
}
locale = (Locale) fields.get("locale", null);
String patt = (String) fields.get("pattern", null);
int maxOff = fields.get("maxOffset", -2);
int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone();
int[] offs = ((int[]) fields.get("offsets", null)).clone();
Format[] fmts = ((Format[]) fields.get("formats", null)).clone();
// Check arrays/maxOffset have correct value/length
boolean isValid = maxOff >= -1 && argNums.length > maxOff
&& offs.length > maxOff && fmts.length > maxOff;
// Check the correctness of arguments and offsets
if (isValid) {
int lastOffset = pattern.length() + 1;
for (int i = maxOffset; i >= 0; --i) {
if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
int lastOffset = patt.length() + 1;
for (int i = maxOff; i >= 0; --i) {
if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX
|| offs[i] < 0 || offs[i] > lastOffset) {
isValid = false;
break;
} else {
lastOffset = offsets[i];
lastOffset = offs[i];
}
}
}
if (!isValid) {
throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
throw new InvalidObjectException("Stream has invalid data");
}
maxOffset = maxOff;
pattern = patt;
offsets = offs;
formats = fmts;
argumentNumbers = argNums;
}
/**
* Serialization without data not supported for this class.
*/
@java.io.Serial
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("Deserialized MessageFormat objects need data");
}
}

View File

@@ -1047,7 +1047,7 @@ public final class StackMapGenerator {
void setLocalsFromArg(String name, MethodTypeDesc methodDesc, boolean isStatic, Type thisKlass) {
int localsSize = 0;
// Pre-emptively create a locals array that encompass all parameter slots
checkLocal(methodDesc.parameterCount() + (isStatic ? 0 : -1));
checkLocal(methodDesc.parameterCount() + (isStatic ? -1 : 0));
if (!isStatic) {
localsSize++;
if (OBJECT_INITIALIZER_NAME.equals(name) && !CD_Object.equals(thisKlass.sym)) {

View File

@@ -165,6 +165,7 @@ module java.base {
java.desktop,
java.logging,
java.management,
java.management.rmi,
java.naming,
java.rmi,
jdk.charsets,

View File

@@ -30,6 +30,8 @@
package sun.net.www;
import java.io.*;
import java.lang.reflect.Array;
import java.net.ProtocolException;
import java.util.Collections;
import java.util.*;
@@ -45,11 +47,32 @@ public final class MessageHeader {
private String[] values;
private int nkeys;
// max number of bytes for headers, <=0 means unlimited;
// this corresponds to the length of the names, plus the length
// of the values, plus an overhead of 32 bytes per name: value
// pair.
// Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE
// see RFC 9113, section 6.5.2.
// https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE
private final int maxHeaderSize;
// Aggregate size of the field lines (name + value + 32) x N
// that have been parsed and accepted so far.
// This is defined as a long to force promotion to long
// and avoid overflows; see checkNewSize;
private long size;
public MessageHeader () {
this(0);
}
public MessageHeader (int maxHeaderSize) {
this.maxHeaderSize = maxHeaderSize;
grow();
}
public MessageHeader (InputStream is) throws java.io.IOException {
maxHeaderSize = 0;
parseHeader(is);
}
@@ -476,10 +499,28 @@ public final class MessageHeader {
public void parseHeader(InputStream is) throws java.io.IOException {
synchronized (this) {
nkeys = 0;
size = 0;
}
mergeHeader(is);
}
private void checkMaxHeaderSize(int sz) throws ProtocolException {
if (maxHeaderSize > 0) checkNewSize(size, sz, 0);
}
private long checkNewSize(long size, int name, int value) throws ProtocolException {
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
long newSize = size + name + value + 32;
if (maxHeaderSize > 0 && newSize > maxHeaderSize) {
Arrays.fill(keys, 0, nkeys, null);
Arrays.fill(values,0, nkeys, null);
nkeys = 0;
throw new ProtocolException(String.format("Header size too big: %s > %s",
newSize, maxHeaderSize));
}
return newSize;
}
/** Parse and merge a MIME header from an input stream. */
@SuppressWarnings("fallthrough")
public void mergeHeader(InputStream is) throws java.io.IOException {
@@ -493,7 +534,15 @@ public final class MessageHeader {
int c;
boolean inKey = firstc > ' ';
s[len++] = (char) firstc;
checkMaxHeaderSize(len);
parseloop:{
// We start parsing for a new name value pair here.
// The max header size includes an overhead of 32 bytes per
// name value pair.
// See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2.
long maxRemaining = maxHeaderSize > 0
? maxHeaderSize - size - 32
: Long.MAX_VALUE;
while ((c = is.read()) >= 0) {
switch (c) {
case ':':
@@ -527,6 +576,9 @@ public final class MessageHeader {
s = ns;
}
s[len++] = (char) c;
if (maxHeaderSize > 0 && len > maxRemaining) {
checkMaxHeaderSize(len);
}
}
firstc = -1;
}
@@ -548,6 +600,9 @@ public final class MessageHeader {
v = new String();
else
v = String.copyValueOf(s, keyend, len - keyend);
int klen = k == null ? 0 : k.length();
size = checkNewSize(size, klen, v.length());
add(k, v);
}
}

View File

@@ -172,6 +172,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
*/
private static final int bufSize4ES;
private static final int maxHeaderSize;
/*
* Restrict setting of request headers through the public api
* consistent with JavaScript XMLHttpRequest2 with a few
@@ -288,6 +290,19 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
} else {
restrictedHeaderSet = null;
}
int defMaxHeaderSize = 384 * 1024;
String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize");
int maxHeaderSizeVal = defMaxHeaderSize;
if (maxHeaderSizeStr != null) {
try {
maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr);
} catch (NumberFormatException n) {
maxHeaderSizeVal = defMaxHeaderSize;
}
}
if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0;
maxHeaderSize = maxHeaderSizeVal;
}
static final String httpVersion = "HTTP/1.1";
@@ -754,7 +769,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
}
ps = (PrintStream) http.getOutputStream();
connected=true;
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
setRequests=false;
writeRequests();
}
@@ -912,7 +927,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
throws IOException {
super(checkURL(u));
requests = new MessageHeader();
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
userHeaders = new MessageHeader();
this.handler = handler;
instProxy = p;
@@ -2810,7 +2825,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
}
// clear out old response headers!!!!
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
if (stat == HTTP_USE_PROXY) {
/* This means we must re-request the resource through the
* proxy denoted in the "Location:" field of the response.
@@ -3000,7 +3015,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
} catch (IOException e) { }
}
responseCode = -1;
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
connected = false;
}

View File

@@ -806,14 +806,19 @@ class DatagramChannelImpl
}
}
/**
* Receives a datagram into a direct buffer.
*/
private int receiveIntoNativeBuffer(ByteBuffer bb, int rem, int pos,
boolean connected)
throws IOException
{
NIO_ACCESS.acquireSession(bb);
try {
long bufAddress = NIO_ACCESS.getBufferAddress(bb);
int n = receive0(fd,
((DirectBuffer)bb).address() + pos, rem,
bufAddress + pos,
rem,
sourceSockAddr.address(),
connected);
if (n > 0)
@@ -991,6 +996,9 @@ class DatagramChannelImpl
}
}
/**
* Send a datagram contained in a direct buffer.
*/
private int sendFromNativeBuffer(FileDescriptor fd, ByteBuffer bb,
InetSocketAddress target)
throws IOException
@@ -1003,9 +1011,13 @@ class DatagramChannelImpl
int written;
NIO_ACCESS.acquireSession(bb);
try {
long bufAddress = NIO_ACCESS.getBufferAddress(bb);
int addressLen = targetSocketAddress(target);
written = send0(fd, ((DirectBuffer)bb).address() + pos, rem,
targetSockAddr.address(), addressLen);
written = send0(fd,
bufAddress + pos,
rem,
targetSockAddr.address(),
addressLen);
} catch (PortUnreachableException pue) {
if (isConnected())
throw pue;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -213,8 +213,6 @@ final class ClientHello {
// ignore cookie
hos.putBytes16(getEncodedCipherSuites());
hos.putBytes8(compressionMethod);
extensions.send(hos); // In TLS 1.3, use of certain
// extensions is mandatory.
} catch (IOException ioe) {
// unlikely
}
@@ -903,8 +901,8 @@ final class ClientHello {
throw context.conContext.fatal(Alert.PROTOCOL_VERSION,
"The client supported protocol versions " + Arrays.toString(
ProtocolVersion.toStringArray(clientSupportedVersions)) +
" are not accepted by server preferences " +
context.activeProtocols);
" are not accepted by server preferences " + Arrays.toString(
ProtocolVersion.toStringArray(context.activeProtocols)));
}
}
@@ -1426,6 +1424,9 @@ final class ClientHello {
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
SSLHandshake.SERVER_HELLO);
// Reset the ClientHello non-zero offset fragment allowance
shc.acceptCliHelloFragments = false;
//
// produce
//

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,12 +40,23 @@ import sun.security.ssl.SSLCipher.SSLReadCipher;
final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private DTLSReassembler reassembler = null;
private int readEpoch;
private SSLContextImpl sslContext;
DTLSInputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
this.readEpoch = 0;
}
// Method to set TransportContext
public void setTransportContext(TransportContext tc) {
this.tc = tc;
}
// Method to set SSLContext
public void setSSLContext(SSLContextImpl sslContext) {
this.sslContext = sslContext;
}
@Override
void changeReadCiphers(SSLReadCipher readCipher) {
this.readCipher = readCipher;
@@ -537,6 +548,27 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
/**
* Turn a sufficiently-large initial ClientHello fragment into one that
* stops immediately after the compression methods. This is only used
* for the initial CH message fragment at offset 0.
*
* @param srcFrag the fragment actually received by the DTLSReassembler
* @param limit the size of the new, cloned/truncated handshake fragment
*
* @return a truncated handshake fragment that is sized to look like a
* complete message, but actually contains only up to the compression
* methods (no extensions)
*/
private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag,
int limit) {
return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit),
srcFrag.contentType, srcFrag.majorVersion,
srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch,
srcFrag.recordSeq, srcFrag.handshakeType, limit,
srcFrag.messageSeq, srcFrag.fragmentOffset, limit);
}
private static final class HoleDescriptor {
int offset; // fragment_offset
int limit; // fragment_offset + fragment_length
@@ -640,10 +672,17 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Queue up a handshake message.
void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
if (!isDesirable(hsf)) {
// Not a dedired record, discard it.
// Not a desired record, discard it.
return;
}
if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) {
// validate the first or subsequent ClientHello message
if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) {
return;
}
}
// Clean up the retransmission messages if necessary.
cleanUpRetransmit(hsf);
@@ -769,6 +808,100 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
private HandshakeFragment valHello(HandshakeFragment hsf,
boolean firstHello) {
ServerHandshakeContext shc =
(ServerHandshakeContext) tc.handshakeContext;
// Drop any fragment that is not a zero offset until we've received
// a second (or possibly later) CH message that passes the cookie
// check.
if (shc == null || !shc.acceptCliHelloFragments) {
if (hsf.fragmentOffset != 0) {
return null;
}
} else {
// Let this fragment through to the DTLSReassembler as-is
return hsf;
}
try {
ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment);
ProtocolVersion pv = ProtocolVersion.valueOf(
Record.getInt16(fragmentData));
if (!pv.isDTLS) {
return null;
}
// Read the random (32 bytes)
if (fragmentData.remaining() < 32) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected client hello fragment (bad random len) " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
fragmentData.position(fragmentData.position() + 32);
// SessionID
byte[] sessId = Record.getBytes8(fragmentData);
if (sessId.length > 0 &&
!SSLConfiguration.enableDtlsResumeCookie) {
// If we are in a resumption it is possible that the cookie
// exchange will be skipped. This is a server-side setting
// and it is NOT the default. If enableDtlsResumeCookie is
// false though, then we will buffer fragments since there
// is no cookie exchange to execute prior to performing
// reassembly.
return hsf;
}
// Cookie
byte[] cookie = Record.getBytes8(fragmentData);
if (firstHello && cookie.length != 0) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
// CipherSuites
Record.getBytes16(fragmentData);
// Compression methods
Record.getBytes8(fragmentData);
// If it's the first fragment, we'll truncate it and push it
// through the reassembler.
if (firstHello) {
return truncateChFragment(hsf, fragmentData.position());
} else {
HelloCookieManager hcMgr = sslContext.
getHelloCookieManager(ProtocolVersion.DTLS10);
ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0,
fragmentData.position());
ClientHello.ClientHelloMessage chMsg =
new ClientHello.ClientHelloMessage(shc, msgFragBuf, null);
if (!hcMgr.isCookieValid(shc, chMsg, cookie)) {
// Bad cookie check, truncate it and let the ClientHello
// consumer recheck, fail and take the appropriate action.
return truncateChFragment(hsf, fragmentData.position());
} else {
// It's a good cookie, return the original handshake
// fragment and let it go into the DTLSReassembler like
// any other fragment so we can wait for the rest of
// the CH message.
shc.acceptCliHelloFragments = true;
return hsf;
}
}
} catch (IOException ioe) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected client hello fragment " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
}
// Queue up a ChangeCipherSpec message
void queueUpChangeCipherSpec(RecordFragment rf)
throws SSLProtocolException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,7 @@ class ServerHandshakeContext extends HandshakeContext {
CertificateMessage.CertificateEntry currentCertEntry;
private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
final long statusRespTimeout;
boolean acceptCliHelloFragments = false;
ServerHandshakeContext(SSLContextImpl sslContext,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -156,6 +156,11 @@ final class TransportContext implements ConnectionContext {
this.acc = AccessController.getContext();
this.consumers = new HashMap<>();
if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) {
dtlsInputRecord.setTransportContext(this);
dtlsInputRecord.setSSLContext(this.sslContext);
}
}
// Dispatch plaintext to a specific consumer.

View File

@@ -90,12 +90,11 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
* store the result in an IntegerPolynomial representation in a. Requires
* that a.length == numLimbs.
*/
protected int multByInt(long[] a, long b) {
protected void multByInt(long[] a, long b) {
for (int i = 0; i < a.length; i++) {
a[i] *= b;
}
reduce(a);
return 0;
}
/**
@@ -104,7 +103,7 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
* a.length == b.length == r.length == numLimbs. It is allowed for a and r
* to be the same array.
*/
protected abstract int mult(long[] a, long[] b, long[] r);
protected abstract void mult(long[] a, long[] b, long[] r);
/**
* Multiply an IntegerPolynomial representation (a) with itself and store
@@ -112,7 +111,7 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
* a.length == r.length == numLimbs. It is allowed for a and r
* to be the same array.
*/
protected abstract int square(long[] a, long[] r);
protected abstract void square(long[] a, long[] r);
IntegerPolynomial(int bitsPerLimb,
int numLimbs,
@@ -622,8 +621,8 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
}
long[] newLimbs = new long[limbs.length];
int numAdds = mult(limbs, b.limbs, newLimbs);
return new ImmutableElement(newLimbs, numAdds);
mult(limbs, b.limbs, newLimbs);
return new ImmutableElement(newLimbs, 0);
}
@Override
@@ -635,8 +634,8 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
}
long[] newLimbs = new long[limbs.length];
int numAdds = IntegerPolynomial.this.square(limbs, newLimbs);
return new ImmutableElement(newLimbs, numAdds);
IntegerPolynomial.this.square(limbs, newLimbs);
return new ImmutableElement(newLimbs, 0);
}
public void addModPowerTwo(IntegerModuloP arg, byte[] result) {
@@ -751,7 +750,8 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
b.numAdds = 0;
}
numAdds = mult(limbs, b.limbs, limbs);
mult(limbs, b.limbs, limbs);
numAdds = 0;
return this;
}
@@ -764,7 +764,8 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
}
int value = ((Limb)v).value;
numAdds += multByInt(limbs, value);
multByInt(limbs, value);
numAdds = 0;
return this;
}
@@ -824,7 +825,8 @@ public abstract sealed class IntegerPolynomial implements IntegerFieldModuloP
numAdds = 0;
}
numAdds = IntegerPolynomial.this.square(limbs, limbs);
IntegerPolynomial.this.square(limbs, limbs);
numAdds = 0;
return this;
}

View File

@@ -50,7 +50,7 @@ public final class IntegerPolynomial1305 extends IntegerPolynomial {
super(BITS_PER_LIMB, NUM_LIMBS, 1, MODULUS);
}
protected int mult(long[] a, long[] b, long[] r) {
protected void mult(long[] a, long[] b, long[] r) {
// Use grade-school multiplication into primitives to avoid the
// temporary array allocation. This is equivalent to the following
@@ -73,7 +73,6 @@ public final class IntegerPolynomial1305 extends IntegerPolynomial {
long c8 = (a[4] * b[4]);
carryReduce(r, c0, c1, c2, c3, c4, c5, c6, c7, c8);
return 0;
}
private void carryReduce(long[] r, long c0, long c1, long c2, long c3,
@@ -100,7 +99,7 @@ public final class IntegerPolynomial1305 extends IntegerPolynomial {
}
@Override
protected int square(long[] a, long[] r) {
protected void square(long[] a, long[] r) {
// Use grade-school multiplication with a simple squaring optimization.
// Multiply into primitives to avoid the temporary array allocation.
// This is equivalent to the following code:
@@ -123,7 +122,6 @@ public final class IntegerPolynomial1305 extends IntegerPolynomial {
long c8 = (a[4] * a[4]);
carryReduce(r, c0, c1, c2, c3, c4, c5, c6, c7, c8);
return 0;
}
@Override

View File

@@ -131,12 +131,11 @@ public sealed class IntegerPolynomialModBinP extends IntegerPolynomial {
}
@Override
protected int mult(long[] a, long[] b, long[] r) {
protected void mult(long[] a, long[] b, long[] r) {
long[] c = new long[2 * numLimbs];
multOnly(a, b, c);
carryReduce(c, r);
return 0;
}
private void modReduceInBits(long[] limbs, int index, int bits, long x) {
@@ -189,7 +188,7 @@ public sealed class IntegerPolynomialModBinP extends IntegerPolynomial {
}
@Override
protected int square(long[] a, long[] r) {
protected void square(long[] a, long[] r) {
long[] c = new long[2 * numLimbs];
for (int i = 0; i < numLimbs; i++) {
@@ -200,7 +199,6 @@ public sealed class IntegerPolynomialModBinP extends IntegerPolynomial {
}
carryReduce(c, r);
return 0;
}
/**

View File

@@ -31,6 +31,7 @@ import sun.security.util.math.SmallValue;
import sun.security.util.math.IntegerFieldModuloP;
import java.math.BigInteger;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.ForceInline;
// Reference:
// - [1] Shay Gueron and Vlad Krasnov "Fast Prime Field Elliptic Curve
@@ -103,8 +104,8 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
setLimbsValuePositive(v, vLimbs);
// Convert to Montgomery domain
int numAdds = mult(vLimbs, h, montLimbs);
return new ImmutableElement(montLimbs, numAdds);
mult(vLimbs, h, montLimbs);
return new ImmutableElement(montLimbs, 0);
}
@Override
@@ -114,24 +115,6 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
return super.getSmallValue(value);
}
/*
* This function is used by IntegerPolynomial.setProduct(SmallValue v) to
* multiply by a small constant (i.e. (int) 1,2,3,4). Instead of doing a
* montgomery conversion followed by a montgomery multiplication, just use
* the spare top (64-BITS_PER_LIMB) bits to multiply by a constant. (See [1]
* Section 4 )
*
* Will return an unreduced value
*/
@Override
protected int multByInt(long[] a, long b) {
assert (b < (1 << BITS_PER_LIMB));
for (int i = 0; i < a.length; i++) {
a[i] *= b;
}
return (int) (b - 1);
}
@Override
public ImmutableIntegerModuloP fromMontgomery(ImmutableIntegerModuloP n) {
assert n.getField() == MontgomeryIntegerPolynomialP256.ONE;
@@ -163,10 +146,11 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
}
@Override
protected int square(long[] a, long[] r) {
return mult(a, a, r);
protected void square(long[] a, long[] r) {
mult(a, a, r);
}
/**
* Unrolled Word-by-Word Montgomery Multiplication r = a * b * 2^-260 (mod P)
*
@@ -174,8 +158,15 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
* for a Montgomery Friendly modulus p". Note: Step 6. Skipped; Instead use
* numAdds to reuse existing overflow logic.
*/
@Override
protected void mult(long[] a, long[] b, long[] r) {
multImpl(a, b, r);
reducePositive(r);
}
@ForceInline
@IntrinsicCandidate
protected int mult(long[] a, long[] b, long[] r) {
private void multImpl(long[] a, long[] b, long[] r) {
long aa0 = a[0];
long aa1 = a[1];
long aa2 = a[2];
@@ -408,36 +399,16 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
d4 += n4 & LIMB_MASK;
c5 += d1 + dd0 + (d0 >>> BITS_PER_LIMB);
c6 += d2 + dd1 + (c5 >>> BITS_PER_LIMB);
c7 += d3 + dd2 + (c6 >>> BITS_PER_LIMB);
c8 += d4 + dd3 + (c7 >>> BITS_PER_LIMB);
c9 = dd4 + (c8 >>> BITS_PER_LIMB);
c6 += d2 + dd1;
c7 += d3 + dd2;
c8 += d4 + dd3;
c9 = dd4;
c5 &= LIMB_MASK;
c6 &= LIMB_MASK;
c7 &= LIMB_MASK;
c8 &= LIMB_MASK;
// At this point, the result could overflow by one modulus.
c0 = c5 - modulus[0];
c1 = c6 - modulus[1] + (c0 >> BITS_PER_LIMB);
c0 &= LIMB_MASK;
c2 = c7 - modulus[2] + (c1 >> BITS_PER_LIMB);
c1 &= LIMB_MASK;
c3 = c8 - modulus[3] + (c2 >> BITS_PER_LIMB);
c2 &= LIMB_MASK;
c4 = c9 - modulus[4] + (c3 >> BITS_PER_LIMB);
c3 &= LIMB_MASK;
long mask = c4 >> BITS_PER_LIMB; // Signed shift!
r[0] = ((c5 & mask) | (c0 & ~mask));
r[1] = ((c6 & mask) | (c1 & ~mask));
r[2] = ((c7 & mask) | (c2 & ~mask));
r[3] = ((c8 & mask) | (c3 & ~mask));
r[4] = ((c9 & mask) | (c4 & ~mask));
return 0;
r[0] = c5;
r[1] = c6;
r[2] = c7;
r[3] = c8;
r[4] = c9;
}
@Override
@@ -516,8 +487,8 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
super.encode(v, offset, length, highByte, vLimbs);
// Convert to Montgomery domain
int numAdds = mult(vLimbs, h, montLimbs);
return new ImmutableElement(montLimbs, numAdds);
mult(vLimbs, h, montLimbs);
return new ImmutableElement(montLimbs, 0);
}
/*
@@ -556,4 +527,27 @@ public final class MontgomeryIntegerPolynomialP256 extends IntegerPolynomial
limbs[i - 5] += (v << 4) & LIMB_MASK;
limbs[i - 4] += v >> 48;
}
// Used when limbs a could overflow by one modulus.
@ForceInline
protected void reducePositive(long[] a) {
long aa0 = a[0];
long aa1 = a[1] + (aa0>>BITS_PER_LIMB);
long aa2 = a[2] + (aa1>>BITS_PER_LIMB);
long aa3 = a[3] + (aa2>>BITS_PER_LIMB);
long aa4 = a[4] + (aa3>>BITS_PER_LIMB);
long c0 = a[0] - modulus[0];
long c1 = a[1] - modulus[1] + (c0 >> BITS_PER_LIMB);
long c2 = a[2] - modulus[2] + (c1 >> BITS_PER_LIMB);
long c3 = a[3] - modulus[3] + (c2 >> BITS_PER_LIMB);
long c4 = a[4] - modulus[4] + (c3 >> BITS_PER_LIMB);
long mask = c4 >> BITS_PER_LIMB; // Signed shift!
a[0] = ((aa0 & mask) | (c0 & ~mask)) & LIMB_MASK;
a[1] = ((aa1 & mask) | (c1 & ~mask)) & LIMB_MASK;
a[2] = ((aa2 & mask) | (c2 & ~mask)) & LIMB_MASK;
a[3] = ((aa3 & mask) | (c3 & ~mask)) & LIMB_MASK;
a[4] = ((aa4 & mask) | (c4 & ~mask));
}
}

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -287,6 +287,7 @@ ZAR=ZAR
ZMK=ZMK
ZMW=ZMW
ZWD=ZWD
ZWG=ZWG
ZWL=ZWL
ZWN=ZWN
ZWR=ZWR
@@ -512,5 +513,6 @@ yum=Yugoslavian New Dinar (1994-2002)
zar=South African Rand
zmk=Zambian Kwacha
zwd=Zimbabwean Dollar (1980-2008)
zwg=Zimbabwe Gold
zwl=Zimbabwean Dollar (2009)
zwr=Zimbabwean Dollar (2008)

View File

@@ -130,3 +130,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic
#jdk.http.ntlm.transparentAuth=trustedHosts
#
jdk.http.ntlm.transparentAuth=disabled
#
# Maximum HTTP field section size that a client is prepared to accept
#
# jdk.http.maxHeaderSize=393216
#
# This is the maximum header field section size that a client is prepared to accept.
# This is computed as the sum of the size of the uncompressed header name, plus
# the size of the uncompressed header value, plus an overhead of 32 bytes for
# each field section line. If a peer sends a field section that exceeds this
# size a {@link java.net.ProtocolException ProtocolException} will be raised.
# This applies to all versions of the HTTP protocol. A value of zero or a negative
# value means no limit. If left unspecified, the default value is 393216 bytes
# or 384kB.
#
# Note: This property is currently used by the JDK Reference implementation. It
# is not guaranteed to be examined and used by other implementations.

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
# 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 @@ formatVersion=3
# Version of the currency code information in this class.
# It is a serial number that accompanies with each amendment.
dataVersion=176
dataVersion=177
# List of all valid ISO 4217 currency codes.
# To ensure compatibility, do not remove codes.
@@ -56,8 +56,8 @@ all=ADP020-AED784-AFA004-AFN971-ALL008-AMD051-ANG532-AOA973-ARS032-ATS040-AUD036
TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\
UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\
XBB956-XBC957-XBD958-XCD951-XCG532-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\
XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\
ZWN942-ZWR935
XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWG924-\
ZWL932-ZWN942-ZWR935
# Mappings from ISO 3166 country codes to ISO 4217 currency codes.
@@ -582,7 +582,7 @@ YE=YER
# ZAMBIA
ZM=ZMW
# ZIMBABWE
ZW=ZWL
ZW=ZWG
# List of currencies with non-2digit decimals for minor units,

View File

@@ -1710,10 +1710,12 @@ This prevents the JVM from exiting and keeps the process active so that
you can attach a debugger to it to investigate the cause of the error.
By default, this option is disabled.
.TP
\f[V]-XX:StartFlightRecording=\f[R]\f[I]parameter\f[R]\f[V]=\f[R]\f[I]value\f[R]
\f[V]-XX:StartFlightRecording:\f[R]\f[I]parameter\f[R]\f[V]=\f[R]\f[I]value\f[R]
Starts a JFR recording for the Java application.
This option is equivalent to the \f[V]JFR.start\f[R] diagnostic command
that starts a recording during runtime.
\f[V]-XX:StartFlightRecording:help\f[R] prints available options and
example command lines.
You can set the following \f[I]parameter\f[R]\f[V]=\f[R]\f[I]value\f[R]
entries when starting a JFR recording:
.RS
@@ -1760,6 +1762,8 @@ written when the recording is stopped, for example:
.PP
If %p and/or %t is specified in the filename, it expands to the
JVM\[aq]s PID and the current timestamp, respectively.
The filename may also be a directory in which case, the filename is
generated from the PID and the current date in the specified directory.
.RE
.TP
\f[V]name=\f[R]\f[I]identifier\f[R]
@@ -1840,6 +1844,9 @@ The whitespace delimiter can be omitted for timespan values, i.e.
20ms.
For more information about the settings syntax, see Javadoc of the
jdk.jfr package.
.PP
To only see warnings and errors from JFR during startup set
-Xlog:jfr+startup=warning.
.RE
.TP
\f[V]-XX:ThreadStackSize=\f[R]\f[I]size\f[R]

View File

@@ -150,7 +150,10 @@ public class BasicSliderUI extends SliderUI{
/**
* Constructs a {@code BasicSliderUI}.
* @deprecated This constructor was exposed erroneously and will be removed in a future release.
* Use {@link #BasicSliderUI(JSlider)} instead.
*/
@Deprecated(since = "23", forRemoval = true)
public BasicSliderUI() {}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,7 +187,9 @@ public class Type1Font extends FileFont {
private synchronized ByteBuffer getBuffer() throws FontFormatException {
ByteBuffer bbuf = bufferRef.get();
if (bbuf == null) {
//System.out.println("open T1 " + platName);
if (FontUtilities.isLogging()) {
FontUtilities.logInfo("open Type 1 font: " + platName);
}
try {
@SuppressWarnings("removal")
RandomAccessFile raf = (RandomAccessFile)
@@ -229,6 +231,9 @@ public class Type1Font extends FileFont {
void readFile(ByteBuffer buffer) {
RandomAccessFile raf = null;
FileChannel fc;
if (FontUtilities.isLogging()) {
FontUtilities.logInfo("open Type 1 font: " + platName);
}
try {
raf = (RandomAccessFile)
java.security.AccessController.doPrivileged(

View File

@@ -1317,13 +1317,6 @@ public class FilePane extends JPanel implements PropertyChangeListener {
detailsTable.addFocusListener(repaintListener);
}
// TAB/SHIFT-TAB should transfer focus and ENTER should select an item.
// We don't want them to navigate within the table
ActionMap am = SwingUtilities.getUIActionMap(detailsTable);
am.remove("selectNextRowCell");
am.remove("selectPreviousRowCell");
am.remove("selectNextColumnCell");
am.remove("selectPreviousColumnCell");
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
null);
detailsTable.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,

View File

@@ -522,6 +522,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
AwtComponent *awtParent = (parent != NULL) ? (AwtComponent *)JNI_GET_PDATA(parent) : NULL;
HWND hwndOwner = awtParent ? awtParent->GetHWnd() : NULL;
jboolean doIt = JNI_FALSE;
PAGESETUPDLG setup;
memset(&setup, 0, sizeof(setup));
@@ -577,7 +578,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
*/
if ((setup.hDevMode == NULL) && (setup.hDevNames == NULL)) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
} else {
int measure = PSD_INTHOUSANDTHSOFINCHES;
@@ -605,7 +606,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
pageFormatToSetup(env, self, page, &setup, AwtPrintControl::getPrintDC(env, self));
if (env->ExceptionCheck()) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
setup.lpfnPageSetupHook = reinterpret_cast<LPPAGESETUPHOOK>(pageDlgHook);
@@ -619,7 +620,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
jobject paper = getPaper(env, page);
if (paper == NULL) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
int units = setup.Flags & PSD_INTHOUSANDTHSOFINCHES ?
MM_HIENGLISH :
@@ -661,7 +662,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
setPaperValues(env, paper, &paperSize, &margins, units);
if (env->ExceptionCheck()) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
/*
* Put the updated Paper instance and the orientation into
@@ -670,7 +671,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
setPaper(env, page, paper);
if (env->ExceptionCheck()) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
setPageFormatOrientation(env, page, orientation);
if (env->ExceptionCheck()) {
@@ -684,12 +685,13 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
jboolean err = setPrintPaperSize(env, self, devmode->dmPaperSize);
if (err) {
CLEANUP_SHOW;
return JNI_FALSE;
return doIt;
}
}
}
::GlobalUnlock(setup.hDevMode);
}
doIt = JNI_TRUE;
}
AwtDialog::CheckUninstallModalHook();
@@ -708,7 +710,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
CLEANUP_SHOW;
return JNI_TRUE;
return doIt;
CATCH_BAD_ALLOC_RET(0);
}

View File

@@ -46,6 +46,7 @@ import javax.management.*;
import javax.management.remote.JMXServerErrorException;
import javax.management.remote.NotificationResult;
import javax.security.auth.Subject;
import jdk.internal.access.SharedSecrets;
import sun.reflect.misc.ReflectUtil;
import static javax.management.remote.rmi.RMIConnector.Util.cast;
@@ -108,14 +109,19 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
this.rmiServer = rmiServer;
this.connectionId = connectionId;
this.defaultClassLoader = defaultClassLoader;
this.subject = subject;
if (subject == null) {
this.acc = null;
} else {
// An authenticated Subject was provided.
// Subject Delegation has been removed.
this.acc = JMXSubjectDomainCombiner.getContext(subject);
if (SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
// SM is allowed. Will use ACC created with Subject:
this.acc = JMXSubjectDomainCombiner.getContext(subject);
} else {
this.acc = null;
}
}
this.mbeanServer = rmiServer.getMBeanServer();
@@ -1292,10 +1298,21 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
return getServerNotifFwd().fetchNotifs(csn, t, mn);
}
};
if (acc == null)
return action.run();
else
return AccessController.doPrivileged(action, acc);
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
// Modern case
if (subject == null) {
return action.run();
} else {
return Subject.doAs(subject, action);
}
} else {
// SM permitted
if (acc == null) {
return action.run(); // No Subject or ACC
} else {
return AccessController.doPrivileged(action, acc);
}
}
} finally {
serverCommunicatorAdmin.rspOutgoing();
}
@@ -1411,16 +1428,36 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
serverCommunicatorAdmin.reqIncoming();
try {
PrivilegedOperation op = new PrivilegedOperation(operation, params);
if (acc == null) {
try {
return op.run();
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
throw new PrivilegedActionException(e);
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
// Modern case
if (subject == null) {
try {
return op.run();
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new PrivilegedActionException(e);
}
}
} else {
return Subject.doAs(subject, op);
}
} else {
return AccessController.doPrivileged(op, acc);
// SM permitted
if (acc == null) {
try {
return op.run();
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new PrivilegedActionException(e);
}
}
} else {
return AccessController.doPrivileged(op, acc);
}
}
} catch (Error e) {
throw new JMXServerErrorException(e.toString(),e);
@@ -1585,15 +1622,25 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
}
try {
final ClassLoader old = AccessController.doPrivileged(new SetCcl(cl));
try{
if (acc != null) {
return AccessController.doPrivileged(
(PrivilegedExceptionAction<T>) () ->
wrappedClass.cast(mo.get()), acc);
}else{
return wrappedClass.cast(mo.get());
try {
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
// Modern case
if (subject != null) {
return Subject.doAs(subject, (PrivilegedExceptionAction<T>) () -> wrappedClass.cast(mo.get()));
} else {
return wrappedClass.cast(mo.get());
}
} else {
// SM permitted
if (acc != null) {
return AccessController.doPrivileged(
(PrivilegedExceptionAction<T>) () ->
wrappedClass.cast(mo.get()), acc);
} else {
return wrappedClass.cast(mo.get());
}
}
}finally{
} finally {
AccessController.doPrivileged(new SetCcl(old));
}
} catch (PrivilegedActionException pe) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -343,10 +343,9 @@ public class ServerNotifForwarder {
//----------------
// PRIVATE METHODS
//----------------
@SuppressWarnings("removal")
private Subject getSubject() {
return Subject.getSubject(AccessController.getContext());
return Subject.current();
}
private void checkState() throws IOException {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,6 +42,7 @@ import java.util.regex.Pattern;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.security.auth.Subject;
import jdk.internal.access.SharedSecrets;
/**
* <p>An object of this class implements the MBeanServerAccessController
@@ -300,16 +301,19 @@ public class MBeanServerFileAccessController
}
}
@SuppressWarnings("removal")
private synchronized void checkAccess(AccessType requiredAccess, String arg) {
@SuppressWarnings("removal")
final AccessControlContext acc = AccessController.getContext();
@SuppressWarnings("removal")
final Subject s =
AccessController.doPrivileged(new PrivilegedAction<>() {
public Subject run() {
return Subject.getSubject(acc);
}
Subject s = null;
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
s = Subject.current();
} else {
final AccessControlContext acc = AccessController.getContext();
s = AccessController.doPrivileged(new PrivilegedAction<>() {
public Subject run() {
return Subject.getSubject(acc);
}
});
}
if (s == null) return; /* security has not been enabled */
final Set<Principal> principals = s.getPrincipals();
String newPropertyValue = null;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -60,6 +60,8 @@ import javax.management.MBeanServerConnection;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.security.auth.Subject;
import jdk.internal.access.SharedSecrets;
import static javax.management.monitor.MonitorNotification.*;
/**
@@ -169,8 +171,9 @@ public abstract class Monitor
new CopyOnWriteArrayList<>();
/**
* AccessControlContext of the Monitor.start() caller.
* Subject and possibly AccessControlContext of the Monitor.start() caller.
*/
private volatile Subject subject;
@SuppressWarnings("removal")
private static final AccessControlContext noPermissionsACC =
new AccessControlContext(
@@ -713,10 +716,14 @@ public abstract class Monitor
//
cleanupIsComplexTypeAttribute();
// Cache the AccessControlContext of the Monitor.start() caller.
// Cache the Subject or AccessControlContext of the Monitor.start() caller.
// The monitor tasks will be executed within this context.
//
acc = AccessController.getContext();
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
subject = Subject.current();
} else {
acc = AccessController.getContext();
}
// Start the scheduler.
//
@@ -747,8 +754,9 @@ public abstract class Monitor
//
cleanupFutures();
// Reset the AccessControlContext.
// Reset the Subject and AccessControlContext.
//
subject = null;
acc = noPermissionsACC;
// Reset the complex type attribute information
@@ -1512,9 +1520,11 @@ public abstract class Monitor
@SuppressWarnings("removal")
public void run() {
final ScheduledFuture<?> sf;
final Subject s;
final AccessControlContext ac;
synchronized (Monitor.this) {
sf = Monitor.this.schedulerFuture;
s = Monitor.this.subject;
ac = Monitor.this.acc;
}
PrivilegedAction<Void> action = new PrivilegedAction<>() {
@@ -1531,10 +1541,20 @@ public abstract class Monitor
return null;
}
};
if (ac == null) {
throw new SecurityException("AccessControlContext cannot be null");
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
// No SecurityManager permitted:
if (s == null) {
action.run();
} else {
Subject.doAs(s, action);
}
} else {
if (ac == null) {
throw new SecurityException("AccessControlContext cannot be null");
}
// ACC means SM is permitted.
AccessController.doPrivileged(action, ac);
}
AccessController.doPrivileged(action, ac);
synchronized (Monitor.this) {
if (Monitor.this.isActive() &&
Monitor.this.schedulerFuture == sf) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
@@ -67,6 +68,8 @@ import static jdk.internal.net.http.common.Utils.permissionForProxy;
*/
final class Exchange<T> {
static final int MAX_NON_FINAL_RESPONSES =
Utils.getIntegerNetProperty("jdk.httpclient.maxNonFinalResponses", 8);
final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
final HttpRequestImpl request;
@@ -91,6 +94,8 @@ final class Exchange<T> {
// exchange so that it can be aborted/timed out mid setup.
final ConnectionAborter connectionAborter = new ConnectionAborter();
final AtomicInteger nonFinalResponses = new AtomicInteger();
Exchange(HttpRequestImpl request, MultiExchange<T> multi) {
this.request = request;
this.upgrading = false;
@@ -357,7 +362,7 @@ final class Exchange<T> {
public void h2Upgrade() {
upgrading = true;
request.setH2Upgrade(client.client2());
request.setH2Upgrade(this);
}
synchronized IOException getCancelCause() {
@@ -458,6 +463,7 @@ final class Exchange<T> {
Log.logResponse(r1::toString);
int rcode = r1.statusCode();
if (rcode == 100) {
nonFinalResponses.incrementAndGet();
Log.logTrace("Received 100-Continue: sending body");
if (debug.on()) debug.log("Received 100-Continue for %s", r1);
CompletableFuture<Response> cf =
@@ -534,12 +540,20 @@ final class Exchange<T> {
+ rsp.statusCode());
}
assert exchImpl != null : "Illegal state - current exchange isn't set";
// ignore this Response and wait again for the subsequent response headers
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
// we recompose the CF again into the ignore1xxResponse check/function because
// the 1xx response is allowed to be sent multiple times for a request, before
// a final response arrives
return cf.thenCompose(this::ignore1xxResponse);
int count = nonFinalResponses.incrementAndGet();
if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) {
return MinimalFuture.failedFuture(
new ProtocolException(String.format(
"Too many interim responses received: %s > %s",
count, MAX_NON_FINAL_RESPONSES)));
} else {
// ignore this Response and wait again for the subsequent response headers
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
// we recompose the CF again into the ignore1xxResponse check/function because
// the 1xx response is allowed to be sent multiple times for a request, before
// a final response arrives
return cf.thenCompose(this::ignore1xxResponse);
}
} else {
// return the already completed future
return MinimalFuture.completedFuture(rsp);
@@ -804,6 +818,14 @@ final class Exchange<T> {
return multi.version();
}
boolean pushEnabled() {
return pushGroup != null;
}
String h2cSettingsStrings() {
return client.client2().getSettingsString(pushEnabled());
}
String dbgString() {
return dbgTag;
}

View File

@@ -25,6 +25,7 @@
package jdk.internal.net.http;
import java.io.IOException;
import java.net.ProtocolException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@@ -53,6 +54,12 @@ class Http1HeaderParser {
private int responseCode;
private HttpHeaders headers;
private Map<String,List<String>> privateMap = new HashMap<>();
private long size;
private static final int K = 1024;
private static final int MAX_HTTP_HEADER_SIZE = Utils.getIntegerNetProperty(
"jdk.http.maxHeaderSize",
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K, true);
enum State { INITIAL,
STATUS_LINE,
@@ -164,11 +171,16 @@ class Http1HeaderParser {
return (char)(input.get() & 0xFF);
}
private void readResumeStatusLine(ByteBuffer input) {
private void readResumeStatusLine(ByteBuffer input) throws ProtocolException {
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
int count = 0;
char c = 0;
while (input.hasRemaining() && (c = get(input)) != CR) {
if (c == LF) break;
sb.append(c);
if (++count > max) {
checkMaxHeaderSize(sb.length());
}
}
if (c == CR) {
state = State.STATUS_LINE_FOUND_CR;
@@ -185,6 +197,7 @@ class Http1HeaderParser {
}
statusLine = sb.toString();
size = size + 32 + statusLine.length();
sb = new StringBuilder();
if (!statusLine.startsWith("HTTP/1.")) {
throw protocolException("Invalid status line: \"%s\"", statusLine);
@@ -205,7 +218,23 @@ class Http1HeaderParser {
state = State.STATUS_LINE_END;
}
private void maybeStartHeaders(ByteBuffer input) {
private void checkMaxHeaderSize(int sz) throws ProtocolException {
long s = size + sz + 32;
if (MAX_HTTP_HEADER_SIZE > 0 && s > MAX_HTTP_HEADER_SIZE) {
throw new ProtocolException(String.format("Header size too big: %s > %s",
s, MAX_HTTP_HEADER_SIZE));
}
}
static private long newSize(long size, int name, int value) throws ProtocolException {
long newSize = size + name + value + 32;
if (MAX_HTTP_HEADER_SIZE > 0 && newSize > MAX_HTTP_HEADER_SIZE) {
throw new ProtocolException(String.format("Header size too big: %s > %s",
newSize, MAX_HTTP_HEADER_SIZE));
}
return newSize;
}
private void maybeStartHeaders(ByteBuffer input) throws ProtocolException {
assert state == State.STATUS_LINE_END;
assert sb.length() == 0;
char c = get(input);
@@ -215,6 +244,7 @@ class Http1HeaderParser {
state = State.STATUS_LINE_END_LF;
} else {
sb.append(c);
checkMaxHeaderSize(sb.length());
state = State.HEADER;
}
}
@@ -232,9 +262,11 @@ class Http1HeaderParser {
}
}
private void readResumeHeader(ByteBuffer input) {
private void readResumeHeader(ByteBuffer input) throws ProtocolException {
assert state == State.HEADER;
assert input.hasRemaining();
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
int count = 0;
while (input.hasRemaining()) {
char c = get(input);
if (c == CR) {
@@ -248,6 +280,9 @@ class Http1HeaderParser {
if (c == HT)
c = SP;
sb.append(c);
if (++count > max) {
checkMaxHeaderSize(sb.length());
}
}
}
@@ -268,12 +303,12 @@ class Http1HeaderParser {
if (!Utils.isValidValue(value)) {
throw protocolException("Invalid header value \"%s: %s\"", name, value);
}
size = newSize(size, name.length(), value.length());
privateMap.computeIfAbsent(name.toLowerCase(Locale.US),
k -> new ArrayList<>()).add(value);
}
private void resumeOrLF(ByteBuffer input) {
private void resumeOrLF(ByteBuffer input) throws ProtocolException {
assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF;
char c = state == State.HEADER_FOUND_LF ? LF : get(input);
if (c == LF) {
@@ -283,10 +318,12 @@ class Http1HeaderParser {
state = State.HEADER_FOUND_CR_LF;
} else if (c == SP || c == HT) {
sb.append(SP); // parity with MessageHeaders
checkMaxHeaderSize(sb.length());
state = State.HEADER;
} else {
sb = new StringBuilder();
sb.append(c);
checkMaxHeaderSize(1);
state = State.HEADER;
}
}
@@ -312,6 +349,7 @@ class Http1HeaderParser {
} else if (c == SP || c == HT) {
assert sb.length() != 0;
sb.append(SP); // continuation line
checkMaxHeaderSize(sb.length());
state = State.HEADER;
} else {
if (sb.length() > 0) {
@@ -322,6 +360,7 @@ class Http1HeaderParser {
addHeaderFromString(headerString);
}
sb.append(c);
checkMaxHeaderSize(sb.length());
state = State.HEADER;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import jdk.internal.net.http.common.Log;
import jdk.internal.net.http.common.Logger;
import jdk.internal.net.http.common.MinimalFuture;
import jdk.internal.net.http.common.Utils;
@@ -46,6 +45,7 @@ import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
/**
* Http2 specific aspects of HttpClientImpl
@@ -98,16 +98,20 @@ class Http2ClientImpl {
CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req,
Exchange<?> exchange) {
String key = Http2Connection.keyFor(req);
boolean pushEnabled = exchange.pushEnabled();
connectionPoolLock.lock();
try {
Http2Connection connection = connections.get(key);
if (connection != null) {
try {
if (!connection.tryReserveForPoolCheckout() || !connection.reserveStream(true)) {
if (!connection.tryReserveForPoolCheckout()
|| !connection.reserveStream(true, pushEnabled)) {
if (debug.on())
debug.log("removing connection from pool since it couldn't be" +
" reserved for use: %s", connection);
" reserved for use%s: %s",
pushEnabled ? " with server push enabled" : "",
connection);
removeFromPool(connection);
} else {
// fast path if connection already exists
@@ -137,7 +141,7 @@ class Http2ClientImpl {
try {
if (conn != null) {
try {
conn.reserveStream(true);
conn.reserveStream(true, exchange.pushEnabled());
} catch (IOException e) {
throw new UncheckedIOException(e); // shouldn't happen
}
@@ -183,10 +187,21 @@ class Http2ClientImpl {
}
Http2Connection c1 = connections.putIfAbsent(key, c);
if (c1 != null) {
c.setFinalStream();
if (debug.on())
debug.log("existing entry in connection pool for %s", key);
return false;
if (c.serverPushEnabled() && !c1.serverPushEnabled()) {
c1.setFinalStream();
connections.remove(key, c1);
connections.put(key, c);
if (debug.on()) {
debug.log("Replacing %s with %s in connection pool", c1, c);
}
if (c1.shouldClose()) c1.close();
return true;
} else {
c.setFinalStream();
if (debug.on())
debug.log("existing entry in connection pool for %s", key);
return false;
}
}
if (debug.on())
debug.log("put in the connection pool: %s", c);
@@ -250,8 +265,8 @@ class Http2ClientImpl {
}
/** Returns the client settings as a base64 (url) encoded string */
String getSettingsString() {
SettingsFrame sf = getClientSettings();
String getSettingsString(boolean defaultServerPush) {
SettingsFrame sf = getClientSettings(defaultServerPush);
byte[] settings = sf.toByteArray(); // without the header
Base64.Encoder encoder = Base64.getUrlEncoder()
.withoutPadding();
@@ -261,14 +276,7 @@ class Http2ClientImpl {
private static final int K = 1024;
private static int getParameter(String property, int min, int max, int defaultValue) {
int value = Utils.getIntegerNetProperty(property, defaultValue);
// use default value if misconfigured
if (value < min || value > max) {
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
"using default={4}", property, value, min, max, defaultValue);
value = defaultValue;
}
return value;
return Utils.getIntegerNetProperty(property, min, max, defaultValue, true);
}
// used for the connection window, to have a connection window size
@@ -288,7 +296,18 @@ class Http2ClientImpl {
streamWindow, Integer.MAX_VALUE, defaultValue);
}
SettingsFrame getClientSettings() {
/**
* This method is used to test whether pushes are globally
* disabled on all connections.
* @return true if pushes are globally disabled on all connections
*/
boolean serverPushDisabled() {
return getParameter(
"jdk.httpclient.enablepush",
0, 1, 1) == 0;
}
SettingsFrame getClientSettings(boolean defaultServerPush) {
SettingsFrame frame = new SettingsFrame();
// default defined for HTTP/2 is 4 K, we use 16 K.
frame.setParameter(HEADER_TABLE_SIZE, getParameter(
@@ -297,14 +316,15 @@ class Http2ClientImpl {
// O: does not accept push streams. 1: accepts push streams.
frame.setParameter(ENABLE_PUSH, getParameter(
"jdk.httpclient.enablepush",
0, 1, 1));
0, 1, defaultServerPush ? 1 : 0));
// HTTP/2 recommends to set the number of concurrent streams
// no lower than 100. We use 100. 0 means no stream would be
// accepted. That would render the client to be non functional,
// so we won't let 0 be configured for our Http2ClientImpl.
// no lower than 100. We use 100, unless push promises are
// disabled.
int initialServerStreams = frame.getParameter(ENABLE_PUSH) == 0
? 0 : 100;
frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter(
"jdk.httpclient.maxstreams",
1, Integer.MAX_VALUE, 100));
0, Integer.MAX_VALUE, initialServerStreams));
// Maximum size is 2^31-1. Don't allow window size to be less
// than the minimum frame size as this is likely to be a
// configuration error. HTTP/2 specify a default of 64 * K -1,
@@ -317,6 +337,14 @@ class Http2ClientImpl {
frame.setParameter(MAX_FRAME_SIZE, getParameter(
"jdk.httpclient.maxframesize",
16 * K, 16 * K * K -1, 16 * K));
// Maximum field section size we're prepared to accept
// This is the uncompressed name + value size + 32 per field line
int maxHeaderSize = getParameter(
"jdk.http.maxHeaderSize",
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K);
// If the property is <= 0 the value is unlimited
if (maxHeaderSize <= 0) maxHeaderSize = -1;
frame.setParameter(MAX_HEADER_LIST_SIZE, maxHeaderSize);
return frame;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* 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,7 @@ import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.nio.ByteBuffer;
@@ -47,6 +48,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@@ -86,10 +89,12 @@ import jdk.internal.net.http.hpack.DecodingCallback;
import jdk.internal.net.http.hpack.Encoder;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_INITIAL_WINDOW_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.INITIAL_WINDOW_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
/**
* An Http2Connection. Encapsulates the socket(channel) and any SSLEngine used
@@ -325,6 +330,45 @@ class Http2Connection {
}
}
private final class PushPromiseDecoder extends HeaderDecoder implements DecodingCallback {
final int parentStreamId;
final int pushPromiseStreamId;
final Stream<?> parent;
final AtomicReference<Throwable> errorRef = new AtomicReference<>();
PushPromiseDecoder(int parentStreamId, int pushPromiseStreamId, Stream<?> parent) {
this.parentStreamId = parentStreamId;
this.pushPromiseStreamId = pushPromiseStreamId;
this.parent = parent;
}
@Override
protected void addHeader(String name, String value) {
if (errorRef.get() == null) {
super.addHeader(name, value);
}
}
@Override
public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException {
try {
DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize);
} catch (ProtocolException pe) {
if (parent != null) {
if (errorRef.compareAndSet(null, pe)) {
// cancel the parent stream
resetStream(pushPromiseStreamId, ResetFrame.REFUSED_STREAM);
parent.onProtocolError(pe);
}
} else {
// interrupt decoding and closes the connection
throw pe;
}
}
}
}
private static final int HALF_CLOSED_LOCAL = 1;
private static final int HALF_CLOSED_REMOTE = 2;
@@ -353,7 +397,7 @@ class Http2Connection {
private final Decoder hpackIn;
final SettingsFrame clientSettings;
private volatile SettingsFrame serverSettings;
private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
private volatile PushContinuationState pushContinuationState;
private final String key; // for HttpClientImpl.connections map
private final FramesDecoder framesDecoder;
@@ -367,12 +411,24 @@ class Http2Connection {
private final FramesController framesController = new FramesController();
private final Http2TubeSubscriber subscriber;
final ConnectionWindowUpdateSender windowUpdater;
private volatile Throwable cause;
private final AtomicReference<Throwable> cause = new AtomicReference<>();
private volatile Supplier<ByteBuffer> initial;
private volatile Stream<?> initialStream;
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
private ValidatingHeadersConsumer orphanedConsumer;
private final AtomicInteger orphanedHeaders = new AtomicInteger();
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
static final int MAX_LITERAL_WITH_INDEXING =
Utils.getIntegerNetProperty("jdk.httpclient.maxLiteralWithIndexing",512);
// The maximum number of HEADER frames, CONTINUATION frames, or PUSH_PROMISE frames
// referring to an already closed or non-existent stream that a client will accept to
// process. Receiving frames referring to non-existent or closed streams doesn't necessarily
// constitute an HTTP/2 protocol error, but receiving too many may indicate a problem
// with the connection. If this limit is reached, a {@link java.net.ProtocolException
// ProtocolException} will be raised and the connection will be closed.
static final int MAX_ORPHANED_HEADERS = 1024;
// TODO: need list of control frames from other threads
// that need to be sent
@@ -380,19 +436,21 @@ class Http2Connection {
private Http2Connection(HttpConnection connection,
Http2ClientImpl client2,
int nextstreamid,
String key) {
String key,
boolean defaultServerPush) {
this.connection = connection;
this.client2 = client2;
this.subscriber = new Http2TubeSubscriber(client2.client());
this.nextstreamid = nextstreamid;
this.key = key;
this.clientSettings = this.client2.getClientSettings();
this.clientSettings = this.client2.getClientSettings(defaultServerPush);
this.framesDecoder = new FramesDecoder(this::processFrame,
clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE));
// serverSettings will be updated by server
this.serverSettings = SettingsFrame.defaultRFCSettings();
this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE),
clientSettings.getParameter(MAX_HEADER_LIST_SIZE), MAX_LITERAL_WITH_INDEXING);
if (debugHpack.on()) {
debugHpack.log("For the record:" + super.toString());
debugHpack.log("Decoder created: %s", hpackIn);
@@ -411,14 +469,16 @@ class Http2Connection {
private Http2Connection(HttpConnection connection,
Http2ClientImpl client2,
Exchange<?> exchange,
Supplier<ByteBuffer> initial)
Supplier<ByteBuffer> initial,
boolean defaultServerPush)
throws IOException, InterruptedException
{
this(connection,
client2,
3, // stream 1 is registered during the upgrade
keyFor(connection));
reserveStream(true);
keyFor(connection),
defaultServerPush);
reserveStream(true, clientSettings.getFlag(ENABLE_PUSH));
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
Stream<?> initialStream = createStream(exchange);
@@ -451,7 +511,8 @@ class Http2Connection {
Exchange<?> exchange,
Supplier<ByteBuffer> initial)
{
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial));
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial,
exchange.pushEnabled()));
}
// Requires TLS handshake. So, is really async
@@ -475,7 +536,8 @@ class Http2Connection {
.thenCompose(notused-> {
CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
try {
Http2Connection hc = new Http2Connection(request, h2client, connection);
Http2Connection hc = new Http2Connection(request, h2client,
connection, exchange.pushEnabled());
cf.complete(hc);
} catch (IOException e) {
cf.completeExceptionally(e);
@@ -490,13 +552,15 @@ class Http2Connection {
*/
private Http2Connection(HttpRequestImpl request,
Http2ClientImpl h2client,
HttpConnection connection)
HttpConnection connection,
boolean defaultServerPush)
throws IOException
{
this(connection,
h2client,
1,
keyFor(request));
keyFor(request),
defaultServerPush);
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
@@ -519,24 +583,30 @@ class Http2Connection {
// if false returned then a new Http2Connection is required
// if true, the stream may be assigned to this connection
// for server push, if false returned, then the stream should be cancelled
boolean reserveStream(boolean clientInitiated) throws IOException {
boolean reserveStream(boolean clientInitiated, boolean pushEnabled) throws IOException {
stateLock.lock();
try {
return reserveStream0(clientInitiated);
return reserveStream0(clientInitiated, pushEnabled);
} finally {
stateLock.unlock();
}
}
private boolean reserveStream0(boolean clientInitiated) throws IOException {
private boolean reserveStream0(boolean clientInitiated, boolean pushEnabled) throws IOException {
if (finalStream()) {
return false;
}
if (clientInitiated && (lastReservedClientStreamid + 2) >= MAX_CLIENT_STREAM_ID) {
// If requesting to reserve a stream for an exchange for which push is enabled,
// we will reserve the stream in this connection only if this connection is also
// push enabled, unless pushes are globally disabled.
boolean pushCompatible = !clientInitiated || !pushEnabled
|| this.serverPushEnabled()
|| client2.serverPushDisabled();
if (clientInitiated && (lastReservedClientStreamid >= MAX_CLIENT_STREAM_ID -2 || !pushCompatible)) {
setFinalStream();
client2.removeFromPool(this);
return false;
} else if (!clientInitiated && (lastReservedServerStreamid + 2) >= MAX_SERVER_STREAM_ID) {
} else if (!clientInitiated && (lastReservedServerStreamid >= MAX_SERVER_STREAM_ID - 2)) {
setFinalStream();
client2.removeFromPool(this);
return false;
@@ -561,6 +631,15 @@ class Http2Connection {
return true;
}
boolean shouldClose() {
stateLock.lock();
try {
return finalStream() && streams.isEmpty();
} finally {
stateLock.unlock();
}
}
/**
* Throws an IOException if h2 was not negotiated
*/
@@ -688,6 +767,10 @@ class Http2Connection {
return this.key;
}
public boolean serverPushEnabled() {
return clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
}
boolean offerConnection() {
return client2.offerConnection(this);
}
@@ -790,7 +873,7 @@ class Http2Connection {
}
Throwable getRecordedCause() {
return cause;
return cause.get();
}
void shutdown(Throwable t) {
@@ -799,11 +882,11 @@ class Http2Connection {
stateLock.lock();
try {
if (!markShutdownRequested()) return;
Throwable initialCause = this.cause;
if (initialCause == null && t != null) this.cause = t;
cause.compareAndSet(null, t);
} finally {
stateLock.unlock();
}
if (Log.errors()) {
if (t!= null && (!(t instanceof EOFException) || isActive())) {
Log.logError(t);
@@ -814,6 +897,7 @@ class Http2Connection {
}
}
client2.removeFromPool(this);
subscriber.stop(cause.get());
for (Stream<?> s : streams.values()) {
try {
s.connectionClosing(t);
@@ -867,17 +951,39 @@ class Http2Connection {
return;
}
if (frame instanceof PushPromiseFrame && !serverPushEnabled()) {
String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0";
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
Stream<?> stream = getStream(streamid);
var nextstreamid = this.nextstreamid;
if (stream == null && (streamid & 0x01) == 0x01 && streamid >= nextstreamid) {
String protocolError = String.format(
"received a frame for a non existing streamid(%s) >= nextstreamid(%s)",
streamid, nextstreamid);
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
if (stream == null && pushContinuationState == null) {
// Should never receive a frame with unknown stream id
if (frame instanceof HeaderFrame) {
if (frame instanceof HeaderFrame hf) {
String protocolError = checkMaxOrphanedHeadersExceeded(hf);
if (protocolError != null) {
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
// always decode the headers as they may affect
// connection-level HPACK decoding state
DecodingCallback decoder = new ValidatingHeadersConsumer()::onDecoded;
if (orphanedConsumer == null || frame.getClass() != ContinuationFrame.class) {
orphanedConsumer = new ValidatingHeadersConsumer();
}
DecodingCallback decoder = orphanedConsumer::onDecoded;
try {
decodeHeaders((HeaderFrame) frame, decoder);
} catch (UncheckedIOException e) {
decodeHeaders(hf, decoder);
} catch (IOException | UncheckedIOException e) {
protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
@@ -905,29 +1011,41 @@ class Http2Connection {
// While push frame is not null, the only acceptable frame on this
// stream is a Continuation frame
if (pushContinuationState != null) {
PushContinuationState pcs = pushContinuationState;
if (pcs != null) {
if (frame instanceof ContinuationFrame cf) {
if (stream == null) {
String protocolError = checkMaxOrphanedHeadersExceeded(cf);
if (protocolError != null) {
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
}
try {
if (streamid == pushContinuationState.pushContFrame.streamid())
handlePushContinuation(stream, cf);
else
protocolError(ErrorFrame.PROTOCOL_ERROR, "Received a Continuation Frame with an " +
"unexpected stream id");
} catch (UncheckedIOException e) {
if (streamid == pcs.pushContFrame.streamid())
handlePushContinuation(pcs, stream, cf);
else {
String protocolError = "Received a CONTINUATION with " +
"unexpected stream id: " + streamid + " != "
+ pcs.pushContFrame.streamid();
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
}
} catch (IOException | UncheckedIOException e) {
debug.log("Error handling Push Promise with Continuation: " + e.getMessage(), e);
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
} else {
pushContinuationState = null;
protocolError(ErrorFrame.PROTOCOL_ERROR, "Expected a Continuation frame but received " + frame);
String protocolError = "Expected a CONTINUATION frame but received " + frame;
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
return;
}
} else {
if (frame instanceof PushPromiseFrame pp) {
try {
handlePushPromise(stream, pp);
} catch (UncheckedIOException e) {
} catch (IOException | UncheckedIOException e) {
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
@@ -935,7 +1053,7 @@ class Http2Connection {
// decode headers
try {
decodeHeaders(hf, stream.rspHeadersConsumer());
} catch (UncheckedIOException e) {
} catch (IOException | UncheckedIOException e) {
debug.log("Error decoding headers: " + e.getMessage(), e);
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
@@ -948,6 +1066,16 @@ class Http2Connection {
}
}
private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) {
if (MAX_ORPHANED_HEADERS > 0 ) {
int orphaned = orphanedHeaders.incrementAndGet();
if (orphaned < 0 || orphaned > MAX_ORPHANED_HEADERS) {
return "Too many orphaned header frames received on connection";
}
}
return null;
}
final void dropDataFrame(DataFrame df) {
if (isMarked(closedState, SHUTDOWN_REQUESTED)) return;
if (debug.on()) {
@@ -972,38 +1100,65 @@ class Http2Connection {
private <T> void handlePushPromise(Stream<T> parent, PushPromiseFrame pp)
throws IOException
{
int promisedStreamid = pp.getPromisedStream();
if ((promisedStreamid & 0x01) != 0x00) {
throw new ProtocolException("Received PUSH_PROMISE for stream " + promisedStreamid);
}
int streamId = pp.streamid();
if ((streamId & 0x01) != 0x01) {
throw new ProtocolException("Received PUSH_PROMISE on stream " + streamId);
}
// always decode the headers as they may affect connection-level HPACK
// decoding state
assert pushContinuationState == null;
HeaderDecoder decoder = new HeaderDecoder();
decodeHeaders(pp, decoder::onDecoded);
int promisedStreamid = pp.getPromisedStream();
PushPromiseDecoder decoder = new PushPromiseDecoder(streamId, promisedStreamid, parent);
decodeHeaders(pp, decoder);
if (pp.endHeaders()) {
completePushPromise(promisedStreamid, parent, decoder.headers());
if (decoder.errorRef.get() == null) {
completePushPromise(promisedStreamid, parent, decoder.headers());
}
} else {
pushContinuationState = new PushContinuationState(decoder, pp);
}
}
private <T> void handlePushContinuation(Stream<T> parent, ContinuationFrame cf)
private <T> void handlePushContinuation(PushContinuationState pcs, Stream<T> parent, ContinuationFrame cf)
throws IOException {
var pcs = pushContinuationState;
decodeHeaders(cf, pcs.pushContDecoder::onDecoded);
assert pcs.pushContFrame.streamid() == cf.streamid() : String.format(
"Received CONTINUATION on a different stream %s != %s",
cf.streamid(), pcs.pushContFrame.streamid());
decodeHeaders(cf, pcs.pushContDecoder);
// if all continuations are sent, set pushWithContinuation to null
if (cf.endHeaders()) {
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
pcs.pushContDecoder.headers());
if (pcs.pushContDecoder.errorRef.get() == null) {
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
pcs.pushContDecoder.headers());
}
pushContinuationState = null;
}
}
private <T> void completePushPromise(int promisedStreamid, Stream<T> parent, HttpHeaders headers)
throws IOException {
if (parent == null) {
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
return;
}
HttpRequestImpl parentReq = parent.request;
if (promisedStreamid < nextPushStream) {
// From RFC 9113 section 5.1.1:
// The identifier of a newly established stream MUST be numerically
// greater than all streams that the initiating endpoint has
// opened or reserved.
protocolError(ResetFrame.PROTOCOL_ERROR, String.format(
"Unexpected stream identifier: %s < %s", promisedStreamid, nextPushStream));
return;
}
if (promisedStreamid != nextPushStream) {
// we don't support skipping stream ids;
resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR);
return;
} else if (!reserveStream(false)) {
} else if (!reserveStream(false, true)) {
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
return;
} else {
@@ -1172,11 +1327,17 @@ class Http2Connection {
private void protocolError(int errorCode, String msg)
throws IOException
{
String protocolError = "protocol error" + (msg == null?"":(": " + msg));
ProtocolException protocolException =
new ProtocolException(protocolError);
if (markHalfClosedLocal()) {
framesDecoder.close(protocolError);
subscriber.stop(protocolException);
if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException);
GoAwayFrame frame = new GoAwayFrame(0, errorCode);
sendFrame(frame);
}
shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg))));
shutdown(protocolException);
}
private void handleSettings(SettingsFrame frame)
@@ -1318,7 +1479,7 @@ class Http2Connection {
<T> Stream.PushedStream<T> createPushStream(Stream<T> parent, Exchange<T> pushEx) {
PushGroup<T> pg = parent.exchange.getPushGroup();
return new Stream.PushedStream<>(pg, this, pushEx);
return new Stream.PushedStream<>(parent, pg, this, pushEx);
}
/**
@@ -1428,16 +1589,18 @@ class Http2Connection {
private List<ByteBuffer> encodeHeadersImpl(int bufferSize, HttpHeaders... headers) {
ByteBuffer buffer = getHeaderBuffer(bufferSize);
List<ByteBuffer> buffers = new ArrayList<>();
for(HttpHeaders header : headers) {
for (HttpHeaders header : headers) {
for (Map.Entry<String, List<String>> e : header.map().entrySet()) {
String lKey = e.getKey().toLowerCase(Locale.US);
List<String> values = e.getValue();
for (String value : values) {
hpackOut.header(lKey, value);
while (!hpackOut.encode(buffer)) {
buffer.flip();
buffers.add(buffer);
buffer = getHeaderBuffer(bufferSize);
if (!buffer.hasRemaining()) {
buffer.flip();
buffers.add(buffer);
buffer = getHeaderBuffer(bufferSize);
}
}
}
}
@@ -1476,7 +1639,7 @@ class Http2Connection {
Throwable cause = null;
synchronized (this) {
if (isMarked(closedState, SHUTDOWN_REQUESTED)) {
cause = this.cause;
cause = this.cause.get();
if (cause == null) {
cause = new IOException("Connection closed");
}
@@ -1515,6 +1678,8 @@ class Http2Connection {
Stream<?> stream = registerNewStream(oh);
// provide protection from inserting unordered frames between Headers and Continuation
if (stream != null) {
// we are creating a new stream: reset orphaned header count
orphanedHeaders.set(0);
publisher.enqueue(encodeHeaders(oh, stream));
}
} else {
@@ -1583,7 +1748,7 @@ class Http2Connection {
private volatile Flow.Subscription subscription;
private volatile boolean completed;
private volatile boolean dropped;
private volatile Throwable error;
private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
private final ConcurrentLinkedQueue<ByteBuffer> queue
= new ConcurrentLinkedQueue<>();
private final SequentialScheduler scheduler =
@@ -1604,10 +1769,9 @@ class Http2Connection {
asyncReceive(buffer);
}
} catch (Throwable t) {
Throwable x = error;
if (x == null) error = t;
errorRef.compareAndSet(null, t);
} finally {
Throwable x = error;
Throwable x = errorRef.get();
if (x != null) {
if (debug.on()) debug.log("Stopping scheduler", x);
scheduler.stop();
@@ -1642,6 +1806,7 @@ class Http2Connection {
@Override
public void onNext(List<ByteBuffer> item) {
if (completed) return;
if (debug.on()) debug.log(() -> "onNext: got " + Utils.remaining(item)
+ " bytes in " + item.size() + " buffers");
queue.addAll(item);
@@ -1650,19 +1815,21 @@ class Http2Connection {
@Override
public void onError(Throwable throwable) {
if (completed) return;
if (debug.on()) debug.log(() -> "onError: " + throwable);
error = throwable;
errorRef.compareAndSet(null, throwable);
completed = true;
runOrSchedule();
}
@Override
public void onComplete() {
if (completed) return;
String msg = isActive()
? "EOF reached while reading"
: "Idle connection closed by HTTP/2 peer";
if (debug.on()) debug.log(msg);
error = new EOFException(msg);
errorRef.compareAndSet(null, new EOFException(msg));
completed = true;
runOrSchedule();
}
@@ -1674,6 +1841,18 @@ class Http2Connection {
// then we might not need the 'dropped' boolean?
dropped = true;
}
void stop(Throwable error) {
if (errorRef.compareAndSet(null, error)) {
completed = true;
scheduler.stop();
queue.clear();
if (subscription != null) {
subscription.cancel();
}
queue.clear();
}
}
}
boolean isActive() {

View File

@@ -964,7 +964,9 @@ final class HttpClientImpl extends HttpClient implements Trackable {
// SSLException
throw new SSLException(msg, throwable);
} else if (throwable instanceof ProtocolException) {
throw new ProtocolException(msg);
ProtocolException pe = new ProtocolException(msg);
pe.initCause(throwable);
throw pe;
} else if (throwable instanceof IOException) {
throw new IOException(msg, throwable);
} else {

Some files were not shown because too many files have changed in this diff Show More