From 9fb3d591f52ad5cbd1e885f698248e9ed33cecce Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 1 Oct 2026 06:38:01 +0000 Subject: [PATCH 01/98] 8385993: C2: Incorrect escape analysis in reduce allocation merges causes NPE Reviewed-by: cslucas, qamai --- src/hotspot/share/opto/escape.cpp | 125 +++++++++-- src/hotspot/share/opto/escape.hpp | 3 + .../TestReduceAllocationClonedCast.java | 204 ++++++++++++++++++ 3 files changed, 310 insertions(+), 22 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationClonedCast.java diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 858bb657802..776aba1ff1c 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1127,6 +1127,24 @@ void ConnectionGraph::updates_after_load_split(Node* data_phi, Node* previous_lo } Node* base = get_addp_base(new_addp); + if (base->Opcode() == Op_CastPP) { + Node* previous_base = get_addp_base(previous_addp); + if (previous_base->Opcode() == Op_CastPP && (base->_idx >= nodes_size() || ptnode_adr(base->_idx) == nullptr)) { + assert(previous_base != base, "Should have been pushed through Phi"); + // add the CastPP to the connection graph + _nodes.at_grow(base->_idx, nullptr); + PointsToNode* curr_castpp_ptn = ptnode_adr(previous_base->_idx); + add_local_var(base, curr_castpp_ptn->escape_state()); + add_edge(ptnode_adr(base->_idx), ptnode_adr(base->in(1)->_idx)); + JavaObjectNode* java_object = unique_java_object(base->in(1)); + if (java_object != nullptr) { + add_edge(ptnode_adr(base->_idx), java_object); + } + base = base->in(1); + assert(previous_base->in(1)->as_Phi()->in(i) == base, "an input to the cast was cloned"); + } + } + // The base might not be something that we can create an unique // type for. If that's the case we are done with that input. PointsToNode* jobj_ptn = unique_java_object(base); @@ -4330,7 +4348,22 @@ bool ConnectionGraph::split_AddP(Node *addp, Node *base) { // for the instance type. Note: C++ will not remove it since the call // has side effect. int alias_idx = _compile->get_alias_index(tinst); - igvn->set_type(addp, tinst); + if (igvn->type(addp)->isa_oopptr()) { + igvn->set_type(addp, tinst); + } else { + // Captured stores should remain raw memory stores +#ifdef ASSERT + InitializeNode* init = base->in(0)->in(0)->as_Initialize(); + bool found = false; + for (uint i = InitializeNode::RawStores; i < init->req(); i++) { + Node* st = init->in(i); + if (st->is_Store() && st->in(MemNode::Address) == addp) { + found = true; + } + } + assert(found, "expected AddP from captured store"); +#endif + } // record the allocation in the node map set_map(addp, get_map(base->_idx)); // Set addp's Base and Address to 'base'. @@ -4796,6 +4829,74 @@ Node* ConnectionGraph::find_inst_mem(Node* orig_mem, int alias_idx, Unique_Node_ return result; } +#ifdef ASSERT +void ConnectionGraph::verify_ram_after_reduce_phi(const Unique_Node_List &reducible_merges, const Unique_Node_List& reduced_merges) { + if (VerifyReduceAllocationMerges) { + { + // Checks that nodes added by reduce_phi() were properly added to the connection graph + ResourceMark rm; + Unique_Node_List wq; + wq.push(_compile->root()); + for (uint i = 0; i < wq.size(); ++i) { + Node* n = wq.at(i); + Node* base = nullptr; + switch (n->Opcode()) { + case Op_CastPP: + base = n->in(1); + while (base->Opcode() == Op_CastPP) { + base = base->in(1); + } + if (base->is_AddP()) { + // LibraryCallKit::inline_unsafe_flat_access() inserts a CastPP with an AddP input + base = get_addp_base(base); + } + break; + case Op_AddP: + base = get_addp_base(n); + break; + case Op_LoadP: + case Op_LoadN: { + Node* adr = n->in(MemNode::Address); + if (adr->is_AddP()) { + base = get_addp_base(adr); + } + break; + } + default: + break; + } + assert(base == nullptr || unique_java_object(base) == nullptr || !unique_java_object(base)->scalar_replaceable() || + (n->_idx < nodes_size() && ptnode_adr(n->_idx) != nullptr), "missing node"); + for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { + Node* u = n->fast_out(j); + wq.push(u); + } + } + } + + for (uint i = 0; i < reducible_merges.size(); i++) { + Node* phi = reducible_merges.at(i); + + if (!reduced_merges.member(phi)) { + phi->dump(2); + phi->dump(-2); + assert(false, "This reducible merge wasn't reduced."); + } + + // At this point reducible Phis shouldn't have AddP users anymore; only SafePoints or Casts. + for (DUIterator_Fast jmax, j = phi->fast_outs(jmax); j < jmax; j++) { + Node* use = phi->fast_out(j); + if (!use->is_SafePoint() && !use->is_CastPP()) { + phi->dump(2); + phi->dump(-2); + assert(false, "Unexpected user of reducible Phi -> %d:%s:%d", use->_idx, use->Name(), use->outcnt()); + } + } + } + } +} +#endif + Node* ConnectionGraph::find_inst_mem_assert_no_new_node(Node* orig_mem, int alias_idx, Unique_Node_List& orig_phis) { uint orig_uniq = _compile->unique(); Node* result = find_inst_mem(orig_mem, alias_idx, orig_phis); @@ -5222,27 +5323,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, } #ifdef ASSERT - if (VerifyReduceAllocationMerges) { - for (uint i = 0; i < reducible_merges.size(); i++) { - Node* phi = reducible_merges.at(i); - - if (!reduced_merges.member(phi)) { - phi->dump(2); - phi->dump(-2); - assert(false, "This reducible merge wasn't reduced."); - } - - // At this point reducible Phis shouldn't have AddP users anymore; only SafePoints or Casts. - for (DUIterator_Fast jmax, j = phi->fast_outs(jmax); j < jmax; j++) { - Node* use = phi->fast_out(j); - if (!use->is_SafePoint() && !use->is_CastPP()) { - phi->dump(2); - phi->dump(-2); - assert(false, "Unexpected user of reducible Phi -> %d:%s:%d", use->_idx, use->Name(), use->outcnt()); - } - } - } - } + verify_ram_after_reduce_phi(reducible_merges, reduced_merges); #endif // Go over all ArrayCopy nodes and if one of the inputs has a unique diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 44f2d6aaa69..aedfaa2810d 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -628,6 +628,9 @@ class ConnectionGraph: public ArenaObj { bool reduce_phi_on_safepoints(PhiNode* ophi); bool reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, Node* selector, Unique_Node_List& safepoints); void reduce_phi(PhiNode* ophi, GrowableArray &alloc_worklist); +#ifdef ASSERT + void verify_ram_after_reduce_phi(const Unique_Node_List &reducible_merges, const Unique_Node_List& reduced_merges); +#endif void set_not_scalar_replaceable(PointsToNode* ptn NOT_PRODUCT(COMMA const char* reason)) const { #ifndef PRODUCT diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationClonedCast.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationClonedCast.java new file mode 100644 index 00000000000..8f7b711eab4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationClonedCast.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/* + * @test + * @bug 8385993 + * @summary C2: Incorrect escape analysis in reduce allocation merges causes NPE + * @library /test/lib + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-UseDeepIGVNRevisit -XX:+UnlockDiagnosticVMOptions + * -XX:+StressIGVN -XX:StressSeed=30740779 -XX:CompileCommand=compileonly,${test.main.class}::* ${test.main.class} + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-UseDeepIGVNRevisit -XX:+UnlockDiagnosticVMOptions + * -XX:+StressIGVN -XX:StressSeed=42701780 -XX:CompileCommand=compileonly,${test.main.class}::* ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.escapeAnalysis; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.IOException; + +public class TestReduceAllocationClonedCast { + public static void main(String[] args) throws IOException { + for (int i = 0; i < 8_000; i++) { + test1(); + test2(); + test3(true, true); + test3(false, false); + test3(false, true); + test4(true, true); + test4(false, false); + test4(false, true); + } + System.out.println("DONE"); + } + + static int test1() { + int val = 0; + A1 a = getA1(Integer.valueOf(14)); + if (a != null) { + val += a.getA1(); + } + return val; + } + + static int test2() { + int val = 0; + A2 a = getA2(Integer.valueOf(14)); + if (a != null) { + val += a.getA1(); + } + return val; + } + + static A1 getA1(Object obj) { + java.util.Random rnd = new java.util.Random(); + int rndInt= rnd.nextInt(100); + if (rndInt < 15) { + return null; + } + + A1 retA = new A1(rndInt); + if (obj != null) { + B1 b = new B1(retA); + retA = b.getAFromB(obj); + } + + return retA; + } + + static A2 getA2(Object obj) { + java.util.Random rnd = new java.util.Random(); + int rndInt= rnd.nextInt(100); + if (rndInt < 15) { + return null; + } + + A2 retA = new A2(rndInt, rnd.nextInt(100), rnd.nextInt(100)); + if (obj != null) { + B2 b = new B2(retA); + retA = b.getAFromB(obj); + } + + return retA; + } + + static A fieldA = new A(42); + static int field; + + static int test3(boolean flag, boolean flag2) { + A a = null; + if (flag) { + a = new A(42); + } else { + a = fieldA; + if (flag2) { + if (a == null) { + throw new RuntimeException("not taken"); + } + } + field = 42; + } + return a.field; + } + + static int test4(boolean flag, boolean flag2) { + A a = null; + if (flag) { + a = new A(42); + } else { + a = fieldA; + if (flag2) { + if (a == null) { + throw new RuntimeException("not taken"); + } + } + } + return a.field; + } + + static class A { + int field; + + A(int v) { + field = v; + } + + } + + + static class A1 { + final Integer a1; + + A1(int a1) { + this.a1 = a1; + } + + int getA1() { + return a1.intValue(); + } + + } + + static class A2 { + final Integer a1; + final int a2; + final long a3; + + A2(int a1, int a2, long a3) { + this.a1 = a1; + this.a2 = a2; + this.a3 = a3; + } + + int getA1() { + return a1.intValue(); + } + + } + + static class B1 { + final A1 a; + B1(A1 a) { + this.a = a; + } + + A1 getAFromB(Object obj) { + return a; + } + } + + static class B2 { + final A2 a; + B2(A2 a) { + this.a = a; + } + + A2 getAFromB(Object obj) { + return a; + } + } +} From 10a0e008c05a89ebd35d992ecab0f69b2c0d7f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Thu, 1 Oct 2026 06:48:51 +0000 Subject: [PATCH 02/98] 8392337: [Valhalla] C2: incorrect escape analysis for mismatching scalarization of value argument and return Reviewed-by: qamai, roland, thartmann --- src/hotspot/share/opto/escape.cpp | 39 ++- src/hotspot/share/opto/escape.hpp | 7 +- .../valuetypes/TestReturnValueArgument.java | 280 ++++++++++++++++++ 3 files changed, 312 insertions(+), 14 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnValueArgument.java diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 776aba1ff1c..2796f7c908c 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1647,10 +1647,11 @@ void ConnectionGraph::add_proj(Node* n, Unique_Node_List* delayed_worklist) { } else if (n->as_Proj()->_con >= TypeFunc::Parms && n->in(0)->is_Call() && n->bottom_type()->isa_ptr()) { CallNode* call = n->in(0)->as_Call(); assert(call->tf()->returns_value_type_as_fields(), ""); - if (n->as_Proj()->_con == TypeFunc::Parms || !returns_an_argument(call)) { + if (n->as_Proj()->_con == TypeFunc::Parms || !returns_an_argument(call) || has_incompatible_argument_return(call)) { // either: // - not an argument returned // - the returned buffer for a returned scalarized argument + // - incompatible returned argument add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist); } else { add_local_var(n, PointsToNode::NoEscape); @@ -2284,16 +2285,26 @@ class DomainIterator : public StackObj { } }; -// Determine whether any arguments are returned. -bool ConnectionGraph::returns_an_argument(CallNode* call) { +bool ConnectionGraph::returns_an_argument(const CallNode* call) { ciMethod* meth = call->as_CallJava()->method(); + assert(meth != nullptr, "call must have an associated method pointer"); BCEscapeAnalyzer* call_analyzer = meth->get_bcea(); - if (call_analyzer == nullptr) { - return false; + const TypeTuple* d = call->tf()->domain_sig(); + for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { + const Type* t = d->field_at(i); + if (t->isa_ptr() != nullptr && + call_analyzer->is_arg_returned(i - TypeFunc::Parms)) { + return true; + } } + return false; +} +bool ConnectionGraph::has_incompatible_argument_return(const CallNode* call) { + ciMethod* meth = call->as_CallJava()->method(); + assert(meth != nullptr, "call must have an associated method pointer"); + BCEscapeAnalyzer* call_analyzer = meth->get_bcea(); const TypeTuple* d = call->tf()->domain_sig(); - bool ret_arg = false; int arg_num = 0; for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* t = d->field_at(i); @@ -2301,19 +2312,18 @@ bool ConnectionGraph::returns_an_argument(CallNode* call) { call_analyzer->is_arg_returned(i - TypeFunc::Parms)) { const bool scalarized_arg = meth->is_scalarized_arg(arg_num); if (scalarized_arg && !compatible_return(call->as_CallJava(), i)) { - return false; + return true; } if (call->tf()->returns_value_type_as_fields() != scalarized_arg) { - return false; + return true; } - ret_arg = true; } if (t != Type::HALF) { arg_num++; } } assert(arg_num == meth->signature()->count() + (meth->is_static() ? 0 : 1), "inconsistent argument count"); - return ret_arg; + return false; } void ConnectionGraph::add_call_node(CallNode* call) { @@ -2430,7 +2440,9 @@ void ConnectionGraph::add_call_node(CallNode* call) { // For scalarized argument/return: process_call_arguments() adds an edge between a call projection for a field // and the argument input to the call for that field. An edge is added between the projection for the returned // buffer and the call. - if (returns_an_argument(call) && !call->tf()->returns_value_type_as_fields()) { + // For incompatible argument/return (only one of them is scalarized, or their types do not match): the call is + // conservatively assumed to make all its oop arguments escape globally and return an unknown object. + if (returns_an_argument(call) && !has_incompatible_argument_return(call) && !call->tf()->returns_value_type_as_fields()) { // returns non scalarized argument add_local_var(call, PointsToNode::ArgEscape); } else { @@ -2639,8 +2651,9 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { break; // Boxing methods do not modify any oops. } BCEscapeAnalyzer* call_analyzer = (meth !=nullptr) ? meth->get_bcea() : nullptr; - // fall-through if not a Java method or no analyzer information - if (call_analyzer != nullptr) { + // Fall-through if not a Java method, no analyzer information, or + // incompatible argument returns. + if (call_analyzer != nullptr && !has_incompatible_argument_return(call)) { PointsToNode* call_ptn = ptnode_adr(call->_idx); bool ret_arg = returns_an_argument(call); for (DomainIterator di(call->as_CallJava()); di.has_next(); di.next()) { diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index aedfaa2810d..dfa21836ec5 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -372,7 +372,12 @@ class ConnectionGraph: public ArenaObj { // Compute the escape state for arguments to a call. void process_call_arguments(CallNode *call); - bool returns_an_argument(CallNode* call); + // Whether a call returns one of its arguments, according to BCEA. + bool returns_an_argument(const CallNode* call); + + // Whether a call's returned argument cannot be connected due to scalarization + // or type mismatches. + bool has_incompatible_argument_return(const CallNode* call); // Add PointsToNode node corresponding to a call void add_call_node(CallNode* call); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnValueArgument.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnValueArgument.java new file mode 100644 index 00000000000..5e9c7a0c86b --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnValueArgument.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.valhalla.valuetypes; + +import compiler.lib.ir_framework.*; + +/** + * @test + * @bug 8392337 + * @summary Test that C2 propagates escape state correctly for returned + * arguments in non-inlined calls for different scalarization choices. + * @library /test/lib / + * @enablePreview + * @run driver ${test.main.class} + */ + +class TestReturnValueArgument { + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + Scenario[] scenarios = new Scenario[2*2]; + int scenarioIndex = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + scenarios[scenarioIndex] = + new Scenario(scenarioIndex, + "--enable-preview", + "-XX:" + (i == 0 ? "-" : "+") + "ValueTypePassFieldsAsArgs", + "-XX:" + (j == 0 ? "-" : "+") + "ValueTypeReturnedAsFields"); + scenarioIndex++; + } + } + framework.addScenarios(scenarios); + framework.start(); + } + + static class IdentityObject { + int val; + } + + static value class ValueHolder { + IdentityObject obj; + ValueHolder(IdentityObject obj) { + this.obj = obj; + } + } + + static IdentityObject globalObj = null; + static ValueHolder globalHolder = null; + static Object globalPlainObj = null; + + @ForceInline + static ValueHolder createValueHolderWithSynchronizedFieldWrite() { + IdentityObject obj = new IdentityObject(); + ValueHolder holder = new ValueHolder(obj); + synchronized (obj) { + obj.val = 42; + } + return holder; + } + + @ForceInline + static ValueHolder createValueHolder() { + IdentityObject obj = new IdentityObject(); + ValueHolder holder = new ValueHolder(obj); + return holder; + } + + @DontInline + static ValueHolder returnArgument(ValueHolder holder) { + return holder; + } + + @DontInline + static Object returnAsObject(ValueHolder holder) { + return holder; + } + + @DontInline + static ValueHolder returnOneArgument(boolean first, ValueHolder holder1, ValueHolder holder2) { + if (first) { + return holder1; + } + return holder2; + } + + @DontInline + static ValueHolder returnMiddleArgument(ValueHolder holder1, ValueHolder holder2, ValueHolder holder3) { + return holder2; + } + + @DontInline + static ValueHolder returnArgumentAfterTwoSlots(long unused, ValueHolder holder) { + return holder; + } + + static class BaseReturner { + native ValueHolder returnArgument(ValueHolder holder); + } + + static final class Returner extends BaseReturner { + @DontInline + @Override + ValueHolder returnArgument(ValueHolder holder) { + return holder; + } + } + + @DontInline + static ValueHolder escapeAndReturnArgument(ValueHolder holder) { + globalHolder = holder; + return holder; + } + + @DontInline + static ValueHolder escapeFieldAndReturnArgument(ValueHolder holder) { + globalObj = holder.obj; + return holder; + } + + // Return holder passed as argument. Because the IdentityObject held by it + // is then stored globally, its synchronization should not be removed. + // Depending on the values of the ValueTypePassFieldsAsArgs and + // ValueTypeReturnedAsFields flags, the holder is scalarized as argument + // and/or return of the returnArgument() call. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnHolderAndEscapeIdentityObject() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder returnedHolder = returnArgument(holder); + globalObj = returnedHolder.obj; + } + + // Variant of the above test where the holder is passed as an argument after + // another argument taking two slots. The same outcome is expected. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnHolderAfterTwoSlotsAndEscapeIdentityObject() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder returnedHolder = returnArgumentAfterTwoSlots(42L, holder); + globalObj = returnedHolder.obj; + } + + // Variant of the above test where one of two passed holders is returned. + // The same outcome is expected for each of the IdentityObject instances. + @Test + @Arguments(values = {Argument.BOOLEAN_TOGGLE_FIRST_TRUE}) + @IR(counts = {IRNode.FAST_LOCK, "> 1", IRNode.FAST_UNLOCK, "> 1"}) + static void testReturnOneOfTwoHoldersAndEscapeIdentityObjects(boolean first) { + ValueHolder holder1 = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder holder2 = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder returnedHolder = returnOneArgument(first, holder1, holder2); + globalObj = returnedHolder.obj; + } + + // Variant of the above test where three holders are passed and always one + // of them is returned. Synchronization of the IdentityObject held by the + // returned holder should be retained. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnMiddleHolderAndEscapeIdentityObject() { + ValueHolder holder1 = createValueHolder(); + ValueHolder holder2 = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder holder3 = createValueHolder(); + ValueHolder returnedHolder = returnMiddleArgument(holder1, holder2, holder3); + globalObj = returnedHolder.obj; + } + + // Variant of the above test where the passed and returned holder escapes + // within the call. The same outcome is expected. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnEscapedHolder() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + escapeAndReturnArgument(holder); + } + + // Variant of the above test where the IdentityObject referred to by the + // passed and returned holder escapes within the call. The same outcome is + // expected. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnHolderWithEscapedField() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + escapeFieldAndReturnArgument(holder); + } + + // Return holder as an Object, which naturally inhibits scalarization of the + // returned value. Because the holder is then stored globally, the + // IdentityObject it points to escapes, and its synchronization should not + // be removed. Depending on the value of the ValueTypePassFieldsAsArgs flag, + // the holder is scalarized as argument of the returnAsObject() call. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnHolderAsObjectAndEscapeHolder() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + Object holderAsObject = returnAsObject(holder); + globalPlainObj = holderAsObject; + } + + // Return holder via a non-static override of a native method, which + // naturally inhibits scalarization of the holder as argument, but allows it + // to be scalarized on return. Because the IdentityObject held by it is then + // stored globally, its synchronization should not be removed. + @Test + @IR(counts = {IRNode.FAST_LOCK, "> 0", IRNode.FAST_UNLOCK, "> 0"}) + static void testReturnHolderViaNativeSuperclassAndEscapeIdentityObject() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder returnedHolder = new Returner().returnArgument(holder); + globalObj = returnedHolder.obj; + } + + // Return holder passed as argument, do not force a global escape. In the + // cases when the holder is passed and returned in the same form (both + // scalarized or both buffered), we expect C2 to exploit the bytecode escape + // analyzer's ability to detect the argument as "local" and "returned" and + // optimize away the lock and unlock operations implementing the + // synchronized block. + // Currently C2 does not exploit bytecode escape analyzer information for + // returned arguments with mismatched buffer/scalar representations. + @Test + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "true", "ValueTypeReturnedAsFields", "true"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "false", "ValueTypeReturnedAsFields", "false"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + static void testReturnHolderWithoutEscape() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + returnArgument(holder); + } + + // Variant of the above test where the holder is passed as an argument after + // another argument taking two slots. The same outcome is expected. + @Test + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "true", "ValueTypeReturnedAsFields", "true"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "false", "ValueTypeReturnedAsFields", "false"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + static void testReturnHolderAfterTwoSlotsWithoutEscape() { + ValueHolder holder = createValueHolderWithSynchronizedFieldWrite(); + returnArgumentAfterTwoSlots(42L, holder); + } + + // Variant of the above test where three holders are passed and always one + // of them is returned. Similarly to above, we expect C2 to remove all + // synchronization, which it does except for the mismatched buffer/scalar + // cases. + @Test + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "true", "ValueTypeReturnedAsFields", "true"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + @IR(applyIfAnd = {"ValueTypePassFieldsAsArgs", "false", "ValueTypeReturnedAsFields", "false"}, + failOn = {IRNode.FAST_LOCK, IRNode.FAST_UNLOCK}) + static void testReturnMiddleHolderAndNoEscape() { + ValueHolder holder1 = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder holder2 = createValueHolderWithSynchronizedFieldWrite(); + ValueHolder holder3 = createValueHolderWithSynchronizedFieldWrite(); + returnMiddleArgument(holder1, holder2, holder3); + } + +} From fd13c2b295a7ecc5c8747b0c93e37c8c8e025589 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Thu, 1 Oct 2026 10:30:58 +0000 Subject: [PATCH 03/98] 8393257: Parallel: Remove unused RegionData::set_live_obj_size Reviewed-by: tschatzl --- src/hotspot/share/gc/parallel/psParallelCompact.hpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp index e9ad68278ba..3eb3691c8c0 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp @@ -153,7 +153,6 @@ class ParallelCompactData } inline void set_destination_count(uint count); - inline void set_live_obj_size(size_t words); inline void set_completed(); inline bool claim_unsafe(); @@ -290,12 +289,6 @@ ParallelCompactData::RegionData::set_destination_count(uint count) _dc_and_los.store_relaxed((count << dc_shift) | live_sz); } -inline void ParallelCompactData::RegionData::set_live_obj_size(size_t words) -{ - assert(words <= los_mask, "would overflow"); - _dc_and_los.store_relaxed(destination_count_raw() | (region_sz_t)words); -} - inline void ParallelCompactData::RegionData::decrement_destination_count() { assert(dc_and_los() < dc_claimed, "already claimed"); From 2b4e5170d35488409b2e8c4bdaa339a04822d255 Mon Sep 17 00:00:00 2001 From: Yunbo Zhang Date: Thu, 1 Oct 2026 11:03:17 +0000 Subject: [PATCH 04/98] 8369663: [IR Framework] Exclude IR verification when using DTest on a @Run method with multiple tests Reviewed-by: qamai, chagedorn --- .../lib/ir_framework/test/TestVM.java | 4 +- .../ir_framework/tests/TestRunTests.java | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index b7c0a2201a5..a8d6fbcb4e9 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -771,7 +771,9 @@ private void addCustomRunTest(Method m, Run runAnno) { CustomRunTest customRunTest = new CustomRunTest(m, getAnnotation(m, Warmup.class), runAnno, tests, shouldExcludeTest); allTests.add(customRunTest); if (PRINT_VALID_IR_RULES) { - tests.forEach(test -> irMatchRulePrinter.emitApplicableIRRules(test.getTestMethod(), customRunTest.isSkipped())); + // Even though we cannot exclude a @Test method from being run when invoked from a @Run method for multiple + // tests, we can disable its IR verification. + tests.forEach(test -> irMatchRulePrinter.emitApplicableIRRules(test.getTestMethod(), shouldExcludeTest(test.getTestMethod()))); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java index 39dae4ae3ad..feae6987aef 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java @@ -64,6 +64,18 @@ public static void main(String[] args) { new TestFramework(SkipC2Compilation.class).addFlags("-XX:TieredStopAtLevel=1").start(); new TestFramework(SkipC2Compilation.class).addFlags("-XX:TieredStopAtLevel=2").start(); new TestFramework(SkipC2Compilation.class).addFlags("-XX:TieredStopAtLevel=3").start(); + new TestFramework(FilteredIRRun.class).addFlags("-DTest=test1").start(); + new TestFramework(FilteredIRRun.class).addFlags("-DExclude=test2").start(); + new TestFramework(FilteredIRRun.class).addFlags("-DTest=test1,test2", "-DExclude=test2").start(); + for (String[] flags : new String[][] { {}, { "-DTest=test2" } }) { + try { + new TestFramework(FilteredIRRun.class).addFlags(flags).start(); + Asserts.fail("Expected test2 to fail IR verification"); + } catch (IRViolationException e) { + Asserts.assertTrue(e.getExceptionInfo().contains("ir_framework.tests.FilteredIRRun::test2"), + e.getExceptionInfo()); + } + } } public int iFld; @@ -268,6 +280,32 @@ public void run2(RunInfo info) { } } +class FilteredIRRun { + int iFld; + + @Test + @IR(counts = {IRNode.STORE_I, "1"}) + public void test1(int x) { + iFld = x; + } + + @Test + @IR(failOn = IRNode.STORE_I) + public void test2(int x) { + iFld = x; + } + + @Run(test = {"test1", "test2"}) + public void run(RunInfo info) { + test1(23); + test2(42); + if (!info.isWarmUp()) { + TestFramework.assertCompiledByC2(info.getTest("test1")); + TestFramework.assertCompiledByC2(info.getTest("test2")); + } + } +} + // Run with TieredStopAt=[1,3]. IR verification is skipped. class SkipC2Compilation { From 246f461015a2584f5893b4d66eb1435c5e405a93 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 1 Oct 2026 12:16:35 +0000 Subject: [PATCH 05/98] 8392714: PhaseIdealLoop::clone_cmp_loadklass_down fails with assert(n->is_dead()) Reviewed-by: qamai, mchevalier --- src/hotspot/share/opto/phaseX.cpp | 65 ++++++++++--------- .../splitif/TestDeadAddPLoadCmpLoadKlass.java | 64 ++++++++++++++++++ 2 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/splitif/TestDeadAddPLoadCmpLoadKlass.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c0d9908d46e..c3f255ab08e 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2337,42 +2337,47 @@ void PhaseIterGVN::remove_globally_dead_node(Node* dead, NodeOrigin origin) { } bool recurse = false; // Remove from hash table - _table.hash_delete( dead ); + _table.hash_delete(dead); // Smash all inputs to 'dead', isolating him completely for (uint i = 0; i < dead->req(); i++) { - Node *in = dead->in(i); - if (in != nullptr && in != C->top()) { // Points to something? - int nrep = dead->replace_edge(in, nullptr, this); // Kill edges - assert((nrep > 0), "sanity"); - if (in->outcnt() == 0) { // Made input go dead? - stack.push(in, PROCESS_INPUTS); // Recursively remove - recurse = true; - } else if (in->outcnt() == 1 && in->has_special_unique_user()) { - add_users_to_worklist(in); - } else if (in->outcnt() <= 2 && dead->is_Phi()) { - if (in->Opcode() == Op_Region) { - _worklist.push(in); - } else if (in->is_Store()) { - DUIterator_Fast imax, i = in->fast_outs(imax); + Node* in = dead->in(i); + if (in == nullptr) { + continue; + } + int nrep = dead->replace_edge(in, nullptr, this); // Kill edges + assert((nrep > 0), "sanity"); + if (in == C->top()) { + continue; + } + if (in->outcnt() == 0) { + // Made input go dead? + stack.push(in, PROCESS_INPUTS); // Recursively remove + recurse = true; + } else if (in->outcnt() == 1 && in->has_special_unique_user()) { + add_users_to_worklist(in); + } else if (in->outcnt() <= 2 && dead->is_Phi()) { + if (in->Opcode() == Op_Region) { + _worklist.push(in); + } else if (in->is_Store()) { + DUIterator_Fast imax, i = in->fast_outs(imax); + _worklist.push(in->fast_out(i)); + i++; + if (in->outcnt() == 2) { _worklist.push(in->fast_out(i)); i++; - if (in->outcnt() == 2) { - _worklist.push(in->fast_out(i)); - i++; - } - assert(!(i < imax), "sanity"); } - } else if (in->should_process_when_disconnect_output(dead)) { - _worklist.push(in); - } - if (ReduceFieldZeroing && dead->is_Load() && i == MemNode::Memory && - in->is_Proj() && in->in(0) != nullptr && in->in(0)->is_Initialize()) { - // A Load that directly follows an InitializeNode is - // going away. The Stores that follow are candidates - // again to be captured by the InitializeNode. - add_users_to_worklist_if(_worklist, in, [](Node* n) { return n->is_Store(); }); + assert(!(i < imax), "sanity"); } - } // if (in != nullptr && in != C->top()) + } else if (in->should_process_when_disconnect_output(dead)) { + _worklist.push(in); + } + if (ReduceFieldZeroing && dead->is_Load() && i == MemNode::Memory && + in->is_Proj() && in->in(0) != nullptr && in->in(0)->is_Initialize()) { + // A Load that directly follows an InitializeNode is + // going away. The Stores that follow are candidates + // again to be captured by the InitializeNode. + add_users_to_worklist_if(_worklist, in, [](Node* n) { return n->is_Store(); }); + } } // for (uint i = 0; i < dead->req(); i++) if (recurse) { continue; diff --git a/test/hotspot/jtreg/compiler/splitif/TestDeadAddPLoadCmpLoadKlass.java b/test/hotspot/jtreg/compiler/splitif/TestDeadAddPLoadCmpLoadKlass.java new file mode 100644 index 00000000000..c0f9fd98f6f --- /dev/null +++ b/test/hotspot/jtreg/compiler/splitif/TestDeadAddPLoadCmpLoadKlass.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8392714 + * @summary AddPNode killed in PhaseIdealLoop::clone_cmp_loadklass_down() not recognized as dead + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:+StressLoopPeeling + * -XX:CompileThresholdScaling=0.01 + * -XX:CompileOnly=${test.main.class}::test ${test.main.class} + * @run main ${test.main.class} + */ + + +public class TestDeadAddPLoadCmpLoadKlass { + Object[] oArray; + int[] iArray; + + void test(int limit) { + if (limit <= 0) { + return; + } + if (oArray == null) { + oArray = new int[1][]; + iArray = new int[1]; + } + int sum = 0; + for (int i = 0; sum < limit; i++) { + oArray[i] = new int[i]; + iArray[i] = ++sum; + } + } + + public static void main(String[] args) { + TestDeadAddPLoadCmpLoadKlass value = new TestDeadAddPLoadCmpLoadKlass(); + for (int i = 0; i <= 60; i++) { + value = new TestDeadAddPLoadCmpLoadKlass(); + value.test(1); + } + for (int i = 0; i < 1000; i++) { + value.test(1); + } + } +} From 5cbf4b411ac60fbfc3796ad0e17d484ae1feba03 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 1 Oct 2026 12:30:02 +0000 Subject: [PATCH 06/98] 8392944: (fc) FileChannel scatter/gather ops don't check alignment when file open for direct I/O Reviewed-by: vyazici --- .../share/classes/sun/nio/ch/IOUtil.java | 26 +- .../directio/GatheringWriteAlignment.java | 146 +++++++++++ .../FileChannel/directio/Misaligned.java | 243 ++++++++++++++++++ 3 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 test/jdk/java/nio/channels/FileChannel/directio/GatheringWriteAlignment.java create mode 100644 test/jdk/java/nio/channels/FileChannel/directio/Misaligned.java diff --git a/src/java.base/share/classes/sun/nio/ch/IOUtil.java b/src/java.base/share/classes/sun/nio/ch/IOUtil.java index df6677ab94d..8bd7b784272 100644 --- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java +++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -190,13 +190,25 @@ static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, int lim = buf.limit(); assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); - if (directIO) + + if (directIO) { Util.checkRemainingBufferSizeAligned(rem, alignment); + if (buf instanceof DirectBuffer) { + Util.checkBufferPositionAligned(buf, pos, alignment); + } + } if (rem > 0) { long headroom = WRITEV_MAX - writevLen; - if (headroom < rem) - rem = (int)headroom; + if (headroom < rem) { + if (directIO) { + headroom -= headroom % alignment; + if (headroom == 0) { + break; + } + } + rem = (int) headroom; + } vec.setBuffer(iov_len, buf, pos, rem); @@ -404,8 +416,12 @@ static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); - if (directIO) + if (directIO) { Util.checkRemainingBufferSizeAligned(rem, alignment); + if (buf instanceof DirectBuffer) { + Util.checkBufferPositionAligned(buf, pos, alignment); + } + } if (rem > 0) { vec.setBuffer(iov_len, buf, pos, rem); diff --git a/test/jdk/java/nio/channels/FileChannel/directio/GatheringWriteAlignment.java b/test/jdk/java/nio/channels/FileChannel/directio/GatheringWriteAlignment.java new file mode 100644 index 00000000000..bfe86a048da --- /dev/null +++ b/test/jdk/java/nio/channels/FileChannel/directio/GatheringWriteAlignment.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8392944 + * @summary Test alignment of gathering writes with direct I/O near WRITEV_MAX + * @requires (os.family == "linux" | os.family == "mac") + * @requires vm.bits == 64 & os.maxMemory >= 8G + * @modules jdk.unsupported + * @run junit/othervm/timeout=480 -Xmx4G -XX:MaxDirectMemorySize=3G ${test.main.class} + */ + +import java.lang.foreign.Arena; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.channels.FileChannel; +import static java.nio.file.StandardOpenOption.*; +import static com.sun.nio.file.ExtendedOpenOption.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.*; + +class GatheringWriteAlignment { + + private static Path file; + private static int alignment; + + @BeforeAll + static void setup() throws Exception { + file = Files.createTempFile(Path.of("."), "test", null); + alignment = (int) Files.getFileStore(file).getBlockSize(); + } + + @AfterAll + static void cleanup() throws Exception { + Files.delete(file); + } + + /** + * Tests that a gathering write with direct I/O writes from only the first + * buffer when the remaining headroom is smaller than one alignment unit. + */ + @Test + void testOnlyFirstBufferFits() throws Exception { + try (FileChannel fc = FileChannel.open(file, WRITE, DIRECT); + Arena arena = Arena.ofConfined()) { + + // largest aligned length below WRITEV_MAX + int firstBufferSize = Integer.MAX_VALUE - (Integer.MAX_VALUE % alignment); + ByteBuffer firstBuffer = arena.allocate(firstBufferSize, alignment).asByteBuffer(); + + // second buffer can not fit in the remaining headroom + ByteBuffer secondBuffer = ByteBuffer.allocate(alignment); + + long written = fc.write(new ByteBuffer[]{firstBuffer, secondBuffer}); + + assertTrue(written > 0); + assertEquals(written, (long) firstBuffer.position()); + assertEquals(0, firstBuffer.position() % alignment); + assertEquals(0, secondBuffer.position()); + } + } + + /** + * Tests that a gathering write with direct I/O limits writes from the second + * buffer to alignment units when only part of it fits within the remaining + * headroom. + */ + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testOnlyPartOfSecondBufferFits(boolean direct) throws Exception { + try (FileChannel fc = FileChannel.open(file, WRITE, DIRECT); + Arena arena = Arena.ofConfined()) { + + // leave 2 * alignment - 1 bytes of headroom + int firstBufferSize = Integer.MAX_VALUE - (Integer.MAX_VALUE % alignment) - alignment; + ByteBuffer firstBuffer = arena.allocate(firstBufferSize, alignment).asByteBuffer(); + + // only one alignment unit of this buffer should fit + ByteBuffer secondBuffer = direct + ? arena.allocate(2L * alignment, alignment).asByteBuffer() + : ByteBuffer.allocate(2 * alignment); + + long written = fc.write(new ByteBuffer[]{firstBuffer, secondBuffer}); + + assertTrue(written > 0); + assertEquals(0L, written % alignment); + assertEquals(written, (long) firstBuffer.position() + secondBuffer.position()); + assertEquals((int) Math.min(written, firstBufferSize), firstBuffer.position()); + assertEquals(0, secondBuffer.position() % alignment); + assertTrue(secondBuffer.position() <= alignment); + } + } + + /** + * Tests that a gathering write with direct I/O writes from only the first two + * buffers when the remaining headroom is smaller than one alignment unit. + */ + @Test + void testOnlyFirstTwoBuffersFit() throws Exception { + try (FileChannel fc = FileChannel.open(file, WRITE, DIRECT); + Arena arena = Arena.ofConfined()) { + + // leave room for one aligned buffer, plus alignment - 1 bytes + int firstBufferSize = Integer.MAX_VALUE - (Integer.MAX_VALUE % alignment) - alignment; + ByteBuffer firstBuffer = arena.allocate(firstBufferSize, alignment).asByteBuffer(); + ByteBuffer secondBuffer = ByteBuffer.allocate(alignment); + ByteBuffer thirdBuffer = ByteBuffer.allocate(alignment); + + long written = fc.write(new ByteBuffer[]{firstBuffer, secondBuffer, thirdBuffer}); + + assertTrue(written > 0); + assertEquals(0L, written % alignment); + assertEquals(written, (long) firstBuffer.position() + secondBuffer.position()); + assertEquals((int) Math.min(written, firstBufferSize), firstBuffer.position()); + assertEquals(0, secondBuffer.position() % alignment); + assertEquals(0, thirdBuffer.position()); + } + } +} diff --git a/test/jdk/java/nio/channels/FileChannel/directio/Misaligned.java b/test/jdk/java/nio/channels/FileChannel/directio/Misaligned.java new file mode 100644 index 00000000000..25b55ee93dd --- /dev/null +++ b/test/jdk/java/nio/channels/FileChannel/directio/Misaligned.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8392944 + * @summary Test FileChannel direct I/O with misaligned file position or buffers + * @modules jdk.unsupported + * @run junit ${test.main.class} + */ + +import java.io.IOException; +import java.lang.foreign.Arena; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.channels.FileChannel; +import java.util.function.Supplier; +import java.util.stream.Stream; +import static java.nio.file.StandardOpenOption.*; +import static com.sun.nio.file.ExtendedOpenOption.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.*; + +class Misaligned { + + private static Path file; + private static int alignment; + + @BeforeAll + static void setup() throws Exception { + file = Files.createTempFile(Path.of("."), "test", null); + alignment = (int) Files.getFileStore(file).getBlockSize(); + Files.write(file, new byte[alignment * 4]); + } + + @AfterAll + static void cleanup() throws Exception { + Files.delete(file); + } + + /** + * Return a stream of suppliers for each Arena type. A supplier is used to avoid JUnit + * closing the Arena and failing (as some Arenas are not closable). + */ + static Stream> arenaSuppliers() { + return Stream.of(Arena::global, Arena::ofAuto, Arena::ofConfined, Arena::ofShared); + } + + /** + * Test exception thrown when file position is misaligned. + */ + private void testMisalignedFilePosition(ByteBuffer bb) throws Exception { + // check buffer is not empty and is suitably aligned + assertTrue(bb.hasRemaining()); + assertEquals(0, bb.remaining() % alignment); + if (bb.isDirect()) { + assertEquals(0, bb.alignmentOffset(bb.position(), alignment)); + assertEquals(0, bb.alignmentOffset(bb.limit(), alignment)); + } + + try (FileChannel fc = FileChannel.open(file, READ, WRITE, DIRECT)) { + fc.position(1L); // misaligned file position + assertThrows(IOException.class, () -> fc.read(bb)); + assertThrows(IOException.class, () -> fc.write(bb)); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.read(bb, 1L)); + assertThrows(IOException.class, () -> fc.write(bb, 1L)); + } + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testMisalignedFilePosition(boolean direct) throws Exception { + ByteBuffer bb = direct + ? ByteBuffer.allocateDirect(3 * alignment - 1).alignedSlice(alignment) + : ByteBuffer.allocate(3 * alignment); + testMisalignedFilePosition(bb); + } + + @ParameterizedTest + @MethodSource("arenaSuppliers") + void testMisalignedFilePosition(Supplier arenaSupplier) throws Exception { + Arena arena = arenaSupplier.get(); + try { + ByteBuffer bb = arena.allocate(3L * alignment, alignment).asByteBuffer(); + testMisalignedFilePosition(bb); + } finally { + tryClose(arena); + } + } + + /** + * Test exception thrown when the buffer address is misaligned. + */ + private void testMisalignedAddress(ByteBuffer bb) throws Exception { + // check buffer is direct with an aligned base address + assertTrue(bb.isDirect()); + assertEquals(0, bb.alignmentOffset(0, alignment)); + + // need sufficient capacity for an aligned region starting at position 1 + assertTrue(bb.capacity() >= alignment + 1); + + // another aligned buffer to use for scatter/gather ops + ByteBuffer alignedBuffer = ByteBuffer.allocateDirect(2 * alignment - 1) + .alignedSlice(alignment); + + try (FileChannel fc = FileChannel.open(file, READ, WRITE, DIRECT)) { + bb.position(1); // misaligned address + bb.limit(alignment + 1); + + assertNotEquals(0, bb.alignmentOffset(bb.position(), alignment)); + assertEquals(0, bb.remaining() % alignment); + + assertThrows(IOException.class, () -> fc.read(bb)); + assertThrows(IOException.class, () -> fc.write(bb)); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.read(bb, 0L)); + assertThrows(IOException.class, () -> fc.write(bb, 0L)); + + + } + } + + @Test + void testMisalignedAddress() throws Exception { + ByteBuffer bb = ByteBuffer.allocateDirect(3 * alignment - 1) + .alignedSlice(alignment); + testMisalignedAddress(bb); + } + + @ParameterizedTest + @MethodSource("arenaSuppliers") + void testMisalignedAddress(Supplier arenaSupplier) throws Exception { + Arena arena = arenaSupplier.get(); + try { + ByteBuffer bb = arena.allocate(3L * alignment, alignment).asByteBuffer(); + testMisalignedAddress(bb); + } finally { + tryClose(arena); + } + } + + /** + * Test exception thrown when the remaining length is not a multiple of the alignment. + */ + private void testMisalignedRemaining(ByteBuffer bb) throws Exception { + assertEquals(0, bb.position()); + assertTrue(bb.capacity() >= alignment + 1); + if (bb.isDirect()) { + assertEquals(0, bb.alignmentOffset(0, alignment)); + } + + // another aligned buffer to use for scatter/gather ops + ByteBuffer alignedBuffer = ByteBuffer.allocateDirect(2 * alignment - 1) + .alignedSlice(alignment); + + try (FileChannel fc = FileChannel.open(file, READ, WRITE, DIRECT)) { + bb.limit(alignment - 1); // remaining is 1 byte below an alignment + assertThrows(IOException.class, () -> fc.read(bb)); + assertThrows(IOException.class, () -> fc.write(bb)); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.read(bb, 0L)); + assertThrows(IOException.class, () -> fc.write(bb, 0L)); + + bb.limit(alignment + 1); // remaining is 1 byte beyond an alignment + assertThrows(IOException.class, () -> fc.read(bb)); + assertThrows(IOException.class, () -> fc.write(bb)); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{bb})); + assertThrows(IOException.class, () -> fc.read(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.write(new ByteBuffer[]{alignedBuffer, bb})); + assertThrows(IOException.class, () -> fc.read(bb, 0L)); + assertThrows(IOException.class, () -> fc.write(bb, 0L)); + } + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testMisalignedRemaining(boolean direct) throws Exception { + ByteBuffer bb = direct + ? ByteBuffer.allocateDirect(3 * alignment - 1).alignedSlice(alignment) + : ByteBuffer.allocate(3 * alignment); + testMisalignedRemaining(bb); + } + + @ParameterizedTest + @MethodSource("arenaSuppliers") + void testMisalignedRemaining(Supplier arenaSupplier) throws Exception { + Arena arena = arenaSupplier.get(); + try { + ByteBuffer bb = arena.allocate(3L * alignment, alignment).asByteBuffer(); + testMisalignedRemaining(bb); + } finally { + tryClose(arena); + } + } + + /** + * Attempt to close the given Arena. + */ + private static boolean tryClose(Arena arena) { + try { + arena.close(); + return true; + } catch (UnsupportedOperationException e) { + return false; + } + } +} From d95e3773e228943ee412867134d666cce3663e24 Mon Sep 17 00:00:00 2001 From: Matthew Donovan Date: Thu, 1 Oct 2026 13:27:32 +0000 Subject: [PATCH 07/98] 8387730: Test javax/crypto/KDF/KDFDelayedProviderThreadingTest.java timed out Reviewed-by: weijun --- .../KDF/KDFDelayedProviderThreadingTest.java | 68 +++++++++++++------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/test/jdk/javax/crypto/KDF/KDFDelayedProviderThreadingTest.java b/test/jdk/javax/crypto/KDF/KDFDelayedProviderThreadingTest.java index 8d7ad058c39..65c9bbb50aa 100644 --- a/test/jdk/javax/crypto/KDF/KDFDelayedProviderThreadingTest.java +++ b/test/jdk/javax/crypto/KDF/KDFDelayedProviderThreadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,10 @@ import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + + public class KDFDelayedProviderThreadingTest { /// This number of iterations is enough to see a case where the threads /// arrange themselves such that both `deriveData` attempts cause "ERROR", @@ -54,6 +58,21 @@ public class KDFDelayedProviderThreadingTest { static final HKDFParameterSpec input = HKDFParameterSpec.ofExtract().extractOnly(); + private static final CyclicBarrier START = new CyclicBarrier(3); + private static final CyclicBarrier FINISH = new CyclicBarrier(3); + private static volatile KDF kdf; + + static void wait(CyclicBarrier cb) { + try { + cb.await(); + } catch (InterruptedException exc) { + Thread.currentThread().interrupt(); + throw new RuntimeException(exc); + } catch (BrokenBarrierException exc) { + throw new RuntimeException(exc); + } + } + static String derive(KDF kdf) { try { return Arrays.toString(kdf.deriveData(input)); @@ -62,10 +81,37 @@ static String derive(KDF kdf) { } } - public static void main(String[] args) throws Exception { + static void main(String[] args) throws Exception { + + var t1 = new Thread(() -> { + for (int i = 0 ; i < ITERATIONS ; ++i) { + wait(START); + out = derive(kdf); + wait(FINISH); + } + }, "KDF-derive"); + + var t2 = new Thread(() -> { + for (int i = 0 ; i < ITERATIONS ; ++i) { + wait(START); + kdf.getProviderName(); + wait(FINISH); + } + }, "KDF-provider-name"); + t1.start(); + t2.start(); + Security.insertProviderAt(new P(), 1); for (int i = 0; i < ITERATIONS; i++) { - test(); + kdf = KDF.getInstance("HKDF-SHA256"); + wait(START); + wait(FINISH); + + String out2 = derive(kdf); + Asserts.assertEquals(out, out2); + if (out.length() < 10) { // "error" + threadOrderReversalCounter++; + } } // If the value of threadOrderReversalCounter is consistently zero, @@ -79,22 +125,6 @@ public static void main(String[] args) throws Exception { + " iterations (depends on hardware specs/utilization)."); } - static void test() throws Exception { - var k = KDF.getInstance("HKDF-SHA256"); - var t1 = new Thread(() -> out = derive(k)); - var t2 = new Thread(() -> k.getProviderName()); - t1.start(); - t2.start(); - t1.join(); - t2.join(); - - String out2 = derive(k); - Asserts.assertEquals(out, out2); - if (out.length() < 10) { // "error" - threadOrderReversalCounter++; - } - } - public static class P extends Provider { public P() { super("ME", "1", "ME"); From 46fbea9c4b9b628e43dd8474bde2bd8e984bf857 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Thu, 1 Oct 2026 13:44:51 +0000 Subject: [PATCH 08/98] 8393382: [VectorAPI] AbstractMask.intoArray should be force inlined Reviewed-by: qamai --- .../share/classes/jdk/incubator/vector/AbstractMask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java index 9fac32da26d..256c0668178 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java @@ -72,6 +72,7 @@ public final VectorSpecies vectorSpecies() { } @Override + @ForceInline public void intoArray(boolean[] bits, int i) { AbstractSpecies vsp = (AbstractSpecies) vectorSpecies(); int laneCount = vsp.laneCount(); From 5fa098bf826a709b761486d69f98faf99b7f5f62 Mon Sep 17 00:00:00 2001 From: john spurling Date: Thu, 1 Oct 2026 15:46:56 +0000 Subject: [PATCH 09/98] 8392088: Shenandoah: ShenandoahNMethodLock footprint optimization Reviewed-by: shade, kdnilsen --- .../share/gc/shenandoah/shenandoahLock.cpp | 61 ++++--------------- .../share/gc/shenandoah/shenandoahLock.hpp | 49 ++++++++------- .../share/gc/shenandoah/shenandoahNMethod.hpp | 5 +- .../share/gc/shenandoah/shenandoahUnload.cpp | 3 +- 4 files changed, 43 insertions(+), 75 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp index 7e317f53424..340b28faa4b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp @@ -89,61 +89,24 @@ void ShenandoahLock::yield_or_sleep(int &yields) { } } -ShenandoahSimpleLock::ShenandoahSimpleLock() { +ShenandoahSimpleLock::ShenandoahSimpleLock() : _owner(nullptr) { assert(os::mutex_init_done(), "Too early!"); } -void ShenandoahSimpleLock::lock(bool allow_block_for_safepoint) { - _lock.lock(); -} - -void ShenandoahSimpleLock::unlock() { - _lock.unlock(); -} - -template -ShenandoahReentrantLock::ShenandoahReentrantLock() : - Lock(), _owner(nullptr), _count(0) { -} - -template -ShenandoahReentrantLock::~ShenandoahReentrantLock() { - assert(_count == 0, "Unbalance"); -} - -template -void ShenandoahReentrantLock::lock(bool allow_block_for_safepoint) { - Thread* const thread = Thread::current(); - Thread* const owner = _owner.load_relaxed(); - - if (owner != thread) { - Lock::lock(allow_block_for_safepoint); - _owner.store_relaxed(thread); +bool ShenandoahSimpleLock::lock() { + if (_owner.load_relaxed() == Thread::current()) { + return false; } - _count++; -} - -template -void ShenandoahReentrantLock::unlock() { - assert(owned_by_self(), "Invalid owner"); - assert(_count > 0, "Invalid count"); - - _count--; + _lock.lock(); + assert(_owner.load_relaxed() == nullptr, "No owner yet."); + _owner.store_relaxed(Thread::current()); - if (_count == 0) { - _owner.store_relaxed((Thread*)nullptr); - Lock::unlock(); - } + return true; } -template -bool ShenandoahReentrantLock::owned_by_self() const { - Thread* const thread = Thread::current(); - Thread* const owner = _owner.load_relaxed(); - return owner == thread; +void ShenandoahSimpleLock::unlock() { + assert(_owner.load_relaxed() == Thread::current(), "Lock must be held by current thread."); + _owner.store_relaxed(nullptr); + _lock.unlock(); } - -// Explicit template instantiation -template class ShenandoahReentrantLock; -template class ShenandoahReentrantLock; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp index 5041419b2c7..e342e1474d9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.hpp @@ -87,32 +87,18 @@ class ShenandoahLock { } }; -// Simple lock using PlatformMonitor +// Simple lock using PlatformMutex class ShenandoahSimpleLock { private: - PlatformMonitor _lock; // native lock + PlatformMutex _lock; // native lock + Atomic _owner; public: ShenandoahSimpleLock(); - void lock(bool allow_block_for_safepoint = false); + bool lock(); void unlock(); -}; - -// templated reentrant lock -template -class ShenandoahReentrantLock : public Lock { -private: - Atomic _owner; - uint64_t _count; - -public: - ShenandoahReentrantLock(); - ~ShenandoahReentrantLock(); - - void lock(bool allow_block_for_safepoint = false); - void unlock(); - - // If the lock already owned by this thread - bool owned_by_self() const ; + bool owned_by_self() const { + return _owner.load_relaxed() == Thread::current(); + } }; // template based ShenandoahLocker @@ -130,4 +116,25 @@ class ShenandoahLocker : public StackObj { } }; +template +class ShenandoahReentrantLocker : public StackObj { +private: + Lock* _lock; + +public: + ShenandoahReentrantLocker(Lock* lock) : _lock(nullptr) { + if (lock->lock()) { + _lock = lock; + } + } + + ~ShenandoahReentrantLocker() { + if (_lock != nullptr) { + _lock->unlock(); + _lock = nullptr; + } + } +}; + + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHLOCK_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 04c6bf0a804..280067f63f5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -35,9 +35,8 @@ class BarrierSetNMethod; -// Use ShenandoahReentrantLock as ShenandoahNMethodLock -typedef ShenandoahReentrantLock ShenandoahNMethodLock; -typedef ShenandoahLocker ShenandoahNMethodLocker; +typedef ShenandoahSimpleLock ShenandoahNMethodLock; +typedef ShenandoahReentrantLocker ShenandoahNMethodLocker; struct ShenandoahPatchableJump { int32_t _rel_pc; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index 962935b5799..04c2d1e6b3e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -92,8 +92,7 @@ class ShenandoahCompiledICProtectionBehaviour : public CompiledICProtectionBehav virtual bool lock(nmethod* nm) { ShenandoahNMethodLock* const lock = ShenandoahNMethod::ic_lock_for_nmethod(nm); assert(lock != nullptr, "Not yet registered?"); - lock->lock(); - return true; + return lock->lock(); } virtual void unlock(nmethod* nm) { From b6b2cbe2dd75251889836d0fab60f7033e3619cd Mon Sep 17 00:00:00 2001 From: Joe Wang Date: Thu, 1 Oct 2026 15:50:00 +0000 Subject: [PATCH 10/98] 8357394: Add JDK specific property for external resource access Reviewed-by: lancea, alanb --- .../xalan/internal/xsltc/compiler/Import.java | 10 +- .../internal/xsltc/compiler/Include.java | 10 +- .../xalan/internal/xsltc/compiler/Parser.java | 35 +- .../xsltc/compiler/util/ErrorMessages.java | 6 +- .../internal/xsltc/dom/LoadDocument.java | 11 +- .../xsltc/runtime/AbstractTranslet.java | 40 +- .../internal/xsltc/trax/TemplatesImpl.java | 8 +- .../xsltc/trax/TransformerFactoryImpl.java | 10 +- .../internal/xsltc/trax/TransformerImpl.java | 26 +- .../impl/XMLDocumentFragmentScannerImpl.java | 3 +- .../internal/impl/XMLEntityManager.java | 21 +- .../internal/impl/msg/XMLMessages.properties | 5 +- .../impl/msg/XMLSchemaMessages.properties | 2 +- .../internal/impl/xs/XMLSchemaLoader.java | 21 +- .../impl/xs/traversers/XSDHandler.java | 7 +- .../internal/jaxp/DocumentBuilderImpl.java | 6 +- .../xerces/internal/jaxp/SAXParserImpl.java | 9 +- .../jaxp/validation/XMLSchemaFactory.java | 8 +- .../XMLSchemaValidatorComponentManager.java | 9 +- .../xerces/internal/parsers/DOMParser.java | 6 +- .../xerces/internal/parsers/SAXParser.java | 6 +- .../internal/xinclude/XIncludeHandler.java | 22 +- .../classes/jdk/xml/internal/AccessRule.java | 522 ++++++++++++++++++ .../jdk/xml/internal/FeaturePropertyBase.java | 15 +- .../jdk/xml/internal/JdkConstants.java | 13 +- .../classes/jdk/xml/internal/JdkXmlUtils.java | 4 +- .../jdk/xml/internal/SecuritySupport.java | 115 ++-- .../share/classes/jdk/xml/internal/Utils.java | 52 +- .../jdk/xml/internal/XMLSecurityManager.java | 93 +++- .../internal/XMLSecurityPropertyManager.java | 12 +- src/java.xml/share/classes/module-info.java | 194 ++++++- src/java.xml/share/conf/jaxp.properties | 75 +++ .../common/access/AccessRuleTest.java | 200 +++++++ .../common/access/AccessTestBase.java | 447 +++++++++++++++ .../jaxp/unittest/common/access/DOMTest.java | 65 +++ .../jaxp/unittest/common/access/SAXTest.java | 67 +++ .../unittest/common/access/SchemaTest.java | 83 +++ .../jaxp/unittest/common/access/StAXTest.java | 58 ++ .../unittest/common/access/TransformTest.java | 68 +++ .../unittest/common/access/XIncludeTest.java | 65 +++ .../catalog/testcatalog/TestCatalog.xml | 5 +- .../jaxp/unittest/common/dtd/DTDTestBase.java | 6 +- .../jaxp/unittest/common/util/TestBase.java | 124 ++++- .../unittest/common/xmlfiles/XI_simple.xml | 2 + 44 files changed, 2336 insertions(+), 230 deletions(-) create mode 100644 src/java.xml/share/classes/jdk/xml/internal/AccessRule.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java create mode 100644 test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java index 098d3c8a260..3c8e1edd5b8 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Import.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -33,6 +33,7 @@ import javax.xml.XMLConstants; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -41,7 +42,7 @@ * @author Morten Jorgensen * @author Erwin Bolwidt * @author Gunnlaugur Briem - * @LastModified: May 2021 + * @LastModified: July 2026 */ final class Import extends TopLevelElement { @@ -84,8 +85,9 @@ public void parseContents(final Parser parser) { if (input == null) { docToLoad = SystemIDResolver.getAbsoluteURI(docToLoad, currLoadedDoc); String accessError = SecuritySupport.checkAccess(docToLoad, - (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); + (XMLSecurityManager)xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); if (accessError != null) { final ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java index ecb03df9f1c..8e3b5530320 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Include.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -33,6 +33,7 @@ import javax.xml.XMLConstants; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -41,7 +42,7 @@ * @author Morten Jorgensen * @author Erwin Bolwidt * @author Gunnlaugur Briem - * @LastModified: May 2021 + * @LastModified: July 2026 */ final class Include extends TopLevelElement { @@ -84,8 +85,9 @@ public void parseContents(final Parser parser) { if (input == null) { docToLoad = SystemIDResolver.getAbsoluteURI(docToLoad, currLoadedDoc); String accessError = SecuritySupport.checkAccess(docToLoad, - (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); + (XMLSecurityManager)xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); if (accessError != null) { final ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java index 0dde14f455c..f523b65a2b4 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -60,7 +60,7 @@ * @author G. Todd Miller * @author Morten Jorgensen * @author Erwin Bolwidt - * @LastModified: July 2023 + * @LastModified: July 2026 */ public class Parser implements Constants, ContentHandler { @@ -540,23 +540,20 @@ private SyntaxTreeNode getStylesheet(SyntaxTreeNode root) return(element); } else { - try { - String path = _target; - if (path.indexOf(":")==-1) { - path = "file:" + path; - } - path = SystemIDResolver.getAbsoluteURI(path); - String accessError = SecuritySupport.checkAccess(path, - (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET), - JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(_target), accessError, - root); - throw new CompilerException(msg.toString()); - } - } catch (IOException ex) { - throw new CompilerException(ex); + String path = _target; + if (path.indexOf(":")==-1) { + path = "file:" + path; + } + path = SystemIDResolver.getAbsoluteURI(path); + String accessError = SecuritySupport.checkAccess(path, + (XMLSecurityManager)_xsltc.getProperty(JdkConstants.SECURITY_MANAGER), + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + (String)_xsltc.getProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(_target), accessError, + root); + throw new CompilerException(msg.toString()); } return(loadExternalStylesheet(_target)); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java index 660d1194b30..a3875969bb7 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,7 +24,7 @@ /** * @author Morten Jorgensen - * @LastModified: Dec 2024 + * @LastModified: July 2026 */ public class ErrorMessages extends ListResourceBundle { @@ -469,7 +469,7 @@ public Object[][] getContents() * Note to translators: access to the stylesheet target is denied */ {ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - "Could not read stylesheet target ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalStylesheet property."}, + "Could not read stylesheet target ''{0}'', because access is not allowed due to restriction set by ''{1}''."}, /* * Note to translators: This message represents an internal error in diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java index 39084c87f0b..e17407e2831 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/dom/LoadDocument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -39,7 +39,7 @@ /** * @author Morten Jorgensen - * @LastModified: Sept 2021 + * @LastModified: July 2026 */ public final class LoadDocument { @@ -197,12 +197,7 @@ private static DTMAxisIterator document(String uri, String base, throw new TransletException(e); } } else { - String accessError = SecuritySupport.checkAccess(uri, translet.getAllowedProtocols(), JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(uri), accessError); - throw new Exception(msg.toString()); - } + translet.verifyAccess(uri); // Parse the input document and construct DOM object // Trust the DTMManager to pick the right parser and diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java index 1612fd61923..620eb670547 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/runtime/AbstractTranslet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,6 +25,7 @@ import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM; import com.sun.org.apache.xalan.internal.xsltc.Translet; import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter; import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex; import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory; @@ -40,11 +41,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Templates; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.SecuritySupport; +import jdk.xml.internal.XMLSecurityManager; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; @@ -54,7 +58,7 @@ * @author Morten Jorgensen * @author G. Todd Miller * @author John Howard, JohnH@schemasoft.com - * @LastModified: Sept 2021 + * @LastModified: July 2026 */ public abstract class AbstractTranslet implements Translet { @@ -115,6 +119,7 @@ public abstract class AbstractTranslet implements Translet { * protocols allowed for external references set by the stylesheet processing instruction, Document() function, Import and Include element. */ private String _accessExternalStylesheet = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private XMLSecurityManager _xsm; // The error message when access to exteranl resources is rejected private String _accessErr = null; @@ -789,6 +794,37 @@ public void setAllowedProtocols(String protocols) { _accessExternalStylesheet = protocols; } + /** + * Returns the XMLSecurityManager + */ + public XMLSecurityManager getXMLSecurityManager() { + return _xsm; + } + + /** + * Sets the XMLSecurityManager + * @param xsm the XMLSecurityManager instance + */ + public void setXMLSecurityManager(XMLSecurityManager xsm) { + _xsm = xsm; + } + + /** + * Verifies that access to the resource represented by the systemId is permitted. + * @param systemId the systemId + + * @throws TransletException if access is not permitted + */ + public void verifyAccess(String systemId) throws TransletException { + String accessError = SecuritySupport.checkAccess(systemId, _xsm, + XMLConstants.ACCESS_EXTERNAL_STYLESHEET, _accessExternalStylesheet); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(systemId), accessError); + throw new TransletException(msg.toString()); + } + } + /** * Returns the access error. */ diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java index 41b83bce5df..71aad15b09f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -58,6 +58,7 @@ import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.URIResolver; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.XMLSecurityManager; /** @@ -65,7 +66,7 @@ * @author G. Todd Millerj * @author Jochen Cordes * @author Santiago Pericas-Geertsen - * @LastModified: Jan 2025 + * @LastModified: July 2026 */ public final class TemplatesImpl implements Templates, Serializable { static final long serialVersionUID = 673094361519270707L; @@ -147,6 +148,7 @@ public final class TemplatesImpl implements Templates, Serializable { * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element. */ private transient String _accessExternalStylesheet = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private transient XMLSecurityManager _xsm; /** * @serialField _name String The Name of the main class @@ -240,6 +242,7 @@ private void init(String transletName, _tfactory = tfactory; _overrideDefaultParser = tfactory.overrideDefaultParser(); _accessExternalStylesheet = (String) tfactory.getAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET); + _xsm = (XMLSecurityManager) tfactory.getAttribute(JdkConstants.SECURITY_MANAGER); } /** * Need for de-serialization, see readObject(). @@ -540,6 +543,7 @@ private Translet getTransletInstance() translet.setTemplates(this); translet.setOverrideDefaultParser(_overrideDefaultParser); translet.setAllowedProtocols(_accessExternalStylesheet); + translet.setXMLSecurityManager(_xsm); if (_auxClasses != null) { translet.setAuxiliaryClasses(_auxClasses); } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java index ca56223aa44..14bb3d0df82 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -88,7 +88,7 @@ * @author G. Todd Miller * @author Morten Jorgensen * @author Santiago Pericas-Geertsen - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class TransformerFactoryImpl extends SAXTransformerFactory implements SourceLoader @@ -515,7 +515,7 @@ else if (ImplPropMap.EXTCLSLOADER.is(name)) { } if (_xmlSecurityPropertyMgr != null && - _xmlSecurityPropertyMgr.setValue(name, FeaturePropertyBase.State.APIPROPERTY, value)) { + _xmlSecurityPropertyMgr.setValue(name, JdkProperty.State.APIPROPERTY, value)) { _accessExternalDTD = _xmlSecurityPropertyMgr.getValue( Property.ACCESS_EXTERNAL_DTD); _accessExternalStylesheet = _xmlSecurityPropertyMgr.getValue( @@ -572,9 +572,9 @@ else if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { // set external access restriction when FSP is explicitly set if (value) { _xmlSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); _xmlSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_STYLESHEET, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); _accessExternalDTD = _xmlSecurityPropertyMgr.getValue( Property.ACCESS_EXTERNAL_DTD); _accessExternalStylesheet = _xmlSecurityPropertyMgr.getValue( diff --git a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java index b48b9d2a657..89091355484 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -100,7 +100,7 @@ * @author Morten Jorgensen * @author G. Todd Miller * @author Santiago Pericas-Geertsen - * @LastModified: Jan 2025 + * @LastModified: July 2026 */ public final class TransformerImpl extends Transformer implements DOMCache @@ -1357,21 +1357,13 @@ public DOM retrieveDocument(String baseURI, String href, Translet translet) { */ AbstractTranslet t = (AbstractTranslet)translet; String systemId = SystemIDResolver.getAbsoluteURI(href, baseURI); - String errMsg = null; - try { - String accessError = SecuritySupport.checkAccess(systemId, - t.getAllowedProtocols(), - JdkConstants.ACCESS_EXTERNAL_ALL); - if (accessError != null) { - ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, - SecuritySupport.sanitizePath(href), accessError); - errMsg = msg.toString(); - } - } catch (IOException ioe) { - errMsg = ioe.getMessage(); - } - if (errMsg != null) { - t.setAccessError(errMsg); + String accessError = SecuritySupport.checkAccess(systemId, + t.getXMLSecurityManager(), XMLConstants.ACCESS_EXTERNAL_STYLESHEET, + t.getAllowedProtocols()); + if (accessError != null) { + ErrorMsg msg = new ErrorMsg(ErrorMsg.ACCESSING_XSLT_TARGET_ERR, + SecuritySupport.sanitizePath(href), accessError); + t.setAccessError(msg.toString()); return null; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java index ccaa9f6c5dd..e9cfd0ff388 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java @@ -2023,7 +2023,8 @@ public String getDriverName(Driver driver) { String checkAccess(String systemId, String allowedProtocols) throws IOException { String baseSystemId = fEntityScanner.getBaseSystemId(); String expandedSystemId = XMLEntityManager.expandSystemId(systemId, baseSystemId, fStrictURI); - return SecuritySupport.checkAccess(expandedSystemId, allowedProtocols, JdkConstants.ACCESS_EXTERNAL_ALL); + return SecuritySupport.checkAccess(expandedSystemId, fSecurityManager, + XMLConstants.ACCESS_EXTERNAL_DTD, allowedProtocols); } // diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java index 99b4193b39c..db598c6c356 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -93,7 +93,7 @@ * @author K.Venugopal SUN Microsystems * @author Neeraj Bajaj SUN Microsystems * @author Sunitha Reddy SUN Microsystems - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class XMLEntityManager implements XMLComponent, XMLEntityResolver { @@ -1361,20 +1361,15 @@ public void startEntity(boolean isGE, String entityName, boolean literal) if (external) { staxInputSource = resolveEntityAsPerStax(externalEntity.entityLocation); - /** xxx: Waiting from the EG - * //simply return if there was entity resolver registered and application - * //returns either XMLStreamReader or XMLEventReader. - * if(staxInputSource.hasXMLStreamOrXMLEventReader()) return ; - */ xmlInputSource = staxInputSource.getXMLInputSource() ; if (!fISCreatedByResolver) { - String accessError = SecuritySupport.checkAccess(expandedSystemId, - fAccessExternalDTD, JdkConstants.ACCESS_EXTERNAL_ALL); + String accessError = SecuritySupport.checkAccess(expandedSystemId, fSecurityManager, + XMLConstants.ACCESS_EXTERNAL_DTD, fAccessExternalDTD); if (accessError != null) { - fErrorReporter.reportError(this.getEntityScanner(),XMLMessageFormatter.XML_DOMAIN, - "AccessExternalEntity", - new Object[] { SecuritySupport.sanitizePath(expandedSystemId), accessError }, - XMLErrorReporter.SEVERITY_FATAL_ERROR); + fErrorReporter.reportError(this.getEntityScanner(), XMLMessageFormatter.XML_DOMAIN, + "AccessExternalEntity", + new Object[]{SecuritySupport.sanitizePath(expandedSystemId), accessError}, + XMLErrorReporter.SEVERITY_FATAL_ERROR); } } } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties index 25182a06a3c..b51e912370a 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties @@ -283,8 +283,9 @@ # Entity related messages # 3.1 Start-Tags, End-Tags, and Empty-Element Tags ReferenceToExternalEntity = The external entity reference \"&{0};\" is not permitted in an attribute value. - AccessExternalDTD = External DTD: Failed to read external DTD ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalDTD property. - AccessExternalEntity = External Entity: Failed to read external document ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalDTD property. + AccessExternalDTD = External DTD: Failed to read external DTD ''{0}'', because access is not allowed due to restriction set by ''{1}''. + AccessExternalEntity = External Entity: Failed to read external document ''{0}'', because access is not allowed due to restriction set by ''{1}''. + ResourceAccess = Resource Access: Failed to read external document ''{0}'', because the ''{1}'' property is set to not allow accessing the document. # 4.1 Character and Entity References EntityNotDeclared = The entity \"{0}\" was referenced, but not declared. diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties index 4f15b428c8b..2004e41e51d 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties @@ -108,7 +108,7 @@ #schema valid (3.X.3) - schema_reference.access = schema_reference: Failed to read schema document ''{0}'', because ''{1}'' access is not allowed due to restriction set by the accessExternalSchema property. + schema_reference.access = schema_reference: Failed to read schema document ''{0}'', because access is not allowed due to restriction set by ''{1}''. schema_reference.4 = schema_reference.4: Failed to read schema document ''{0}'', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not . src-annotation = src-annotation: elements can only contain and elements, but ''{0}'' was found. src-attribute.1 = src-attribute.1: The properties ''default'' and ''fixed'' cannot both be present in attribute declaration ''{0}''. Use only one of them. diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java index b4b76546d6d..c884eec6f9f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -103,7 +103,7 @@ * @xerces.internal * * @author Neil Graham, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class XMLSchemaLoader implements XMLGrammarLoader, XMLComponent, XSElementDeclHelper, @@ -285,6 +285,7 @@ public class XMLSchemaLoader implements XMLGrammarLoader, XMLComponent, XSElemen private CMBuilder fCMBuilder; private XSDDescription fXSDDescription = new XSDDescription(); private String faccessExternalSchema = JdkConstants.EXTERNAL_ACCESS_DEFAULT; + private XMLSecurityManager fXMLScurityManager = null; private WeakHashMap fJAXPCache; private Locale fLocale = Locale.getDefault(); @@ -476,6 +477,9 @@ else if (propertyId.equals(XML_SECURITY_PROPERTY_MANAGER)) { XMLSecurityPropertyManager spm = (XMLSecurityPropertyManager)state; faccessExternalSchema = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA); } + else if (propertyId.equals(SECURITY_MANAGER)) { + fXMLScurityManager = (XMLSecurityManager)state; + } } // setProperty(String, Object) /** @@ -567,6 +571,7 @@ public Grammar loadGrammar(XMLInputSource source) desc.fContextType = XSDDescription.CONTEXT_PREPARSE; desc.setBaseSystemId(source.getBaseSystemId()); desc.setLiteralSystemId( source.getSystemId()); + fXSDDescription = desc; // none of the other fields make sense for preparsing Map locationPairs = new HashMap<>(); // Process external schema location properties. @@ -607,8 +612,9 @@ SchemaGrammar loadSchema(XSDDescription desc, XMLInputSource source, processJAXPSchemaSource(locationPairs); } - if (desc.isExternal() && !source.isCreatedByResolver()) { - String accessError = SecuritySupport.checkAccess(desc.getExpandedSystemId(), faccessExternalSchema, JdkConstants.ACCESS_EXTERNAL_ALL); + if (fXSDDescription.isExternal() && !source.isCreatedByResolver()) { + String accessError = SecuritySupport.checkAccess(desc.getExpandedSystemId(), + fXMLScurityManager, XMLConstants.ACCESS_EXTERNAL_SCHEMA, faccessExternalSchema); if (accessError != null) { throw new XNIException(fErrorReporter.reportError(XSMessageFormatter.SCHEMA_DOMAIN, "schema_reference.access", @@ -1006,9 +1012,10 @@ public void reset(XMLComponentManager componentManager) throws XMLConfigurationE setProperty(XML_SECURITY_PROPERTY_MANAGER, spm); } - XMLSecurityManager sm = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER); - if (sm == null) - setProperty(SECURITY_MANAGER, JdkXmlConfig.getInstance(false).getXMLSecurityManager(false)); + fXMLScurityManager = (XMLSecurityManager)componentManager.getProperty(SECURITY_MANAGER); + if (fXMLScurityManager == null) + fXMLScurityManager = JdkXmlConfig.getInstance(false).getXMLSecurityManager(false); + setProperty(SECURITY_MANAGER, fXMLScurityManager); faccessExternalSchema = spm.getValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java index 13973615adb..50660b9fbcf 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/traversers/XSDHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -132,7 +132,7 @@ * @author Neil Graham, IBM * @author Pavani Mukthipudi, Sun Microsystems * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class XSDHandler { @@ -2216,7 +2216,8 @@ private Element getSchemaDocument(String schemaNamespace, XMLInputSource schemaS if ((!schemaSource.isCreatedByResolver()) && (referType == XSDDescription.CONTEXT_IMPORT || referType == XSDDescription.CONTEXT_INCLUDE || referType == XSDDescription.CONTEXT_REDEFINE)) { - String accessError = SecuritySupport.checkAccess(schemaId, fAccessExternalSchema, JdkConstants.ACCESS_EXTERNAL_ALL); + String accessError = SecuritySupport.checkAccess(schemaId, + fSecurityManager, XMLConstants.ACCESS_EXTERNAL_SCHEMA, fAccessExternalSchema); if (accessError != null) { reportSchemaFatalError("schema_reference.access", new Object[] { SecuritySupport.sanitizePath(schemaId), accessError }, diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java index c885cb58cbc..8f96e955880 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/DocumentBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -41,7 +41,7 @@ import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource; import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration; -import jdk.xml.internal.FeaturePropertyBase.State; +import jdk.xml.internal.JdkProperty.State; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -59,7 +59,7 @@ /** * @author Rajiv Mordani * @author Edwin Goei - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class DocumentBuilderImpl extends DocumentBuilder implements JAXPConstants diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java index 9e729c9a9a2..cc61b41648b 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/SAXParserImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -43,6 +43,7 @@ import javax.xml.validation.Schema; import jdk.xml.internal.FeaturePropertyBase; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.JdkProperty; import jdk.xml.internal.JdkXmlConfig; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -65,7 +66,7 @@ * @author Rajiv Mordani * @author Edwin Goei * - * @LastModified: May 2025 + * @LastModified: July 2026 */ @SuppressWarnings("deprecation") public class SAXParserImpl extends javax.xml.parsers.SAXParser @@ -171,9 +172,9 @@ public class SAXParserImpl extends javax.xml.parsers.SAXParser Boolean temp = features.get(XMLConstants.FEATURE_SECURE_PROCESSING); if (temp != null && temp) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); } } } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java index fff15165ae8..58ff7f52e45 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -71,7 +71,7 @@ * * @author Kohsuke Kawaguchi * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public final class XMLSchemaFactory extends SchemaFactory { @@ -446,9 +446,9 @@ public void setFeature(String name, boolean value) fSecurityManager.setSecureProcessing(value); if (value) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); } fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java index b53c582e41f..5939c96affa 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -48,6 +48,7 @@ import javax.xml.catalog.CatalogFeatures; import jdk.xml.internal.FeaturePropertyBase; import jdk.xml.internal.JdkConstants; +import jdk.xml.internal.JdkProperty; import jdk.xml.internal.JdkXmlConfig; import jdk.xml.internal.JdkXmlUtils; import jdk.xml.internal.XMLSecurityManager; @@ -59,7 +60,7 @@ *

An implementation of XMLComponentManager for a schema validator.

* * @author Michael Glavassevich, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ final class XMLSchemaValidatorComponentManager extends ParserConfigurationSettings implements XMLComponentManager { @@ -378,9 +379,9 @@ else if (USE_GRAMMAR_POOL_ONLY.equals(featureId) && value != fUseGrammarPoolOnly if (value) { fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_DTD, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); fSecurityPropertyMgr.setValue(XMLSecurityPropertyManager.Property.ACCESS_EXTERNAL_SCHEMA, - FeaturePropertyBase.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); + JdkProperty.State.FSP, JdkConstants.EXTERNAL_ACCESS_DEFAULT_FSP); setProperty(XML_SECURITY_PROPERTY_MANAGER, fSecurityPropertyMgr); } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java index 9bc3d319619..8597af5f76e 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/DOMParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -61,7 +61,7 @@ * * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class DOMParser extends AbstractDOMParser { @@ -575,7 +575,7 @@ public void setProperty(String propertyId, Object value) * internally the support of this property is done through * XMLSecurityPropertyManager */ - securityPropertyManager.setValue(index, FeaturePropertyBase.State.APIPROPERTY, (String)value); + securityPropertyManager.setValue(index, JdkProperty.State.APIPROPERTY, (String)value); } else { //check if the property is managed by security manager if (!securityManager.setLimit(propertyId, JdkProperty.State.APIPROPERTY, value)) { diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java index 52be34a5a37..922177bb740 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/SAXParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -42,7 +42,7 @@ * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM * - * @LastModified: May 2025 + * @LastModified: July 2026 */ public class SAXParser extends AbstractSAXParser { @@ -169,7 +169,7 @@ public void setProperty(String name, Object value) * internally the support of this property is done through * XMLSecurityPropertyManager */ - securityPropertyManager.setValue(index, FeaturePropertyBase.State.APIPROPERTY, (String)value); + securityPropertyManager.setValue(index, JdkProperty.State.APIPROPERTY, (String)value); } else { //check if the property is managed by security manager if (!securityManager.setLimit(name, JdkProperty.State.APIPROPERTY, value)) { diff --git a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java index 833790834ac..67b7eaf471c 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/xinclude/XIncludeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -78,6 +78,7 @@ import javax.xml.transform.Source; import jdk.xml.internal.JdkConstants; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.SecuritySupport; import jdk.xml.internal.XMLSecurityManager; import jdk.xml.internal.XMLSecurityPropertyManager; import org.xml.sax.InputSource; @@ -128,7 +129,7 @@ * * * @see XIncludeNamespaceSupport - * @LastModified: Apr 2025 + * @LastModified: July 2026 */ public class XIncludeHandler implements XMLComponent, XMLDocumentFilter, XMLDTDFilter { @@ -1704,6 +1705,23 @@ protected boolean handleIncludeElement(XMLAttributes attributes) } if (includedSource == null) { + String expandedSystemId; + try { + expandedSystemId = XMLEntityManager.expandSystemId( + href, fCurrentBaseURI.getExpandedSystemId(), false); + } catch (MalformedURIException e) { + reportResourceError("XMLResourceError", + new Object[] { href, e.getMessage()}, e); + return false; + } + String accessError = SecuritySupport.checkAccess(expandedSystemId, + fSecurityManager, null, JdkConstants.ACCESS_EXTERNAL_ALL); + if (accessError != null) { + reportFatalError("XMLResourceError", + new Object[] { href, accessError}); + return false; + } + // setup an HTTPInputSource if either of the content negotation attributes were specified. if (accept != null || acceptLanguage != null) { includedSource = createInputSource(null, href, fCurrentBaseURI.getExpandedSystemId(), accept, acceptLanguage); diff --git a/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java b/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java new file mode 100644 index 00000000000..6a6a7fb6d85 --- /dev/null +++ b/src/java.xml/share/classes/jdk/xml/internal/AccessRule.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.xml.internal; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * Represents a parsed rule for matching external resource access permissions based on URI patterns. + *

+ * This class encapsulates a resource access rule consisting of a scheme, optional host and port, + * and path pattern. It is used to determine if a specific {@link java.net.URI} is permitted based on + * rules specified with the {@code jdk.xml.resource.access} property. + *

+ *

+ * Supported rule format: + *

+ *   [scheme]://host[:port][/path-pattern]
+ *   [scheme]:/[path-pattern]     (for local schemes such as file and jrt)
+ * 
+ *
    + *
  • scheme: The URI scheme (e.g., http, https, ftp, file, jrt).
  • + *
  • host: Domain name, IPv4, or IPv6 address. For local schemes ("file", "jrt"), host is omitted.
  • + *
  • port: Optional. A decimal port number that matches only the specified port. If omitted, the rule matches all ports..
  • + *
  • path-pattern: Optional. A resource path pattern. Supports wildcards (e.g., {@code /*}, {@code /dtds/*}).
  • + *
+ *

+ * Wildcards are allowed in host (e.g., *.foo.com) and path (e.g., /* or /foo/*). + *

+ *

+ * Example patterns: + *

    + *
  • {@code http://*.foo.com:8080/*} - allows HTTP resources on any subdomain of foo.com at port 8080, any path
  • + *
  • {@code file:/dtds/*} - allows all files under /dtds
  • + *
  • {@code file:/foo/bar.dtd} - allows only the local file /foo/bar.dtd
  • + *
  • {@code *} - allows unrestricted access
  • + *
+ *

+ *

+ * The {@link #allows(java.net.URI)} method determines whether a given URI is permitted according to this rule. + *

+ */ +public class AccessRule { + public static final AccessRule RULE_NONE = new AccessRule(""); + public static final AccessRule RULE_ALL = new AccessRule("*"); + private final List rules = new ArrayList<>(); + private final boolean allowAll; + private final boolean denyAll; + private final String rawInput; + + public AccessRule(String input) { + this.rawInput = input; + String trimmedInput = input == null ? "" : input.trim(); + if (trimmedInput.equals("*")) { + allowAll = true; + denyAll = false; + return; + } else if (trimmedInput.isEmpty()) { + allowAll = false; + denyAll = true; + return; + } + allowAll = false; + denyAll = false; + String[] tokens = input.split(","); + for (String rawToken : tokens) { + String token = checkToken(input, rawToken); + rules.add(URIPatternRule.parse(token)); + } + } + + private String checkToken(String input, String rawToken) { + String token = rawToken.trim(); + if (token.isEmpty() && !rules.isEmpty()) { + throw new IllegalArgumentException("Invalid format for the resource.access property: " + + input + ". Empty rule cannot coexist with other rules."); + } else if (token.equals("*") && !rules.isEmpty()) { + throw new IllegalArgumentException("Invalid format for the resource.access property: " + + input + ". All access (*) rule cannot coexist with other rules."); + } + return token; + } + + public boolean allows(URI uri) { + if (denyAll) return false; + if (allowAll) return true; + for (URIPatternRule rule : rules) { + if (rule.matches(uri)) return true; + } + return false; + } + + @Override + public String toString() { return rawInput; } + + /** + * Represents a parsed URI-based pattern rule. + */ + public static class URIPatternRule { + private final String scheme; + private final HostPattern hostPattern; + private final Integer port; // null if not set + private final PathPattern pathPattern; + private URIPatternRule(String scheme, HostPattern hostPattern, Integer port, PathPattern pathPattern) { + this.scheme = scheme; + this.hostPattern = hostPattern; + this.port = port; + this.pathPattern = pathPattern; + } + + /** + * Parses the specified pattern string. + * Example patterns: file:*, file:/foo, http://foo.com, https://*.foo.com:8080 + * @param pattern the pattern string + * @return an instance of URIPatternRule from the pattern string + */ + public static URIPatternRule parse(String pattern) { + // Syntax: [scheme]:/{0,3}[host[:port]][/path-pattern] + int schemeSep = pattern.indexOf(':'); + if (schemeSep <= 0) + throw new IllegalArgumentException("Missing or invalid scheme in resource access pattern: " + pattern); + + String scheme = pattern.substring(0, schemeSep).toLowerCase(Locale.ROOT); + if (!isSupportedScheme(scheme)) + throw new IllegalArgumentException("Unsupported scheme in resource access pattern: " + pattern); + + String rest = pattern.substring(schemeSep + 1); + int slashCount = countSlashes(rest); + + if ("jrt".equals(scheme) || ("file".equals(scheme) && slashCount != 2)) { + return parseLocalPath(scheme, slashCount, rest, pattern); + } + + return parseAuthorityForm(scheme, slashCount, rest, pattern); + } + + private static URIPatternRule parseAuthorityForm(String scheme, int slashCount, + String rest, String pattern) { + // Authority must have two leading slashes. + if (slashCount != 2) { + throw new IllegalArgumentException("Rule for scheme '" + scheme + + "' must specify an authority: " + pattern); + } + String afterSlashes = rest.substring(2); + if (afterSlashes.isEmpty() || afterSlashes.startsWith(":") || afterSlashes.startsWith("/")) { + throw new IllegalArgumentException("Rule for scheme '" + scheme + + "' must specify a non-empty host: " + pattern); + } + + String hostPart; + String pathPart = null; + + int slashIndex = afterSlashes.indexOf('/'); + if (slashIndex >= 0) { + hostPart = afterSlashes.substring(0, slashIndex); + pathPart = afterSlashes.substring(slashIndex); + } else { + hostPart = afterSlashes; + } + + int portSep; + int pos = 0; + // Check IPv6 literal first as ':' within the brackets belong to the host + if (hostPart.startsWith("[")) { + pos = hostPart.indexOf(']'); + if (pos < 0) { + throw new IllegalArgumentException("Invalid IPv6 host: " + pattern); + } + } + portSep = hostPart.indexOf(':', pos); + + String host; + Integer port = null; + + if (portSep >= 0) { + if ("file".equals(scheme)) { + throw new IllegalArgumentException("Port component is not supported for file URI: " + pattern); + } + host = hostPart.substring(0, portSep); + String portPart = hostPart.substring(portSep + 1); + port = parsePortNumber(portPart, hostPart); + } else { + host = hostPart; + } + + HostPattern hostPattern = HostPattern.of(host); + + PathPattern pathPattern = null; + if (pathPart != null) { + if (pathPart.isEmpty() || pathPart.equals("/*")) { + pathPattern = PathPattern.of("*"); + } else { + pathPattern = PathPattern.of(pathPart); + } + } + + return new URIPatternRule(scheme, hostPattern, port, pathPattern); + } + + private static URIPatternRule parseLocalPath( + String scheme, int slashCount, String rest, String pattern) { + + // Remove the leading slashes + String path = rest.substring(slashCount); + + if (path.isEmpty()) { + throw new IllegalArgumentException( + scheme + " rule must have non-empty path: " + pattern); + } + + if ("*".equals(path)) { + return new URIPatternRule(scheme, null, null, PathPattern.of("*")); + } + + // add a slash for the path pattern + if (!path.startsWith("/")) { + path = "/" + path; + } + + return new URIPatternRule(scheme, null, null, PathPattern.of(path)); + } + + //The slash following the scheme may appear one to three times. + private static int countSlashes(String s) { + int count = 0; + while (count < s.length() && s.charAt(count) == '/') { + count++; + if (count > 3) { + throw new IllegalArgumentException("Too many leading slashes: " + s); + } + } + return count; + } + + public boolean matches(URI uri) { + if (uri != null && "jar".equalsIgnoreCase(uri.getScheme())) { + URI jarFile = getJarFileURI(uri); + return jarFile != null && matches(jarFile); + } + + // Resource access matching applies only to hierarchical URIs. Opaque URIs + // have no hierarchical host/port/path components to match. + if (uri == null || uri.isOpaque()) return false; + + // match scheme + String testScheme = uri.getScheme(); + if (testScheme == null || !testScheme.equalsIgnoreCase(scheme)) return false; + + // match host + String testHost = uri.getHost(); + if (hostPattern == null) { + // A hostless rule only matches a hostless URI. + if (testHost != null) { + return false; + } + } else { + // A host-based rule requires a matching host. + if (!hostPattern.matches(testHost)) return false; + if (port != null && port != (uri.getPort() == -1 ? getDefaultPort(scheme) : uri.getPort())) return false; + } + if (pathPattern != null) { + return pathPattern.matches(uri.getPath()); + } + + return true; + } + + private static URI getJarFileURI(URI jarURI) { + String schemeSpecificPart = jarURI.getRawSchemeSpecificPart(); + int separator = schemeSpecificPart.indexOf("!/"); + if (separator < 0) { + return null; + } + try { + return URI.create(schemeSpecificPart.substring(0, separator)); + } catch (IllegalArgumentException e) { + return null; + } + } + + private static boolean isSupportedScheme(String scheme) { + return switch (scheme) { + case "http", "https", "ftp", "file", "jrt" -> true; + default -> false; + }; + } + + /** + * {@return the port number.} + * @throws IllegalArgumentException if portPart is not an integer + * between 0 and 65535 (valid TCP/UDP port range). + */ + private static int parsePortNumber(String portPart, String pattern) { + if (portPart.isEmpty()) { + throw new IllegalArgumentException("Empty port: " + pattern); + } + try { + int port = Integer.parseInt(portPart); + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Port must be 0-65535: " + pattern); + return port; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Port must be numeric: " + pattern, e); + } + } + + // standard ports for known schemes + private static int getDefaultPort(String scheme) { + return switch (scheme) { + case "http" -> 80; + case "https" -> 443; + case "ftp" -> 21; + default -> -1; + }; + } + } + + + // Host pattern matching for exact, IPv4 and IPv6 hosts. + public static class HostPattern { + private final String pattern; + private final boolean isAny; + private final boolean isSubdomainPattern; + private final byte[] literalAddress; + + private HostPattern(String pattern, boolean isAny, boolean isSubdomainPattern, byte[] literalAddress) { + this.pattern = pattern; + this.isAny = isAny; + this.isSubdomainPattern = isSubdomainPattern; + this.literalAddress = literalAddress; + } + + public static HostPattern of(String hostPattern) { + String trimmed = hostPattern.trim(); + if (trimmed.equals("*")) { + return new HostPattern("*", true, false, null); + } + if (trimmed.startsWith("*.")) { + // *.example.com + return new HostPattern(trimmed.substring(2).toLowerCase(Locale.ROOT), false, true, null); + } + byte[] literalAddress = parseLiteralAddress(trimmed); + if (literalAddress != null) + return new HostPattern(trimmed, false, false, literalAddress); + // Otherwise, treat as literal domain + return new HostPattern(trimmed.toLowerCase(Locale.ROOT), false, false, null); + } + + public boolean matches(String testHost) { + if (isAny) return true; + if (testHost == null) return false; + if (literalAddress != null) { + byte[] testAddress = parseLiteralAddress(testHost); + return testAddress != null && Arrays.equals(literalAddress, testAddress); + } + testHost = testHost.toLowerCase(Locale.ROOT); + // Subdomain wildcard + if (isSubdomainPattern) { + return testHost.endsWith("." + pattern); + } + // Exact match (domain, IPv4, or IPv6) + return testHost.equals(pattern); + } + + private static byte[] parseLiteralAddress(String host) { + try { + return Inet4Address.ofLiteral(host).getAddress(); + } catch (IllegalArgumentException ignored) { + // Not an IPv4 literal; try IPv6 below. + } + + try { + return Inet6Address.ofLiteral(host).getAddress(); + } catch (IllegalArgumentException ignored) { + return null; + } + } + } + + public static class PathPattern { + private final String pattern; // e.g. /dtds/* or /* + private final boolean isAny; + private final boolean isDirectory; // endsWith /* + + private PathPattern(String pattern, boolean isAny, boolean isDirectory) { + this.pattern = pattern; + this.isAny = isAny; + this.isDirectory = isDirectory; + } + + public static PathPattern of(String pattern) { + pattern = (pattern == null || pattern.isEmpty()) ? "/" : pattern; + // supports *, /foo/*, /foo/bar + if (pattern.equals("*") || pattern.equals("/*")) { + return new PathPattern(pattern, true, false); + } + // Now validate. Note: no validation needed if the whole component is a wildcard + validatePath(pattern); + if (pattern.endsWith("/*")) { + return new PathPattern(pattern.substring(0, pattern.length() - 2), false, true); + } + return new PathPattern(pattern, false, false); + } + + public boolean matches(String testPath) { + if (isAny) return true; + if (testPath == null) return false; + testPath = normalizePath(testPath); + if (isDirectory) { + // Path starts with this directory + return testPath.startsWith(pattern + "/") || testPath.equals(pattern); + } + return testPath.equals(pattern); + } + + /* + * Validates the path component. A path is either a literal file or directory + * or wildcard (*). + */ + private static void validatePath(String pathPart) { + if (pathPart != null) { + if (pathPart.indexOf("//") >= 0) { + throw new IllegalArgumentException( + "Path component must not contain empty segments: " + pathPart); + } + + for (String segment : pathPart.split("/")) { + if (isDotSegment(segment)) { + throw new IllegalArgumentException( + "Path component must not contain dot segments: " + pathPart); + } + } + } + } + } + + // Normalizes URI path for rule matching, consistent with URI.normalize(). + private static String normalizePath(String path) { + boolean absolute = path.startsWith("/"); + List segments = new ArrayList<>(); + boolean isDotSegment = false; + for (String segment : path.split("/")) { + if (segment.isEmpty() || segment.equals(".")) { + continue; + } + // URI spec: A ".." segment is removed only if it is preceded by a non-".." segment + if (segment.equals("..")) { + if (!segments.isEmpty() && !segments.get(segments.size() - 1).equals("..")) { + segments.remove(segments.size() - 1); + continue; + } + } + segments.add(segment); + } + String normalizedPath = String.join("/", segments); + return absolute ? "/" + normalizedPath : normalizedPath; + } + + /* + * Checks whether the specified path segment represents a dot segment, + * {@code "."} or {@code ".."}, or encoded as {@code "%2e"}. + */ + private static boolean isDotSegment(String segment) { + int len = segment.length(); + + if (len == 0 || len > 6) { + return false; + } else if (".".equals(segment) || "..".equals(segment)) { + return true; + } else if (segment.indexOf('%') < 0) { + return false; + } + + // encoded dot segment + int dots = 0; + for (int i = 0; i < len;) { + if (segment.charAt(i) == '.') { + dots++; + i++; + } else if (i + 2 < len + && segment.charAt(i) == '%' + && segment.charAt(i + 1) == '2' + && (segment.charAt(i + 2) == 'e' + || segment.charAt(i + 2) == 'E')) { + dots++; + i += 3; + } else { + return false; + } + } + + return dots == 1 || dots == 2; + } +} diff --git a/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java b/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java index 0a17ffa1121..d311b778723 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java +++ b/src/java.xml/share/classes/jdk/xml/internal/FeaturePropertyBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,23 +24,12 @@ */ package jdk.xml.internal; +import jdk.xml.internal.JdkProperty.State; /** * This is the base class for features and properties */ public abstract class FeaturePropertyBase { - - /** - * States of the settings of a property, in the order: default value, value - * set by FEATURE_SECURE_PROCESSING, jaxp.properties file, jaxp system - * properties, and jaxp api properties - */ - public static enum State { - //this order reflects the overriding order - DEFAULT, FSP, JAXPDOTPROPERTIES, SYSTEMPROPERTY, APIPROPERTY - } - - /** * Values of the properties as defined in enum Properties */ diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java index 412eddb5ab0..927d19bc3b3 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -311,6 +311,17 @@ public final class JdkConstants { //public static final int IGNORE = 1; // same as that of DTD public static final int STRICT = 2; + /** + * System Property for the Resource Access property + * @since 26 + */ + public static final String RESOURCE_ACCESS = "jdk.xml.resource.access"; + + // Integer Values that maps to broad access settings such as *, remote + public static final int ACCESS_NONE = 0; + public static final int ACCESS_ALL = 1; // same as that of DTD + public static final int ACCESS_REMOTE = 2; + /** * Values for a feature */ diff --git a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java index 9e718b264e4..f66463833ef 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,7 +113,7 @@ public static boolean setProperty(XMLSecurityManager xsm, XMLSecurityPropertyMan return xsm.setLimit(property, JdkProperty.State.APIPROPERTY, value); } else if (xspm != null && xspm.find(property) != null) { - return xspm.setValue(property, FeaturePropertyBase.State.APIPROPERTY, value); + return xspm.setValue(property, JdkProperty.State.APIPROPERTY, value); } return false; } diff --git a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java index 478d12220d7..2787ba67b5f 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java +++ b/src/java.xml/share/classes/jdk/xml/internal/SecuritySupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,15 +29,18 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.nio.file.Paths; import java.security.CodeSource; import java.text.MessageFormat; +import java.util.HashSet; import java.util.Locale; import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; +import java.util.Set; /** * This class contains utility methods for reading resources in the JAXP packages @@ -267,66 +270,90 @@ public static String sanitizePath(String input) { return input; } + /** + * Checks whether access to resource represented by the systemId is permitted + * by Resource Access and/or External Access Properties (EAP) + * @param systemId the systemId + * @param xsm the XMLSecurityManager + * @param eap the External Access Property, e.g. ACCESS_EXTERNAL_DTD + * @param allowedProtocols protocols allowed by the EAP + * @return true if access is permitted, false otherwise + * @throws IOException if the systemId is invalid + */ + public static String checkAccess(String systemId, XMLSecurityManager xsm, + String eap, String allowedProtocols) { + String errMsg = null; + if (xsm != null && !xsm.isAccessAllowed(systemId)) { + errMsg = "Resource Access (jdk.xml.resource.access)"; + } + + if (!checkAccess(systemId, allowedProtocols)) { + errMsg = (errMsg != null) ? errMsg + " and " + eap : eap; + } + return errMsg; + } + /** * Check the protocol used in the systemId against allowed protocols * * @param systemId the Id of the URI * @param allowedProtocols a list of allowed protocols separated by comma - * @param accessAny keyword to indicate allowing any protocol - * @return the name of the protocol if rejected, null otherwise + * @return true if access is permitted, false otherwise */ - public static String checkAccess(String systemId, String allowedProtocols, - String accessAny) throws IOException { + public static boolean checkAccess(String systemId, String allowedProtocols) { + if (Utils.isEmpty(allowedProtocols)) { + return false; + } if (systemId == null || (allowedProtocols != null && - allowedProtocols.equalsIgnoreCase(accessAny))) { - return null; + allowedProtocols.equalsIgnoreCase(JdkConstants.ACCESS_EXTERNAL_ALL))) { + return true; } - String protocol; - if (!systemId.contains(":")) { - protocol = "file"; + URI uri = Utils.createURI(systemId); + String scheme = uri.getScheme(); + if (scheme == null) { + scheme = "file"; } else { - @SuppressWarnings("deprecation") - URL url = new URL(systemId); - protocol = url.getProtocol(); - if (protocol.equalsIgnoreCase("jar")) { - String path = url.getPath(); - protocol = path.substring(0, path.indexOf(":")); - } else if (protocol.equalsIgnoreCase("jrt")) { - // if the systemId is "jrt" then allow access if "file" allowed - protocol = "file"; + scheme = scheme.toLowerCase(Locale.ROOT); + if ("jar".equals(scheme)) { + String ssp = uri.getSchemeSpecificPart(); // e.g. file:/x.jar!/a.xml + int sep = ssp.indexOf("!/"); + if (sep != -1) { + URI nested = Utils.createURI(ssp.substring(0, sep)); + String nestedScheme = nested.getScheme(); + if (nestedScheme != null) { + scheme = nestedScheme.toLowerCase(Locale.ROOT); + } + } + } else if ("jrt".equals(scheme)) { + // allow access if it's "file" + scheme = "file"; } } - if (isProtocolAllowed(protocol, allowedProtocols)) { - //access allowed - return null; - } else { - return protocol; - } + Set allowed = parseProtocols(allowedProtocols); + return allowed.contains(scheme); } /** - * Check if the protocol is in the allowed list of protocols. The check - * is case-insensitive while ignoring whitespaces. - * - * @param protocol a protocol - * @param allowedProtocols a list of allowed protocols - * @return true if the protocol is in the list + * Parses allowed protocols. + * @param protocols the protocol setting + * @return a set containing allowed protocols */ - private static boolean isProtocolAllowed(String protocol, String allowedProtocols) { - if (allowedProtocols == null) { - return false; - } - String temp[] = allowedProtocols.split(","); - for (String t : temp) { - t = t.trim(); - if (t.equalsIgnoreCase(protocol)) { - return true; - } - } - return false; - } + private static Set parseProtocols(String protocols) { + Set set = new HashSet<>(); + if (protocols == null || protocols.isEmpty()) { + return set; + } + + for (String p : protocols.split(",")) { + String trimmed = p.trim().toLowerCase(Locale.ROOT); + if (!trimmed.isEmpty()) { + set.add(trimmed); + } + } + return set; + } public static ClassLoader getContextClassLoader() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); diff --git a/src/java.xml/share/classes/jdk/xml/internal/Utils.java b/src/java.xml/share/classes/jdk/xml/internal/Utils.java index 96d9b1b9521..f7f5c49387b 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/Utils.java +++ b/src/java.xml/share/classes/jdk/xml/internal/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package jdk.xml.internal; import java.lang.reflect.Array; +import java.net.URI; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; import java.util.function.Supplier; @@ -132,6 +134,52 @@ public static Stream streamOfIfNonNull(final T... values) { * @return {@code true} if the CharSequence is empty or null */ public static boolean isEmpty(final CharSequence cs) { - return cs == null || cs.length() == 0; + return cs == null || cs.isEmpty(); + } + + /** + * Checks if a CharSequence is not null and empty (""). + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not null and empty ("") + */ + public static boolean isNotEmpty(final CharSequence cs) { + return cs != null && !cs.isEmpty(); + } + + /** + * Checks if a CharSequence is not null and empty (""), and also does not end + * with a wildcard (*). + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not null and empty ("") and does + * not end with a wildcard + */ + public static boolean isNotEmptyOrWildcard(final CharSequence cs) { + return cs != null && !cs.isEmpty() && (cs.charAt(cs.length() - 1) != '*'); + } + + /** + * Creates a {@link URI} instance from a systemId. + * This method handles strings that are either absolute URIs or local file + * system paths. + * + * @param systemId the systemId + * @return a {@link URI} instance corresponding to the systemId + */ + public static URI createURI(String systemId) { + if (systemId == null) { + return null; + } + + try { + URI uri = new URI(systemId); + + if (uri.getScheme() == null) { + return Path.of(systemId).toUri(); + } + return uri; + } catch (Exception e) { + // fallback for paths with illegal characters (e.g. spaces) + return Path.of(systemId).toUri(); + } } } diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java index a1687c420c3..336b101f8ac 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ import com.sun.org.apache.xerces.internal.util.SecurityManager; + +import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -82,6 +84,17 @@ public final class XMLSecurityManager implements Cloneable { CR_MAP = Collections.unmodifiableMap(map); } + // Valid values for Resource Access, and mappings between the string and + // integer values + static final Map RA_MAP; + // Source Level JDK 8 + static { + Map map = new HashMap<>(); + map.put("*", 0); + map.put("", 2); + RA_MAP = Collections.unmodifiableMap(map); + } + // Value converter for properties of type Boolean private static final BooleanMapper BOOLMAPPER = new BooleanMapper(); @@ -94,6 +107,9 @@ public final class XMLSecurityManager implements Cloneable { // Catalog Resolve value mapper private static final StringMapper CRMAPPER = new StringMapper(CR_MAP); + // Resource Access value mapper + private static final StringMapper RAMAPPER = new StringMapper(RA_MAP); + /** * Limits managed by the security manager */ @@ -129,6 +145,8 @@ public static enum Limit { STAX_SUPPORT_DTD("supportDTD", XMLInputFactory.SUPPORT_DTD, null, null, 1, 1, Processor.PARSER, BOOLMAPPER), JDKCATALOG_RESOLVE("JDKCatalogResolve", JdkConstants.JDKCATALOG_RESOLVE, JdkConstants.JDKCATALOG_RESOLVE, null, JdkConstants.CONTINUE, JdkConstants.CONTINUE, Processor.PARSER, CRMAPPER), + RESOURCE_ACCESS("ResourceAccess", JdkConstants.RESOURCE_ACCESS, JdkConstants.RESOURCE_ACCESS, null, + JdkConstants.ALLOW, JdkConstants.ALLOW, Processor.PARSER, RAMAPPER), ; final String key; @@ -262,6 +280,8 @@ public static enum Processor { private final int indexEntityCountInfo = 10000; private String printEntityCountInfo = ""; + private AccessRule accessRule = null; + /** * Default constructor. Establishes default values for known security * vulnerabilities. @@ -289,6 +309,11 @@ public XMLSecurityManager(boolean secureProcessing) { states[limit.ordinal()] = State.DEFAULT; } } + if (secureProcessing) { + accessRule = (Limit.RESOURCE_ACCESS.secureValue == JdkConstants.ALLOW) ? AccessRule.RULE_ALL : AccessRule.RULE_NONE; + } else { + accessRule = (Limit.RESOURCE_ACCESS.defaultValue() == JdkConstants.ALLOW) ? AccessRule.RULE_ALL : AccessRule.RULE_NONE; + } } /** @@ -380,6 +405,15 @@ public boolean isSecureProcessingSet() { * @return the limit's new name if found, null otherwise */ public String find(String propertyName) { + /* + * Access property is unique in its value type. Using the SecurityManager + * infrastructure, but handles differently than other limits + */ + if (Limit.RESOURCE_ACCESS.is(propertyName)) { + return (Limit.RESOURCE_ACCESS.systemProperty != null) + ? Limit.RESOURCE_ACCESS.systemProperty + : Limit.RESOURCE_ACCESS.apiProperty; + } for (Limit limit : Limit.values()) { if (limit.is(propertyName)) { // current spec: new property name == systemProperty @@ -440,6 +474,24 @@ public void setLimit(Limit limit, State state, int value) { * @param value the value of the property */ public void setLimit(Limit limit, State state, Object value) { + /* + * Access property is unique in its value type. Using the SecurityManager + * infrastructure, but handles differently than other limits + */ + if (limit == Limit.RESOURCE_ACCESS) { + int index = limit.ordinal(); + if (state.compareTo(states[index]) >= 0) { + String ruleValue = (String)value; + AccessRule rule = new AccessRule(ruleValue); + int intValue = limit.mapper().toInt(ruleValue); + accessRule = rule; + values[index] = intValue; + states[index] = state; + isSet[index] = true; + } + return; + } + int intValue = limit.mapper().toInt(value); if (intValue < 0) { intValue = 0; @@ -469,6 +521,29 @@ public void setLimit(int index, State state, int value) { } } + public AccessRule getAccessRule() { + return accessRule; + } + + public boolean isAccessAllowed(String systemId) { + URI uri = Utils.createURI(systemId); + if (uri == null) return true; + return isAccessAllowed(uri); + } + + /** + * Checks the RESOURCE_ACCESS property to see if access to the specified + * uri is allowed. + * + * @param uri the specified uri + * @return true if the RESOURCE_ACCESS property is set to allow access to the + * specified uri, false otherwise + */ + public boolean isAccessAllowed(URI uri) { + return accessRule.allows(uri); + } + + /** * Return the value of the specified property * @@ -477,6 +552,10 @@ public void setLimit(int index, State state, int value) { * by this manager, its value shall not be null. */ public String getLimitAsString(String propertyName) { + if (Limit.RESOURCE_ACCESS.is(propertyName)) { + return accessRule.toString(); + } + int index = getIndex(propertyName); if (index > -1) { return getLimitValueByIndex(index); @@ -620,10 +699,10 @@ public boolean isOverLimit(int index, String entityName, int size, } /** - * Check against cumulated value + * Check limit against the cumulated value * * @param limit the type of the limit property - * @param size the size (count or length) of the entity + * @param limitAnalyzer the limit analyzer * @return true if the size is over the limit, false otherwise */ public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) { @@ -739,7 +818,7 @@ private boolean getSystemProperty(Limit limit, String sysPropertyName) { try { String value = System.getProperty(sysPropertyName); - if (value != null && !value.equals("")) { + if (isPropertyValuePresent(limit, value)) { setLimit(limit, State.SYSTEMPROPERTY, value); return true; } @@ -760,7 +839,7 @@ private boolean getSystemProperty(Limit limit, String sysPropertyName) { private boolean getPropertyConfig(Limit limit, String sysPropertyName) { try { String value = SecuritySupport.readConfig(sysPropertyName); - if (value != null && !value.equals("")) { + if (isPropertyValuePresent(limit, value)) { setLimit(limit, State.JAXPDOTPROPERTIES, value); return true; } @@ -771,6 +850,10 @@ private boolean getPropertyConfig(Limit limit, String sysPropertyName) { return false; } + private boolean isPropertyValuePresent(Limit limit, String value) { + return value != null && (!value.isEmpty() || limit == Limit.RESOURCE_ACCESS); + } + /** * Convert a value set through setProperty to XMLSecurityManager. * If the value is an instance of XMLSecurityManager, use it to override the default; diff --git a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java index ff9ab621fab..226a5e34fbc 100644 --- a/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java +++ b/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityPropertyManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package jdk.xml.internal; import javax.xml.XMLConstants; +import jdk.xml.internal.JdkProperty.State; /** * This class manages security related properties @@ -161,6 +162,15 @@ public String getValue(Property property) { return values[property.ordinal()]; } + /** + * {@return the state of the specified property} + * + * @param property the specified property + */ + public State getState(Property property) { + return states[property.ordinal()]; + } + /** * Read from system properties, or those in jaxp.properties */ diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java index 1b4f345eb72..a27354ff6a6 100644 --- a/src/java.xml/share/classes/module-info.java +++ b/src/java.xml/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,8 @@ * questions. */ +import java.net.URI; + /** * Defines the Java APIs for XML Processing (JAXP). * @@ -197,6 +199,14 @@ * * * + * If more than one property affects an aspect of operation, all properties must + * permit the operation in order for it to proceed. For example, both the + * Resource Access property + * and External Access Properties (EAPs) must + * be set to allow in order for a direct fetch of an external resource to be + * carried out. For more details, refer to the + * External Resource Resolution Process. + *

* Using the {@link javax.xml.catalog.CatalogFeatures CatalogFeatures}' RESOLVE * property as an example, the following illustrates how these rules are applied: *

    @@ -403,10 +413,7 @@ * *
      *
    • JDK built-in Catalog - * - *
    • + *
    • External Resource Resolution Process
    • *
    • Implementation Specific Properties *
        *
      • Processor Support
      • @@ -511,23 +518,60 @@ *

        * The catalog is loaded once when the first JAXP processor factory is created. * - *

        External Resource Resolution Process with the built-in Catalog

        - * The JDK creates a {@link javax.xml.catalog.CatalogResolver CatalogResolver} - * with the built-in catalog when needed. This CatalogResolver is used as the - * default external resource resolver. + *

        External Resource Resolution Process

        + *

        + * The XML processor resolves external resources using the following steps: + *

          + *
        • User-defined resolver, such as {@link org.xml.sax.EntityResolver EntityResolver}, + * {@link javax.xml.stream.XMLResolver XMLResolver}, if registered on the XML factory
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} if registered or + * if a catalog file is provided
        • + *
        • The JDK Built-in Catalog
        • + *
        • Direct fetch
        • + *
        + * *

        - * XML processors may use resolvers (such as {@link org.xml.sax.EntityResolver EntityResolver}, - * {@link javax.xml.stream.XMLResolver XMLResolver}, and {@link javax.xml.catalog.CatalogResolver CatalogResolver}) - * to handle external references. In the absence of the user-defined resolvers, - * the JDK XML processors fall back to the default CatalogResolver to attempt to - * find a resolution before making a connection to fetch the resources. The fall-back - * also takes place if a user-defined resolver exists but allows the process to - * continue when unable to resolve the resource. + * If a resolver or catalog successfully resolves the resource, the process ends. + * Otherwise, the XML processor evaluates the signal returned to decide whether + * to continue, ignore, or reject the resource. + *

        + * It continues to the next step if: + *

          + *
        • User-defined resolver returns {@code null}
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns {@code null} + * and the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is set to {@code continue} + *
        • + *
        • The JDK Built-in Catalog returns {@code null} and its Resolve property + * {@code jdk.xml.jdkcatalog.resolve} is set to {@code continue}
        • + *
        + *
        + * The XML processor then will attempt a direct fetch if the resource is allowed + * by the Resource Access property {@code jdk.xml.resource.access} and + * the External Access Properties. + *
        *

        - * If the default CatalogResolver is unable to locate a resource, it may signal - * the XML processors to continue processing, or skip the resource, or - * throw a CatalogException. The behavior is configured with the - * {@code jdk.xml.jdkcatalog.resolve} property. + * It ignores the resource and returns an empty source if: + *

          + *
        • User-defined resolver returns an empty source
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns an empty source + * and the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is set to {@code ignore} + *
        • + *
        + * + * It terminates and throws an exception if: + *
          + *
        • User-defined resolver throws an exception
        • + *
        • {@link javax.xml.catalog.CatalogResolver CatalogResolver} returns {@code null}, + * the {@link javax.xml.catalog.CatalogFeatures.Feature#RESOLVE RESOLVE} feature + * is {@code strict} + *
        • + *
        • The resource is not allowed by Resource Access property + * {@code jdk.xml.resource.access} or + * External Access Properties + *
        • + *
        * *

        Implementation Specific Properties

        * In addition to the standard JAXP Properties, @@ -871,7 +915,7 @@ * Determines whether extension functions in the Transform API are to be allowed. * The extension functions in the XPath API are not affected by this property. * - * yes + * yes * Boolean * * true or false. True indicates that extension functions are allowed; False otherwise. @@ -994,6 +1038,109 @@ * Method 1 * 22 * + * + * {@systemProperty jdk.xml.resource.access} + * Defines allowed access to external resources by specifying a list + * of URI patterns, each following the syntax:

        + * {@code scheme:[/][host][:port]/[path]}

        + * Except where this specification explicitly extends or overrides the syntax or + * semantics defined by the {@link java.net.URI URI} class, such as the support for wildcard + * patterns, the definitions of {@link java.net.URI URI} apply.

        + * Where: + *
          + *
        • Component Syntax: A resource access entry is either: + *
            + *
          • + * An exact-match URI, where the scheme, host, port, and path components + * must conform to the syntax requirements of the {@link java.net.URI URI} + * class and the URI is interpreted as a hierarchical URI. + *
          • + *
          • + * A wildcard URI pattern, where the URI syntax is extended to allow + * the wildcard {@code *} in the components explicitly defined by this specification. + * The wildcard matching rules for each supported component are described below. + *
          • + *
          + * For both forms, the scheme and host are case-insensitive, while the path is case-sensitive. + *
        • + *
        • scheme: The URI scheme. Unless the entire pattern is the single wildcard {@code *}, + * the scheme must be one of the supported schemes (http, https, ftp, file, or jrt). + *
        • + *
        • jar: jar URIs are matched against the URI of the underlying JAR file + * using the corresponding rule pattern. For example, the rule pattern + * {@code file:/tmp/foo.jar} permits access to {@code jar:file:/tmp/foo.jar!/dtds/test.dtd}. + * + * Permission to access the JAR file automatically grants access to all entries within + * that JAR; entry-level restrictions are not supported. + *
        • + *
        • / (slash): The slash following the scheme may appear one to three times. + * Two slashes (//) indicate the start of an authority (for example, http://example.com). + * One or three slashes (/ or ///) indicate the start of a local resource path + * (for example, {@code file:/foo/bar.dtd, file:///foo/bar.dtd}). + *
        • + *
        • host: Optional. A domain name, IPv4 address, IPv6 literal, or supported + * wildcard pattern. The wildcard {@code *} denotes any host. A leading wildcard followed + * by a domain, such as {@code*.example.com}, matches all subdomains of {@code example.com}. + * For network schemes, the host component must not be empty.
        • + *
        • IPv6 Literals: IPv6 literals must be enclosed in square brackets, + * as defined by {@link java.net.URI URI}, for example {@code https://[2001:db8::6]}
        • + *
        • port: Optional. A decimal port number, as per the URI standard, + * to indicate only the specified port is permitted. If specified, + * it can not be empty. {@code http://example.com:}, for example, is illegal.
        • + *
        • path: Optional. Specifies a resource path. Wildcard {@code *} + * may be used in the path to match any sequence (e.g., {@code /foo/*} matches + * all resources under {@code /foo/}). For local schemes, paths are typically + * absolute (e.g., {@code file:/dtds/*}). To match all resources under a directory, + * the wildcard {@code *} must be added. Without it, the path is treated as + * a literal file or directory.
        • + *
        • entire pattern: can be a wildcard {@code *} or empty "", which + * represents all access or no access permitted respectively.
        • + *
        • wildcard: The wildcard {@code *} may be used only in the forms + * explicitly defined by this specification: + *
            + *
          • as the entire pattern ({@code *});
          • + *
          • as the entire host component ({@code http://*});
          • + *
          • as a leading wildcard in the host component ({@code http://*.example.com});
          • + *
          • as the final path segment ({@code file:/foo/*}).
          • + *
          + * Any other occurrence of {@code *} is treated as a literal character and does not perform wildcard matching. + *
        • + *
        + * Example: + * {@snippet : + * jdk.xml.resource.access = https://*.sun.com, http://www.w3.org, https://127.0.0.1, file:/dtds/*, jrt:*, file:/tmp/foo.jar + * } + * This configuration permits access to: + *
          + *
        • https access to any subdomain of sun.com, e.g. java.sun.com
        • + *
        • Resources from the specific domain as listed in the example, w3.org, 127.0.0.1
        • + *
        • All local resources under the dtds directory
        • + *
        • Resources from the Java runtime image
        • + *
        • The local {@code /tmp/foo.jar} file and its contents
        • + *
        + * The following configuration permits all access:
        + * {@code jdk.xml.resource.access = *}
        + * The following configuration permits no access:
        + * {@code jdk.xml.resource.access = ""} + * + * + * String + * + * A comma-separated list of URL patterns, * (wildcard), or "" (empty string). + * + * {@code *} + * {@code *} + * No [8] + * + * DOM
        + * SAX
        + * StAX
        + * Validation
        + * Transform + * + * Method 1 + * 27 + * * * *

        @@ -1037,6 +1184,11 @@ * These three properties control whether DTDs as a whole shall be processed. When * they are set to deny or ignore, other properties that regulate a part or an * aspect of DTD shall have no effect. + *

        + * [8] In the current release, the state of the {@code jdk.xml.resource.access} + * property does not change when {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FEATURE_SECURE_PROCESSING} + * (FSP) is enabled. In future releases, it will transition to a restrictive setting + * when FSP is enabled. * *

        Legacy Property Names (deprecated)

        * JDK releases prior to JDK 17 support the use of URI style prefix for properties. diff --git a/src/java.xml/share/conf/jaxp.properties b/src/java.xml/share/conf/jaxp.properties index 2b7136319ce..514793cd932 100644 --- a/src/java.xml/share/conf/jaxp.properties +++ b/src/java.xml/share/conf/jaxp.properties @@ -95,6 +95,81 @@ jdk.xml.overrideDefaultParser=false # javax.xml.accessExternalStylesheet="all" # # +# XML Resource Access Property: +# +# A comma-separated list of URI patterns, * (wildcard), or "" (empty string), +# each following the syntax: scheme:[/][host][:port]/[path] +# +# Component Syntax: A resource access entry is either: +# - An exact-match URI, where the scheme, host, port, and path components must conform +# to the syntax requirements of the java.net.URI class. +# - A wildcard URI pattern, where the URI syntax is extended to allow the wildcard * +# in the components explicitly defined by this specification. +# +# For both forms, the scheme and host are case-insensitive, while the path is case-sensitive. +# +# scheme: The URI scheme. Unless the entire pattern is the single wildcard *, +# the scheme must be one of the supported schemes (http, https, ftp, file, or jrt). +# +# jar: jar URIs are matched against the URI of the underlying JAR file using the +# corresponding rule pattern. For example, the rule pattern file:/tmp/foo.jar permits +# access to jar:file:/tmp/foo.jar!/dtds/test.dtd. +# +# Permission to access the JAR file automatically grants access to all entries within that JAR; +# entry-level restrictions are not supported. + +# / (slash): The slash following the scheme may appear one to three times. +# Two slashes (//) indicate the start of an authority (for example, http://example.com). +# One or three slashes (/ or ///) indicate the start of a local resource path +# (for example, file:/foo/bar.dtd or file:///foo/bar.dtd). + +# host: Optional. A domain name, IPv4 address, IPv6 literal, or supported wildcard pattern. +# The wildcard * denotes any host. A leading wildcard followed by a domain, +# such as *.example.com, matches all subdomains of example.com. For network schemes, +# the host component must not be empty. +# +# IPv6 Literals: IPv6 literals must be enclosed in square brackets, +# as defined by java.net.URI, for example https://[2001:db8::6]. +# +# port: Optional. A decimal port number, as per the URI standard, to indicate only +# the specified port is permitted. If specified, it can not be empty. +# http://example.com:, for example, is illegal. +# +# path: Optional. Specifies a resource path. Wildcard * may be used in the path +# to match any sequence (e.g., /foo/* matches all resources under /foo/). +# For local schemes, paths are typically absolute (e.g., file:/dtds/*). +# To match all resources under a directory, the wildcard * must be added. +# Without it, the path is treated as a literal file or directory. +# +# entire pattern: can be a wildcard * or empty "", which represents all access +# or no access permitted respectively. +# +# wildcard: The wildcard * may be used only in the forms explicitly defined by this specification: +# - as the entire pattern (*); +# - as the entire host component (http://*); +# - as a leading wildcard in the host component (http://*.example.com); +# - as the final path segment (file:/foo/*). +# +# Any other occurrence of * is treated as a literal character and does not perform wildcard matching. +# +# +# Example: +# jdk.xml.resource.access = https://*.sun.com, http://www.w3.org, https://127.0.0.1, file:/dtds/, jrt:*, file:/tmp/foo.jar +#Examples: +# +# jdk.xml.resource.access = https://*.example.com, http://www.example.com, https://127.0.0.1, \ +# https://127.0.0.1:80, https://[fe80::1%lo0], https://[fe80::1%lo0]:80, file:/dtds/*, jrt:/*, file:/tmp/foo.jar +# +# This configuration permits access to: +# - https access to any subdomain of example.com, e.g. foo.example.com +# - Resources from the specific domain as listed in the example, example.com, 127.0.0.1 and [fe80::1%lo0] +# - All local resources under the dtds directory +# - Resources from the Java runtime image +# - The local /tmp/foo.jar file and its contents +# +# The default setting allows unrestricted access +jdk.xml.resource.access=* +# # Catalog Properties: # # The Catalog API defines four features: FILES, PREFER, DEFER and RESOLVE. diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java new file mode 100644 index 00000000000..999b7f3f67c --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessRuleTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import jdk.xml.internal.AccessRule; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.net.URI; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +/* + * @test + * @bug 8357394 + * @summary Verifies access rules defined by property jdk.xml.resource.access + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest /test/lib + * @modules java.xml/jdk.xml.internal + * @run junit/othervm common.access.AccessRuleTest + */ +public class AccessRuleTest { + /** + * Returns test data for testAccessRule. + * Data: rules, URI strings, result (true if allowed, false otherwise) + * @return test data for testAccessRule + */ + private static Stream testData() { + + return Stream.of( + Arguments.of("*", "http://all.access", true), + Arguments.of("", "http://no.access", false), + Arguments.of("http://*", "http://all.http.access", true), + Arguments.of("http://*.oracle.com", "http://subdomains.oracle.com/dtds/example.dtd", true), + Arguments.of("https://*", "https://all.https.access", true), + Arguments.of("https://*.oracle.com", "https://subdomains.oracle.com/dtds/example.dtd", true), + // If port is omitted, matches all ports. + Arguments.of("http://www.oracle.com", + "http://www.oracle.com/dtds/example.dtd; http://www.oracle.com:80/dtds/example.dtd; http://www.oracle.com:8080/dtds/example.dtd", true), + // An explicitly specified default port also matches an omitted port. + Arguments.of("http://www.oracle.com:80", + "http://www.oracle.com/dtds/example.dtd; http://www.oracle.com:80/dtds/example.dtd", true), + // An explicitly specified default port does not match a non-default port. + Arguments.of("http://www.oracle.com:80", "http://www.oracle.com:8080/dtds/example.dtd", false), + // If port is specified, matches only the specified port. + Arguments.of("http://www.oracle.com:8080", + "http://www.oracle.com/dtds/example.dtd; http://www.oracle.com:80/dtds/example.dtd", false), + Arguments.of("http://www.oracle.com:8080", "http://www.oracle.com:8080/dtds/example.dtd", true), + + Arguments.of("http://www.oracle.com, http://*.oracle.com", + "http://www.oracle.com/dtds/example.dtd; http://subdomains.oracle.com/dtds/example.dtd", true), + Arguments.of("file:/dtds/dtd1.dtd", "file:/dtds/dtd1.dtd", true), + Arguments.of("file:/dtds/dtd1.dtd, file:/xsds/*", "file:/dtds/dtd1.dtd; file:/xsds/example.xsd", true), + // To match all resources under a directory, the wildcard * must be added + Arguments.of("file:/dir/*", "file:/dir/child.dtd; file:/dir/sub/example.dtd", true), + // Without the wildcard *, a directory path does not match resources under the directory. + Arguments.of("file:/dir/", "file:/dir/child.dtd; file:/dir/sub/example.dtd", false), + + Arguments.of("http://www.oracle.com, file:/dtds/dtd1.dtd, file:/xsds/*", + "http://www.oracle.com/dtds/example.dtd; file:/dtds/dtd1.dtd; file:/xsds/example.xsd", true), + Arguments.of("http://[2001:db8::1]", "http://[2001:0db8:0000:0000:0000:0000:0000:0001]/dtds/example.dtd; " + + "http://[2001:db8:0:0:0:0:0:1]/dtds/example.dtd", true), + Arguments.of("http://[2001:0db8:0000:0000:0000:0000:0000:0001]", "http://[2001:db8::1]/dtds/example.dtd", true), + Arguments.of("jrt:/*; jrt:/java.xml/*", "jrt:/java.xml/jdk/xml/internal/jdkcatalog/JDKCatalog.xml", true), + Arguments.of("file:/tmp/foo.jar", + "jar:file:/tmp/foo.jar!/dtds/example.dtd; jar:file:/tmp/foo.jar!/xsds/example.xsd", true), + Arguments.of("file:/tmp/foo.jar", "jar:file:/tmp/bar.jar!/dtds/example.dtd", false), + + // URI normalization: each "." segment is simply removed + Arguments.of("file:/dir/*", "file:/dir/./foo.dtd; file:/dir/sub/./foo.dtd; file:/dir/./sub/foo.dtd", true), + // A ".." segment is removed only if it is preceded by a non-".." segment + Arguments.of("file:/dir/*", "file:/dir/../dir/foo.dtd; file:/dir/sub/../foo.dtd", true), + Arguments.of("file:/dir/*", "file:/../dir/foo.dtd; file:/../../dir/foo.dtd; file:/dir/../foo.dtd;" + + " file:/dir/sub/../../foo.dtd; file:/dir/a/b/../../../foo.dtd", false), + + // both "." and ".." segments + Arguments.of("file:/dir/*", "file:/dir/sub/../foo.dtd; file:/dir/./sub/../foo.dtd", true), + Arguments.of("file:/dir/*", "file:/dir/./sub/../foo.dtd; file:/dir/sub/./../sub/../foo.dtd", true), + Arguments.of("file:/dir/*", "file:/dir/./../sub/../foo.dtd; file:/dir/sub/./../../foo.dtd", false), + + // encoded dot segments + Arguments.of("file:/dir/*", "file:/dir/%2e/sub/../foo.dtd", true), + Arguments.of("file:/dir/*", "file:/dir/%2e%2e/foo.dtd; file:/dir/%2E%2E/foo.dtd", false), + Arguments.of("file:/dir/*", "file:/dir/.%2e/foo.dtd; file:/dir/%2e./foo.dtd", false), + Arguments.of("file:/dir/*", "file:/%2e%2e%2fdir%2ffoo.dtd; file:/dir/%2e%2e%2ffoo.dtd; file:/dir%2f%2e%2e%2ffoo.dtd; file:/dir%2f..%2f..%2ffoo.dtd", false), + + // rule allows all file access + Arguments.of("file:/*; file:///*", "file:/dir/../../foo.dtd; file:/../foo.dtd; file:/dir/sub/../foo.dtd", true), + Arguments.of("file:/*; file:///*", "file:/all.file.access; file:///all.file.access", true), + Arguments.of("file://*", "file://all.file.access", true), + + // file scheme with server authority, including Windows UNC path + Arguments.of("file://server/*", "file://server/foo.dtd; file://server/dir/foo.dtd", true), + Arguments.of("file://server/share/*", "file://server/share/foo.dtd; file://SERVER/share/foo.dtd", true), + Arguments.of("file://server/share/foo.dtd", "file://server/share/foo.dtd", true), + Arguments.of("file://server/*", "file://server1/foo.dtd; file:/foo.dtd", false), + Arguments.of("file://server/share/*", "file://server1/share/foo.dtd; file://server/SHARE/foo.dtd", false), + Arguments.of("file:/share/*", "file://server/share/foo.dtd", false), + Arguments.of("file:/*", "file://server/foo.dtd", false), + Arguments.of("file://[2001:db8::1]", "file://[2001:0db8::1]/dtds/example.dtd; file://[2001:db8:0:0:0:0:0:1]/dtds/example.dtd", true) + ); + } + + /** + * Returns test data for testInvalidRules. + * Data: rules, exception class + * @return test data for testInvalidRules + */ + private static Stream testInvalidInput() { + + return Stream.of( + Arguments.of("scheme", IllegalArgumentException.class), + Arguments.of("http; http:; http:*; http:/; http:/*; http://; http://:", IllegalArgumentException.class), + Arguments.of("file; file:; file:*, file:/; file://; file://:", IllegalArgumentException.class), + Arguments.of("jrt; jrt:; jrt:*, jrt:/", IllegalArgumentException.class), + Arguments.of("http://:8080; http://example.com:", IllegalArgumentException.class), + Arguments.of("file://:8080; file://server:8080/", IllegalArgumentException.class), + Arguments.of("http:///dtds", IllegalArgumentException.class), + Arguments.of("http://example.com, , file:*", IllegalArgumentException.class), + Arguments.of("http://example.com, *, file:*", IllegalArgumentException.class), + Arguments.of("jar:file:/tmp/foo.jar!/dtds/*", IllegalArgumentException.class), + Arguments.of("file:/dir/./foo.dtd; file:/dir/../foo.dtd; file:/dir/%2e/foo.dtd; " + + "file:/dir/%2e%2e/foo.dtd; file:/dir/.%2e/foo.dtd; file:/dir/%2e./foo.dtd", IllegalArgumentException.class), + Arguments.of("file://server/share/../foo.dtd", IllegalArgumentException.class) + ); + } + + /** + * Verifies access rules set by the Resource Access property. + * @param rules the access rules separate by ";" + * @param systemIds system IDs represented as semicolon-separated URI strings + * @param permitted the flag indicating whether the rules permit the resource + * represented by the systemId + * @throws Exception if the test fails due to test configuration issues other + * than the expected result + */ + @ParameterizedTest + @MethodSource("testData") + public void testAccessRule(String rules, String systemIds, boolean permitted) + throws Exception { + String[] accessRules = rules.split(";"); + for (String rule : accessRules) { + AccessRule accessRule = new AccessRule(rule.trim()); + String[] ids = systemIds.split(";"); + for (String systemId : ids) { + assertEquals(accessRule.allows(URI.create(systemId.trim())), permitted); + } + } + } + + /** + * Verifies that the specified rule is invalid. + * @param rule indicates whether there is a custom resolver + * @param expectedType the expected throw type + * @throws Exception if the test fails other than the expected Exception, which + * would indicate an issue in configuring the test + */ + @ParameterizedTest + @MethodSource("testInvalidInput") + public void testAccessRule(String rule, Class expectedType) throws Exception { + assertThrows(expectedType, () -> parseRules(rule)); + } + + /** + * Attempts to parse one or a semi-colon separated list of access rules. + * @param rule the access rule(s) + + * @throws Exception if error occurs while attempting to create an AccessRule. + */ + private void parseRules(String rule) { + String[] rules = rule.split(";"); + for (String rule1 : rules) { + System.out.println("Test invalid rule: [" + rule1 + "]"); + AccessRule accessRule = new AccessRule(rule1.trim()); + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java new file mode 100644 index 00000000000..7b6c0423209 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/AccessTestBase.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import common.util.TestBase; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.SAXParser; +import javax.xml.stream.XMLInputFactory; +import javax.xml.transform.TransformerFactory; +import javax.xml.validation.SchemaFactory; + +/** + * Test base for Access Rule test. + */ +public class AccessTestBase extends TestBase { + /* + * Test scenarios for verifying preferences. + * + * Fields: + * file, FSP, state of setting, config file, system property, api property, + * Custom Catalog, error expected, error code or expected result + */ + public Object[][] getConfigs(Processor processor) { + // file with an external DTD that's not in JdkCatalog + String fileDTDNotInC = "properties1.xml"; + // file with an external DTD that's in the Custom Catalog + String fileDTDInCC = "test.xml"; + // file with an external DTD that's in JdkCatalog + String javaDTD = "properties.xml"; + + // error code when CATALOG=strict; The cause for DOM + String errCode = "JAXP09040001"; + + // error (not from catalog) is expect when CATALOG=continue + boolean isErrExpected = true; + String expected1 = UNKNOWN_HOST; + + // expected when reference is resolved by Catalog + String expected2 = ""; + switch (processor) { + case SAX: + errCode = "JAXP00090001"; + break; + case STAX: + errCode = "JAXP00090001"; + // StAX is non-validating parser + isErrExpected = false; + expected1 = ".*[\\w\\s]*(value1)[\\w\\s]*.*"; + expected2 = ".*(123)[\\w\\s]*.*"; + break; + default: + break; + } + + return new Object[][]{ + /** + * Case 1: all properties must permit + * Note: External reference not in the built-in catalog + * Expect: error as the parser continues and tries to access an invalid site + * java.net.UnknownHostException: invalid.site.com + */ + /** + * Case 1-1: by default, both RESOURCE_ACCESS and EAP allow access + */ + {fileDTDNotInC, null, null, null, null, null, null, isErrExpected, expected1}, + /** + * Case 1-2: FSP is set, both RESOURCE_ACCESS and EAP are set to allow + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, isErrExpected, expected1}, + /** + * Case 2: access is denied unless all properties permit the operation + * + * Sample Error Message: [Fatal Error] properties1.xml:7:11: External DTD: Failed to read external DTD 'properties1.dtd', + * because access is not allowed due to restriction set by 'Resource Access (jdk.xml.resource.access) and http://javax.xml.XMLConstants/property/accessExternalDTD'. + * + */ + /** + * Case 2-1: FSP is set. RESOURCE_ACCESS is unchanged, and EAP denies access + + * Expect: [Fatal Error] access denied by ACCESS_EXTERNAL_DTD + */ + {fileDTDNotInC, Properties.FSP, null, null, null, null, null, true, XMLConstants.ACCESS_EXTERNAL_DTD}, + /** + * Case 2-2: RESOURCE_ACCESS is set to deny access though EAP allows it + + * Expect: [Fatal Error] access denied by RESOURCE_ACCESS + */ + {fileDTDNotInC, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS1, Properties.AED0}, null, true, SP_ACCESS}, + /** + * Case 2-3: ACCESS_EXTERNAL_DTD is set to deny access though RESOURCE_ACCESS allows it + + * Expect: [Fatal Error] access denied by ACCESS_EXTERNAL_DTD + */ + {fileDTDNotInC, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED2}, null, true, XMLConstants.ACCESS_EXTERNAL_DTD}, + /** + * Case 2-4: System properties override FSP secure values. + * + * Expect: error as the parser continues and tries to access an invalid site + */ + {fileDTDNotInC, Properties.FSP, PropertyState.SYSTEM, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, null, isErrExpected, expected1}, + /** + * Case 2-5: API property setting overrides system property setting. + * + * Expect: error as the parser continues and tries to access an invalid site + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS1, Properties.AED0}, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, isErrExpected, expected1}, + /** + * Case 2-6: API property setting overrides system property setting. + * + * Expect: [Fatal Error] access denied by RESOURCE_ACCESS + */ + {fileDTDNotInC, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, new Properties[]{Properties.ACCESS1, Properties.AED0}, null, true, SP_ACCESS}, + + /** + * Case 3: Resolvers and Catalogs take precedence in the resource resolution process + * + * Sample Error Message when access is denied by Catalog's Resolve property: + * [Fatal Error] properties1.xml:7:11: JAXP00090001: The CatalogResolver is enabled with the catalog "JDKCatalog.xml", but a CatalogException is returned. + * org.xml.sax.SAXException: javax.xml.catalog.CatalogException: JAXP09040001: No match found for publicId 'null' and systemId 'http://invalid.site.com/dtd/properties1.dtd'. + * + */ + /** + * Case 3-1: the built-in catalog's Resolve property is set to "strict", both Resource Access (jdk.xml.resource.access) and External Access Properties (EAPs) have no effect, + * regardless of their configured values + * + * Expect: error as access is denied by the Catalog's Resolve property + */ + {fileDTDNotInC, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, null, new Properties[]{Properties.ACCESS0, Properties.AED0}, null, true, errCode}, + /** + * Case 3-2: the reference is resolved by the built-in catalog before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {javaDTD, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, null, false, expected1}, + /** + * Case 3-3: the reference is resolved by a custom catalog before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {fileDTDInCC, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected2} + }; + } + + /* + * Test scenarios for XInclude. XInclude direct fetches are controlled by + * RESOURCE_ACCESS, while resolver/catalog results take precedence. + */ + public Object[][] getXIncludeConfigs(Processor processor) { + String xinclude = "XI_roottest.xml"; + + return new Object[][]{ + /** + * Case 1-1: by default, RESOURCE_ACCESS allows direct XInclude access. + */ + {xinclude, null, null, null, null, null, null, false, ""}, + /** + * Case 1-2: FSP is set, RESOURCE_ACCESS is explicitly set to allow. + */ + {xinclude, Properties.FSP, PropertyState.API, null, null, + new Properties[]{Properties.ACCESS0}, null, false, ""}, + /** + * Case 2-1: RESOURCE_ACCESS is set to deny direct XInclude access. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xinclude, Properties.FSP, PropertyState.API, null, null, + new Properties[]{Properties.ACCESS1}, null, true, SP_ACCESS}, + /** + * Case 2-2: system properties override FSP. + */ + {xinclude, Properties.FSP, PropertyState.SYSTEM, null, + new Properties[]{Properties.ACCESS0}, null, null, false, ""}, + /** + * Case 2-3: API property setting overrides system property setting. + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, + new Properties[]{Properties.ACCESS1}, + new Properties[]{Properties.ACCESS0}, null, false, ""}, + /** + * Case 2-4: API property setting overrides system property setting. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, + new Properties[]{Properties.ACCESS0}, + new Properties[]{Properties.ACCESS1}, null, true, SP_ACCESS}, + /** + * Case 3-1: custom catalog resolves the reference before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {xinclude, Properties.FSP, PropertyState.CONFIG_FILE, + Properties.CONFIG_FILE_CATALOG_STRICT, null, null, + CustomCatalog.STRICT, false, ""}, + }; + } + + /* + * Test scenarios for configuring properties for validation or transform. + * + * Fields: + * xml file, xsd or xsl file, FSP, state of setting, config file, system property, + * api property, Custom Catalog, error expected, error code or expected result + */ + public Object[][] getConfig(String m) { + // Schema Import + String xmlFile = "XSDImport_company.xsd"; + String xsdOrXsl = null; + String expected = ""; + String errCode = "JAXP00090001"; + Properties eapAllow = Properties.AES0; + Properties eapDeny = Properties.AES2; + String eapName = "accessExternalDTD,accessExternalSchema"; + + switch (m) { + case "SchemaTest2": + // Schema Include + xmlFile = "XSDInclude_company.xsd"; + break; + case "Validation": + // Schema Location + xmlFile = "val_test.xml"; + break; + case "Stylesheet": + errCode = "JAXP09040001"; + xmlFile = "XSLDTD.xsl"; + eapAllow = Properties.AED0; + eapDeny = Properties.AED2; + eapName = XMLConstants.ACCESS_EXTERNAL_DTD; + break; + case "Transform": + xmlFile = "XSLPI.xml"; + errCode = "JAXP00090001"; + xsdOrXsl = "" + + "" + + "" + + "" + + "" + + "" + + " "; + eapAllow = Properties.AEX0; + eapDeny = Properties.AEX2; + eapName = XMLConstants.ACCESS_EXTERNAL_STYLESHEET; + break; + default: + break; + } + + return new Object[][]{ + /** + * Case 1: all properties must permit. + * + * Case 1-1: by default, both RESOURCE_ACCESS and EAP allow access. + */ + {xmlFile, xsdOrXsl, null, null, null, null, null, null, false, expected}, + /** + * Case 1-2: FSP is set, both RESOURCE_ACCESS and EAP are set to allow. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, false, expected}, + /** + * Case 2: access is denied unless all properties permit the operation. + * + * Case 2-1: FSP is set. RESOURCE_ACCESS is unchanged, and EAP denies access. + * + * Expect: access denied by EAP + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE, null, null, null, null, true, eapName}, + /** + * Case 2-2: RESOURCE_ACCESS is set to deny access though EAP allows it. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, null, true, SP_ACCESS}, + /** + * Case 2-3: EAP is set to deny access though RESOURCE_ACCESS allows it. + * + * Expect: access denied by EAP + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.API, null, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapDeny}, null, true, eapName}, + /** + * Case 2-4: system properties override FSP secure values. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.SYSTEM, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, null, false, expected}, + /** + * Case 2-5: API property setting overrides system property setting. + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, false, expected}, + /** + * Case 2-6: API property setting overrides system property setting. + * + * Expect: access denied by RESOURCE_ACCESS + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE_SYSTEM_API, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, new Properties[]{Properties.ACCESS1, Properties.AED0, eapAllow}, null, true, SP_ACCESS}, + /** + * Case 3: Catalogs take precedence in the resource resolution process. + * + * Case 3-1: the built-in catalog's Resolve property is set to "strict", + * and the reference is not in the built-in catalog. + * + * Expect: error as access is denied by the Catalog's Resolve property + */ + {xmlFile, xsdOrXsl, null, PropertyState.CONFIG_FILE_SYSTEM_API, Properties.CONFIG_FILE_CATALOG_STRICT, null, new Properties[]{Properties.ACCESS0, Properties.AED0, eapAllow}, null, true, errCode}, + /** + * Case 3-2: custom catalog resolves the reference before direct fetch. + * FSP sets EAP to deny direct fetch, but catalog resolution completes first. + * + * Expect: no error + */ + {xmlFile, xsdOrXsl, Properties.FSP, PropertyState.CONFIG_FILE, Properties.CONFIG_FILE_CATALOG_STRICT, null, null, CustomCatalog.STRICT, false, expected}, + }; + } + + public void testDOM(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + DocumentBuilderFactory dbf = getDBF(fsp, state, config, sysProp, apiProp, cc); + process(filename, dbf, expectError, error); + } + + public void testSAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SAXParser parser = getSAXParser(fsp, state, config, sysProp, apiProp, cc); + process(filename, parser, expectError, error); + } + + public void testStAX(String filename, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + XMLInputFactory xif = getXMLInputFactory(state, config, sysProp, apiProp, cc); + process(filename, xif, expectError, error); + } + + public void testSchema1(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, sf, expectError, error); + } + + public void testSchema2(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + } + + public void testValidation(String filename, String xsd, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + SchemaFactory sf = getSchemaFactory(fsp, state, config, sysProp, apiProp, cc); + validate(filename, sf, expectError, error); + } + + public void testStylesheet(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + process(filename, tf, expectError, error); + } + + public void testTransform(String filename, String xsl, Properties fsp, PropertyState state, + Properties config, Properties[] sysProp, Properties[] apiProp, CustomCatalog cc, + boolean expectError, String error) throws Exception { + + TransformerFactory tf = getTransformerFactory(fsp, state, config, sysProp, apiProp, cc); + transform(filename, xsl, tf, expectError, error); + } + + // parameters in the same order as the test method + String filename; String xsd; String xsl; Properties fsp; PropertyState state; + Properties config; Properties[] sysProp; Properties[] apiProp; CustomCatalog cc; + boolean expectError; String error; + + // Maps the scenario array to individual parameters + public void paramMap(Processor processor, String method, String index) { + int i = 0; + Object[][] params; + if (processor == Processor.VALIDATOR || + processor == Processor.TRANSFORMER) { + params = getConfig(method); + i = 1; + } else { + params = getConfigs(processor); + } + Object[] param = params[Integer.parseInt(index)]; + filename = (String)param[0]; + if (processor == Processor.VALIDATOR) { + xsd = (String)param[i]; + } else if (processor == Processor.TRANSFORMER) { + xsl = (String)param[i]; + } + fsp = (Properties)param[i + 1]; + state = (PropertyState)param[i + 2]; + config = (Properties)param[i + 3]; + sysProp = (Properties[])param[i + 4]; + apiProp = (Properties[])param[i + 5]; + cc = (CustomCatalog)param[i + 6]; + expectError = (boolean)param[i + 7]; + error = (String)param[i + 8]; + } + + public void paramMapXInclude(Processor processor, String index) { + Object[] param = getXIncludeConfigs(processor)[Integer.parseInt(index)]; + filename = (String)param[0]; + fsp = (Properties)param[1]; + state = (PropertyState)param[2]; + config = (Properties)param[3]; + sysProp = (Properties[])param[4]; + apiProp = (Properties[])param[5]; + cc = (CustomCatalog)param[6]; + expectError = (boolean)param[7]; + error = (String)param[8]; + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java new file mode 100644 index 00000000000..e0223f93459 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/DOMTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.DOMTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.DOMTest 1 // FSP is set, both RESOURCE_ACCESS and EAP are set to allow + * @run driver common.access.DOMTest 2 // FSP is set, RESOURCE_ACCESS is unchanged, EAP denies access + * @run driver common.access.DOMTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.DOMTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.DOMTest 5 // system properties override FSP secure values + * @run driver common.access.DOMTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.DOMTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.DOMTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.DOMTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.DOMTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class DOMTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new DOMTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } + } + + public void run(String index) throws Exception { + paramMap(Processor.DOM, null, index); + super.testDOM(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java new file mode 100644 index 00000000000..d17852b895b --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/SAXTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import common.util.TestBase; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.SAXTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.SAXTest 1 // FSP is set, both RESOURCE_ACCESS and EAP are set to allow + * @run driver common.access.SAXTest 2 // FSP is set, RESOURCE_ACCESS is unchanged, EAP denies access + * @run driver common.access.SAXTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.SAXTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SAXTest 5 // system properties override FSP secure values + * @run driver common.access.SAXTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SAXTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SAXTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.SAXTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.SAXTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class SAXTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new SAXTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } + } + + public void run(String index) throws Exception { + paramMap(TestBase.Processor.SAX, null, index); + super.testSAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java new file mode 100644 index 00000000000..e984bc16639 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/SchemaTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +import java.net.ProxySelector; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.SchemaTest SchemaTest1 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest SchemaTest1 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest SchemaTest1 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest SchemaTest1 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest SchemaTest1 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest SchemaTest1 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest SchemaTest1 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest SchemaTest1 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest SchemaTest1 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest SchemaTest1 9 // custom catalog resolves before direct fetch + * @run driver common.access.SchemaTest SchemaTest2 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest SchemaTest2 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest SchemaTest2 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest SchemaTest2 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest SchemaTest2 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest SchemaTest2 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest SchemaTest2 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest SchemaTest2 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest SchemaTest2 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest SchemaTest2 9 // custom catalog resolves before direct fetch + * @run driver common.access.SchemaTest Validation 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow access + * @run driver common.access.SchemaTest Validation 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_SCHEMA allow + * @run driver common.access.SchemaTest Validation 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_SCHEMA denies + * @run driver common.access.SchemaTest Validation 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_SCHEMA allows it + * @run driver common.access.SchemaTest Validation 4 // ACCESS_EXTERNAL_SCHEMA denies access though RESOURCE_ACCESS allows it + * @run driver common.access.SchemaTest Validation 5 // system properties override FSP secure values + * @run driver common.access.SchemaTest Validation 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.SchemaTest Validation 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.SchemaTest Validation 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.SchemaTest Validation 9 // custom catalog resolves before direct fetch + * @summary verifies Schema and Validation support for Resource Access, ACCESS_EXTERNAL_SCHEMA, and catalog precedence. + */ +public class SchemaTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new SchemaTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.VALIDATOR, method, index); + switch (method) { + case "SchemaTest1": + super.testSchema1(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "SchemaTest2": + super.testSchema2(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Validation": + super.testValidation(filename, xsd, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java new file mode 100644 index 00000000000..65b73ee03ed --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/StAXTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + + +import common.util.TestBase; + +import java.net.ProxySelector; + +/** + * @test @bug 8357394 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.StAXTest 0 // by default, both RESOURCE_ACCESS and EAP allow access + * @run driver common.access.StAXTest 3 // RESOURCE_ACCESS denies access though EAP allows it + * @run driver common.access.StAXTest 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.StAXTest 5 // system properties override FSP secure values + * @run driver common.access.StAXTest 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.StAXTest 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.StAXTest 8 // the built-in catalog's Resolve property is strict + * @run driver common.access.StAXTest 9 // the built-in catalog resolves the reference before direct fetch + * @run driver common.access.StAXTest 10 // the custom catalog resolves the reference before direct fetch + * @summary Tests the interaction between Resource Access (jdk.xml.resource.access), + * External Access Properties (EAPs), and the built-in catalog's Resolve setting, + * ensuring correct precedence and behavior across different combinations of these + * configuration mechanisms + */ +public class StAXTest extends AccessTestBase { + public static void main(String[] args) throws Exception { + new StAXTest().run(args[0]); + } + + public void run(String index) throws Exception { + paramMap(TestBase.Processor.STAX, null, index); + super.testStAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java new file mode 100644 index 00000000000..38a5ec18b72 --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/TransformTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.TransformTest Stylesheet 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_DTD allow access + * @run driver common.access.TransformTest Stylesheet 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_DTD allow + * @run driver common.access.TransformTest Stylesheet 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_DTD denies + * @run driver common.access.TransformTest Stylesheet 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_DTD allows it + * @run driver common.access.TransformTest Stylesheet 4 // ACCESS_EXTERNAL_DTD denies access though RESOURCE_ACCESS allows it + * @run driver common.access.TransformTest Stylesheet 5 // system properties override FSP secure values + * @run driver common.access.TransformTest Stylesheet 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.TransformTest Stylesheet 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.TransformTest Stylesheet 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.TransformTest Stylesheet 9 // custom catalog resolves before direct fetch + * @run driver common.access.TransformTest Transform 0 // default RESOURCE_ACCESS and ACCESS_EXTERNAL_STYLESHEET allow access + * @run driver common.access.TransformTest Transform 1 // FSP with explicit RESOURCE_ACCESS and ACCESS_EXTERNAL_STYLESHEET allow + * @run driver common.access.TransformTest Transform 2 // FSP leaves RESOURCE_ACCESS unchanged, ACCESS_EXTERNAL_STYLESHEET denies + * @run driver common.access.TransformTest Transform 3 // RESOURCE_ACCESS denies access though ACCESS_EXTERNAL_STYLESHEET allows it + * @run driver common.access.TransformTest Transform 4 // ACCESS_EXTERNAL_STYLESHEET denies access though RESOURCE_ACCESS allows it + * @run driver common.access.TransformTest Transform 5 // system properties override FSP secure values + * @run driver common.access.TransformTest Transform 6 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.TransformTest Transform 7 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.TransformTest Transform 8 // built-in catalog resolve=strict denies unresolved reference + * @run driver common.access.TransformTest Transform 9 // custom catalog resolves before direct fetch + * @summary verifies Transform support for Resource Access, external access properties, and catalog precedence. + */ +public class TransformTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new TransformTest().run(args[0], args[1]); + } + + public void run(String method, String index) throws Exception { + paramMap(Processor.TRANSFORMER, method, index); + switch (method) { + case "Stylesheet": + super.testStylesheet(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case "Transform": + super.testTransform(filename, xsl, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java new file mode 100644 index 00000000000..47b5aa6e85a --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/access/XIncludeTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package common.access; + +/** + * @test @bug 8306632 + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @modules java.xml/jdk.xml.internal + * @run driver common.access.XIncludeTest DOM 0 // default RESOURCE_ACCESS allows direct XInclude access + * @run driver common.access.XIncludeTest DOM 1 // FSP with explicit RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest DOM 2 // RESOURCE_ACCESS denies direct XInclude access + * @run driver common.access.XIncludeTest DOM 3 // system properties override FSP + * @run driver common.access.XIncludeTest DOM 4 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.XIncludeTest DOM 5 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest DOM 6 // custom catalog resolves before direct fetch + * @run driver common.access.XIncludeTest SAX 0 // default RESOURCE_ACCESS allows direct XInclude access + * @run driver common.access.XIncludeTest SAX 1 // FSP with explicit RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest SAX 2 // RESOURCE_ACCESS denies direct XInclude access + * @run driver common.access.XIncludeTest SAX 3 // system properties override FSP + * @run driver common.access.XIncludeTest SAX 4 // API RESOURCE_ACCESS allow overrides system RESOURCE_ACCESS deny + * @run driver common.access.XIncludeTest SAX 5 // API RESOURCE_ACCESS deny overrides system RESOURCE_ACCESS allow + * @run driver common.access.XIncludeTest SAX 6 // custom catalog resolves before direct fetch + * @summary verifies XInclude support for Resource Access and catalog precedence. + */ +public class XIncludeTest extends AccessTestBase { + + public static void main(String args[]) throws Exception { + new XIncludeTest().run(args[0], args[1]); + } + + public void run(String processor, String index) throws Exception { + Processor p = Processor.valueOf(processor); + paramMapXInclude(p, index); + switch (p) { + case DOM: + super.testDOM(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + case SAX: + super.testSAX(filename, fsp, state, config, sysProp, apiProp, cc, expectError, error); + break; + default: + throw new IllegalArgumentException("Unsupported processor: " + processor); + } + } +} diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml index 216b3b81543..f1912ed877a 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/testcatalog/TestCatalog.xml @@ -18,6 +18,9 @@ + + + @@ -26,7 +29,7 @@ - diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java index b37b7da4e9a..81ac14ce9e9 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DTDTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package common.dtd; @@ -12,7 +12,7 @@ import common.util.TestBase; /** - * @bug 8306632 + * @bug 8306632 8357394 * @summary tests the DTD property jdk.xml.dtd.support. * The DTD property controls how DTDs are processed. */ @@ -126,7 +126,7 @@ public Object[][] getConfigs(Processor processor) { * Expect: error as the parser processes DTD and tries to access the invalid site * Error: JAXP00010008 java.net.UnknownHostException: invalid.site.com */ - {fileDTDNotInC, null, null, null, null, null, isErrExpected, expected}, + {fileDTDNotInC, null, null, null, null, null, isErrExpected, expected1}, /** * Case 1-2: DTD=deny in config file diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java index 77672609147..b6c54115e9b 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/util/TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,8 +28,8 @@ import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; +import java.nio.file.Path; import java.util.Objects; -import java.util.regex.Pattern; import javax.xml.XMLConstants; import javax.xml.catalog.CatalogFeatures; import javax.xml.parsers.DocumentBuilder; @@ -44,6 +44,7 @@ import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Source; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; @@ -53,8 +54,9 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.w3c.dom.Document; + import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** @@ -90,8 +92,8 @@ public class TestBase { private static final String CONFIG_FILE = "java.xml.config.file"; // CATALOG Abbreviation: C - static final String C_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); - static final String C_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName(); + public static final String C_FILE = CatalogFeatures.Feature.FILES.getPropertyName(); + public static final String C_RESOLVE = CatalogFeatures.Feature.RESOLVE.getPropertyName(); // Xerces Property public static final String DISALLOW_DTD = "http://apache.org/xml/features/disallow-doctype-decl"; @@ -106,15 +108,31 @@ public class TestBase { public static final String SP_CATALOG = "jdk.xml.jdkcatalog.resolve"; public static final String OVERRIDE_PARSER = "jdk.xml.overrideDefaultParser"; + //System Properties corresponding to ACCESS_EXTERNAL_* properties + public static final String SP_ACCESS_EXTERNAL_STYLESHEET = "javax.xml.accessExternalStylesheet"; + public static final String SP_ACCESS_EXTERNAL_DTD = "javax.xml.accessExternalDTD"; + public static final String SP_ACCESS_EXTERNAL_SCHEMA = "javax.xml.accessExternalSchema"; + //Values for the ACCESS_EXTERNAL_* properties + public static final String ACCESS_EXTERNAL_ALL = "all"; + public static final String ACCESS_EXTERNAL_NONE = ""; + + // JDK 27 + public static final String SP_ACCESS = "jdk.xml.resource.access"; + // DTD/CATALOG constants public static final String RESOLVE_CONTINUE = "continue"; public static final String RESOLVE_IGNORE = "ignore"; public static final String RESOLVE_STRICT = "strict"; + public static final String RESOLVE_STRICT_NONLOCAL = "strict:non-local"; public static final String DTD_ALLOW = "allow"; public static final String DTD_IGNORE = "ignore"; public static final String DTD_DENY = "deny"; + // resource access constants + public static final String ACCESS_ALLOW = "*"; + public static final String ACCESS_DENY = ""; + // JAXP Configuration File(JCF) location // DTD = deny public static final String JCF_DTD2 = "dtd2.properties"; @@ -126,6 +144,8 @@ public class TestBase { public static final String CONFIG_DEFAULT = "jaxp.properties"; public static final String CONFIG_STRICT = "jaxp-strict.properties"; public static final String CONFIG_TEMPLATE_STRICT = "jaxp-strict.properties.template"; + public static final String CONFIG_COMPAT = "jaxp-compat.properties"; + public static final String CONFIG_TEMPLATE_COMPAT = "jaxp-compat.properties.template"; public static final String UNKNOWN_HOST = "invalid.site.com"; @@ -150,6 +170,16 @@ public static enum Properties { CATALOG0(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_CONTINUE), CATALOG1(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_IGNORE), CATALOG2(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_STRICT), + CATALOG_NONLOCAL(SP_CATALOG, "ditto", Type.PROPERTY, RESOLVE_STRICT_NONLOCAL), + AED0(XMLConstants.ACCESS_EXTERNAL_DTD, SP_ACCESS_EXTERNAL_DTD, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AED2(XMLConstants.ACCESS_EXTERNAL_DTD, SP_ACCESS_EXTERNAL_DTD, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + AES0(XMLConstants.ACCESS_EXTERNAL_SCHEMA, SP_ACCESS_EXTERNAL_SCHEMA, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AES2(XMLConstants.ACCESS_EXTERNAL_SCHEMA, SP_ACCESS_EXTERNAL_SCHEMA, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + AEX0(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, SP_ACCESS_EXTERNAL_STYLESHEET, Type.PROPERTY, ACCESS_EXTERNAL_ALL), + AEX2(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, SP_ACCESS_EXTERNAL_STYLESHEET, Type.PROPERTY, ACCESS_EXTERNAL_NONE), + + ACCESS0(SP_ACCESS, "ditto", Type.PROPERTY, ACCESS_ALLOW), + ACCESS1(SP_ACCESS, "ditto", Type.PROPERTY, ACCESS_DENY), // StAX properties SUPPORT_DTD(XMLInputFactory.SUPPORT_DTD, null, Type.FEATURE, "true"), @@ -243,10 +273,21 @@ protected void process(String filename, DocumentBuilderFactory dbf, boolean expe String error) throws Exception { //dbf.setAttribute(CatalogFeatures.Feature.RESOLVE.getPropertyName(), "continue"); DocumentBuilder builder = dbf.newDocumentBuilder(); + builder.setEntityResolver((String publicId, String systemId) -> { + InputSource dtd = null; + InputStream is = null; + if ("http://foo.bar/dtd/test.dtd".equals(systemId)) { + is = getClass().getResourceAsStream("/local/store/path/test.dtd"); + } + if (is != null) { + dtd = new InputSource(is); + } + return dtd; + }); File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { - Document document = builder.parse(file); - Assert.assertTrue(!expectError); + builder.parse(file); + processResult(expectError, error); } catch (Exception e) { e.printStackTrace(); processError(expectError, error, e); @@ -259,7 +300,7 @@ protected void process(String filename, SAXParser parser, boolean expectError, File file = new File(getPath(TEST_SOURCE_DIR, filename)); try { parser.parse(file, new DefaultHandler()); - Assert.assertTrue(!expectError); + processResult(expectError, error); } catch (Exception e) { //e.printStackTrace(); processError(expectError, error, e); @@ -275,8 +316,7 @@ protected void process(String filename, XMLInputFactory xif, boolean expectError XMLStreamReader streamReader = xif.createXMLStreamReader(xml, entityxml); String text = getText(streamReader, XMLStreamConstants.CHARACTERS); System.out.println("Text: [" + text.trim() + "]"); - Assert.assertTrue(Pattern.matches(expected, text.trim())); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { e.printStackTrace(); processError(expectError, expected, e); @@ -289,8 +329,8 @@ protected void process(String filename, SchemaFactory sf, boolean expectError, String xsd = getPath(TEST_SOURCE_DIR, filename); try { Schema schema = sf.newSchema(new StreamSource(new File(xsd))); - Assert.assertTrue(!expectError); - } catch (Exception e) { + processResult(expectError, expected); + } catch (SAXException e) { e.printStackTrace(); processError(expectError, expected, e); } @@ -298,13 +338,13 @@ protected void process(String filename, SchemaFactory sf, boolean expectError, protected void process(String filename, TransformerFactory tf, boolean expectError, String expected) throws Exception { - String xsl = getPath(TEST_SOURCE_DIR, filename); + String xsl = getSystemId(TEST_SOURCE_DIR, filename); try { SAXSource xslSource = new SAXSource(new InputSource(xsl)); xslSource.setSystemId(xsl); Transformer transformer = tf.newTransformer(xslSource); - Assert.assertTrue(!expectError); - } catch (Exception e) { + processResult(expectError, expected); + } catch (TransformerConfigurationException e) { //e.printStackTrace(); processError(expectError, expected, e); } @@ -312,15 +352,14 @@ protected void process(String filename, TransformerFactory tf, boolean expectErr protected void transform(String xmlFile, String xsl, TransformerFactory tf, boolean expectError, String expected) throws Exception { - String xmlSysId = getPath(TEST_SOURCE_DIR, xmlFile); + String xmlSysId = getSystemId(TEST_SOURCE_DIR, xmlFile); try { SAXSource xslSource = new SAXSource(new InputSource(new StringReader(xsl))); - //SAXSource xslSource = new SAXSource(new InputSource(xslSysId)); xslSource.setSystemId(xmlSysId); Transformer transformer = tf.newTransformer(xslSource); StringWriter sw = new StringWriter(); transformer.transform(getSource(SourceType.STREAM, xmlSysId), new StreamResult(sw)); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { processError(expectError, expected, e); } @@ -333,16 +372,23 @@ protected void validate(String filename, SchemaFactory sf, boolean expectError, Schema schema = sf.newSchema(); Validator validator = schema.newValidator(); validator.validate(new StreamSource(new File(xml))); - Assert.assertTrue(!expectError); + processResult(expectError, expected); } catch (Exception e) { e.printStackTrace(); processError(expectError, expected, e); } } + protected void processResult(boolean expectError, String expected) { + if (expectError) { + Assert.assertTrue(false, "Expected error, but processing succeeded."); + } + } + protected void processError(boolean expectError, String error, Exception e) throws Exception { String str = e.getMessage(); + String errorText = getErrorText(e); if (!expectError) { Assert.assertTrue(false, "Expected pass, but Exception is thrown " + str); } else { @@ -351,11 +397,33 @@ protected void processError(boolean expectError, String error, Exception e) if (UNKNOWN_HOST.equals(error)) { Assert.assertTrue((str != null) && str.equals(error)); } else { - Assert.assertTrue((str != null) && str.contains(error)); + String matched = null; + for (String err : error.split(",")) { + String trimmed = err.trim(); + if (!trimmed.isEmpty() && errorText.contains(trimmed)) { + matched = trimmed; + break; + } + } + if (matched == null) { + Assert.assertTrue(false,"Missing expected error code(s): " + error); + } + Assert.assertTrue(true, "Found expected error code: " + matched); } } } + private String getErrorText(Throwable t) { + StringBuilder sb = new StringBuilder(); + while (t != null) { + if (t.getMessage() != null) { + sb.append(t.getMessage()).append('\n'); + } + t = t.getCause(); + } + return sb.toString(); + } + /** * Returns a DocumentBuilderFactory with settings as specified. * @@ -667,11 +735,15 @@ protected void setSystemProperty1(Properties config, PropertyState state, Proper System.setProperty(property.spName, property.value); break; case CONFIG_FILE: - System.setProperty(CONFIG_FILE, config.value); + if (config != null) { + System.setProperty(CONFIG_FILE, config.value); + } break; case CONFIG_FILE_SYSTEM: case CONFIG_FILE_SYSTEM_API: - System.setProperty(CONFIG_FILE, config.value); + if (config != null) { + System.setProperty(CONFIG_FILE, config.value); + } if (property != null) { System.setProperty(property.spName, property.value); } @@ -714,7 +786,11 @@ protected void clearSystemProperty1(PropertyState m, Properties property) { } } - static String getPath(String base, String file) { + public static String getSystemId(String base, String file) { + return Path.of(base, file).toUri().toString(); + } + + public static String getPath(String base, String file) { String temp = base + file; if (IS_WINDOWS) { temp = "/" + temp; @@ -734,6 +810,8 @@ public static void assertTrue(boolean condition, String message) { } else { throw new RuntimeException("Expected true but was false. "); } + } else if (message != null && !message.isEmpty()) { + System.out.println("Passed: " + message); } } diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml new file mode 100644 index 00000000000..98155ece7bb --- /dev/null +++ b/test/jaxp/javax/xml/jaxp/unittest/common/xmlfiles/XI_simple.xml @@ -0,0 +1,2 @@ + +123 From 212b4009a75076836b8f6b87893665b449f0d994 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Thu, 1 Oct 2026 16:59:42 +0000 Subject: [PATCH 11/98] 8393348: Shenandoah: Use Event::log, not EventMark for simple messages Reviewed-by: shade, xpeng, ruili --- .../gc/shenandoah/shenandoahGenerationalControlThread.cpp | 5 +++-- src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 3f9d789123b..1c2571af208 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -787,8 +787,9 @@ void ShenandoahGenerationalControlThread::set_gc_mode(GCMode new_mode) { void ShenandoahGenerationalControlThread::set_gc_mode(MonitorLocker& ml, GCMode new_mode) { if (_gc_mode != new_mode) { - log_debug(gc, thread)("Transition from: %s to: %s", gc_mode_name(_gc_mode), gc_mode_name(new_mode)); - EventMark event("Control thread transition from: %s, to %s", gc_mode_name(_gc_mode), gc_mode_name(new_mode)); + FormatBuffer<> msg("Transition from: %s to: %s", gc_mode_name(_gc_mode), gc_mode_name(new_mode)); + log_debug(gc, thread)("%s", msg.buffer()); + Events::log(this, "%s", msg.buffer()); _gc_mode = new_mode; ml.notify_all(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 020c66aa1fe..be09113c3b5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -513,8 +513,9 @@ const char* ShenandoahOldGeneration::state_name(State state) { void ShenandoahOldGeneration::transition_to(State new_state) { if (_state != new_state) { - log_debug(gc, thread)("Old generation transition from %s to %s", state_name(_state), state_name(new_state)); - EventMark event("Old was %s, now is %s", state_name(_state), state_name(new_state)); + FormatBuffer<> msg("Old was %s, now is %s", state_name(_state), state_name(new_state)); + log_debug(gc, thread)("%s", msg.buffer()); + Events::log(Thread::current(), "%s", msg.buffer()); validate_transition(new_state); _state = new_state; } From b7704a5642dedd44e9da6860ede0df716d3c165d Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 1 Oct 2026 17:55:17 +0000 Subject: [PATCH 12/98] 8393319: ClassPathZipEntry::has_entry exposes unhandlized oops Reviewed-by: dholmes, iklam --- src/hotspot/share/classfile/classLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 15a8a0c576a..d27681e0b26 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -392,8 +392,8 @@ bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name, Handle assert(SystemDictionaryShared::is_builtin_loader(ClassLoaderData::class_loader_data(class_loader())), "must be"); JavaValue result(T_OBJECT); oop class_name_oop = java_lang_String::create_oop_from_str(name, current); - oop zip_name_oop = CDSProtectionDomain::to_file_URL(_zip_name, Handle(), current); Handle h_class_name(current, class_name_oop); + oop zip_name_oop = CDSProtectionDomain::to_file_URL(_zip_name, Handle(), current); Handle h_zip_name(current, zip_name_oop); // URL ClassLoader.getResource(String name) From 63fd3e87adfdbc7d32e145c882632d309d5c6235 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 1 Oct 2026 20:38:01 +0000 Subject: [PATCH 13/98] 8393155: Remove unused medialib code for Convolve edge mode MLIB_EDGE_SRC_EXTEND Reviewed-by: serb, jdv --- .../libawt/awt/medialib/awt_ImagingLib.c | 60 - .../native/libmlib_image/mlib_ImageConv.h | 70 - .../mlib_ImageConvCopyEdge_Bit.c | 353 ----- .../native/libmlib_image/mlib_ImageConvEdge.h | 24 - .../native/libmlib_image/mlib_ImageConvMxN.c | 53 - .../libmlib_image/mlib_ImageConvMxN_Fp.c | 570 ------- .../libmlib_image/mlib_ImageConvMxN_ext.c | 304 ---- .../libmlib_image/mlib_ImageConv_16ext.c | 1340 ---------------- .../libmlib_image/mlib_ImageConv_8ext.c | 1325 ---------------- .../libmlib_image/mlib_ImageConv_u16ext.c | 1348 ----------------- .../native/libmlib_image/mlib_c_ImageConv.h | 48 - .../libmlib_image/mlib_c_ImageConvCopyEdge.c | 165 -- .../native/libmlib_image/mlib_c_ImageConv_f.c | 28 - 13 files changed, 5688 deletions(-) delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_ImageConvCopyEdge_Bit.c delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c delete mode 100644 src/java.desktop/share/native/libmlib_image/mlib_c_ImageConvCopyEdge.c diff --git a/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c b/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c index b6e10617cc3..64a0579b90e 100644 --- a/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c +++ b/src/java.desktop/share/native/libawt/awt/medialib/awt_ImagingLib.c @@ -2133,36 +2133,6 @@ allocateArray(JNIEnv *env, BufImageS_t *imageP, /* increment width. Expanding top and left requires bumping */ /* around pointers and incrementing the width/height */ -#if 0 - if (0 && useEdges) { - baseWidth = rasterP->baseRasterWidth; - baseHeight = rasterP->baseRasterHeight; - baseXoff = rasterP->baseOriginX; - baseYoff = rasterP->baseOriginY; - - if (rasterP->minX + rasterP->width < baseXoff + baseWidth) { - /* Can use edge */ - width++; - } - if (rasterP->minY + rasterP->height < baseYoff + baseHeight) { - /* Can use edge */ - height++; - } - - if (rasterP->minX > baseXoff ) { - /* Can use edge */ - width++; - /* NEED TO BUMP POINTER BACK A PIXELSTRIDE */ - } - if (rasterP->minY > baseYoff) { - /* Can use edge */ - height++; - /* NEED TO BUMP POINTER BACK A SCANLINE */ - } - - - } -#endif if (cvtToDefault) { int status = 0; *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, 4, width, height); @@ -2317,36 +2287,6 @@ allocateRasterArray(JNIEnv *env, RasterS_t *rasterP, /* increment width. Expanding top and left requires bumping */ /* around pointers and incrementing the width/height */ -#if 0 - if (0 && useEdges) { - baseWidth = rasterP->baseRasterWidth; - baseHeight = rasterP->baseRasterHeight; - baseXoff = rasterP->baseOriginX; - baseYoff = rasterP->baseOriginY; - - if (rasterP->minX + rasterP->width < baseXoff + baseWidth) { - /* Can use edge */ - width++; - } - if (rasterP->minY + rasterP->height < baseYoff + baseHeight) { - /* Can use edge */ - height++; - } - - if (rasterP->minX > baseXoff ) { - /* Can use edge */ - width++; - /* NEED TO BUMP POINTER BACK A PIXELSTRIDE */ - } - if (rasterP->minY > baseYoff) { - /* Can use edge */ - height++; - /* NEED TO BUMP POINTER BACK A SCANLINE */ - } - - - } -#endif switch (rasterP->type) { case sun_awt_image_IntegerComponentRaster_TYPE_INT_8BIT_SAMPLES: if (!((rasterP->chanOffsets[0] == 0 || SAFE_TO_ALLOC_2(rasterP->chanOffsets[0], 4)) && diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv.h b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv.h index 2528fb1eae7..b3e7a2b3d5e 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv.h +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv.h @@ -39,18 +39,6 @@ if (k != akernel) mlib_free(k); \ return status #endif /* FREE_AND_RETURN_STATUS */ -mlib_status mlib_convMxNext_s32(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - mlib_status mlib_convMxNnw_d64(mlib_image *dst, const mlib_image *src, const mlib_d64 *ker, @@ -125,64 +113,6 @@ mlib_status mlib_convMxNnw_u8(mlib_image *dst, mlib_s32 scale, mlib_s32 cmask); -mlib_status mlib_convMxNext_u8(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kern, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - -mlib_status mlib_convMxNext_s16(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - -mlib_status mlib_convMxNext_u16(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - -mlib_status mlib_convMxNext_f32(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask); - -mlib_status mlib_convMxNext_d64(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvCopyEdge_Bit.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvCopyEdge_Bit.c deleted file mode 100644 index d9686db5ca7..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvCopyEdge_Bit.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTIONS - * mlib_ImageConvCopyEdge_Bit - Copy src edges to dst edges - * - * - * SYNOPSIS - * mlib_status mlib_ImageConvCopyEdge_Bit(mlib_image *dst, - * const mlib_image *src, - * mlib_s32 dx_l, - * mlib_32 dx_r, - * mlib_s32 dy_t, - * mlib_32 dy_b, - * mlib_s32 cmask); - * - * ARGUMENT - * dst Pointer to an dst image. - * src Pointer to an src image. - * dx_l Number of columns on the left side of the - * image to be copied. - * dx_r Number of columns on the right side of the - * image to be copied. - * dy_t Number of rows on the top edge of the - * image to be copied. - * dy_b Number of rows on the top edge of the - * image to be copied. - * cmask Channel mask to indicate the channels to be convolved. - * Each bit of which represents a channel in the image. The - * channels corresponded to 1 bits are those to be processed. - * - * RESTRICTION - * The src and the dst must be the MLIB_BIT type, same width, same height and have same number - * of channels (1). The unselected channels are not - * overwritten. If both src and dst have just one channel, - * cmask is ignored. - * - * DESCRIPTION - * Copy src edges to dst edges. - * - * The unselected channels are not overwritten. - * If src and dst have just one channel, - * cmask is ignored. - */ - -#include "mlib_image.h" -#include "mlib_ImageConvEdge.h" - -/***************************************************************/ -mlib_status mlib_ImageConvCopyEdge_Bit(mlib_image *dst, - const mlib_image *src, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - mlib_u8 *pdst = mlib_ImageGetData(dst), *pd; - mlib_u8 *psrc = mlib_ImageGetData(src), *ps; - mlib_s32 img_height = mlib_ImageGetHeight(dst); - mlib_s32 img_width = mlib_ImageGetWidth(dst); - mlib_s32 img_strided = mlib_ImageGetStride(dst); - mlib_s32 img_strides = mlib_ImageGetStride(src); - mlib_s32 bitoffd = mlib_ImageGetBitOffset(dst); - mlib_s32 bitoffs = mlib_ImageGetBitOffset(src); - mlib_s32 bitoff_end, test, shift1, shift2; - mlib_u32 s0, s1, tmp; - mlib_u8 mask, mask_end; - mlib_u8 tmp_start, tmp_end; - mlib_s32 i, j, amount; - - if (bitoffd == bitoffs) { - pd = pdst; - ps = psrc; - - if (dx_l > 0) { - if (bitoffd + dx_l <= 8) { - mask = (0xFF >> bitoffd) & (0xFF << ((8 - (bitoffd + dx_l)) & 7)); - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (ps[i*img_strides] & mask); - } - - } else { - mask = (0xFF >> bitoffd); - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (ps[i*img_strides] & mask); - } - - amount = (bitoffd + dx_l + 7) >> 3; - mask = (0xFF << ((8 - (bitoffd + dx_l)) & 7)); - - for (j = 1; j < amount - 1; j++) { - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided + j] = ps[i*img_strides + j]; - } - } - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided + amount - 1] = (pd[i*img_strided + amount - 1] & ~mask) | - (ps[i*img_strides + amount - 1] & mask); - } - } - } - - if (dx_r > 0) { - pd = pdst + (img_width + bitoffd - dx_r) / 8; - ps = psrc + (img_width + bitoffd - dx_r) / 8; - bitoffd = (img_width + bitoffd - dx_r) & 7; - - if (bitoffd + dx_r <= 8) { - mask = (0xFF >> bitoffd) & (0xFF << ((8 - (bitoffd + dx_r)) & 7)); - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (ps[i*img_strides] & mask); - } - - } else { - mask = (0xFF >> bitoffd); - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (ps[i*img_strides] & mask); - } - - amount = (bitoffd + dx_r + 7) >> 3; - mask = (0xFF << ((8 - (bitoffd + dx_r)) & 7)); - - for (j = 1; j < amount - 1; j++) { - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided + j] = ps[i*img_strides + j]; - } - } - - for (i = dy_t; i < (img_height - dy_b); i++) { - pd[i*img_strided + amount - 1] = (pd[i*img_strided + amount - 1] & ~mask) | - (ps[i*img_strides + amount - 1] & mask); - } - } - } - - bitoffd = mlib_ImageGetBitOffset(dst); - bitoff_end = (bitoffd + img_width) & 7; - amount = (bitoffd + img_width + 7) >> 3; - mask = (0xFF >> bitoffd); - mask_end = (0xFF << ((8 - bitoff_end) & 7)); - - pd = pdst; - ps = psrc; - - for (i = 0; i < dy_t; i++) { - tmp_start = pd[i*img_strided]; - tmp_end = pd[i*img_strided+amount-1]; - for (j = 0; j < amount; j++) { - pd[i*img_strided + j] = ps[i*img_strides + j]; - } - - pd[i*img_strided] = (tmp_start & (~mask)) | (pd[i*img_strided] & mask); - pd[i*img_strided+amount-1] = (tmp_end & (~mask_end)) | - (pd[i*img_strided+amount-1] & mask_end); - } - - pd = pdst + (img_height-1)*img_strided; - ps = psrc + (img_height-1)*img_strides; - - for (i = 0; i < dy_b; i++) { - tmp_start = pd[-i*img_strided]; - tmp_end = pd[-i*img_strided+amount-1]; - for (j = 0; j < amount; j++) { - pd[-i*img_strided + j] = ps[-i*img_strides + j]; - } - - pd[-i*img_strided] = (tmp_start & (~mask)) | (pd[-i*img_strided] & mask); - pd[-i*img_strided+amount-1] = (tmp_end & (~mask_end)) | - (pd[-i*img_strided+amount-1] & mask_end); - } - - } else { - pd = pdst; - - if (bitoffs > bitoffd) { - ps = psrc; - shift2 = (8 - (bitoffs - bitoffd)); - test = 0; - } else { - test = 1; - ps = psrc - 1; - shift2 = bitoffd - bitoffs; - } - - shift1 = 8 - shift2; - - if (dx_l > 0) { - if (bitoffd + dx_l <= 8) { - mask = (0xFF >> bitoffd) & (0xFF << ((8 - (bitoffd + dx_l)) & 7)); - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides]; - s1 = ps[i*img_strides+1]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (tmp & mask); - } - - } else { - mask = (0xFF >> bitoffd); - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides]; - s1 = ps[i*img_strides+1]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (tmp & mask); - } - - amount = (bitoffd + dx_l + 7) >> 3; - mask = (0xFF << ((8 - (bitoffd + dx_l)) & 7)); - - for (j = 1; j < amount - 1; j++) { - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides+j]; - s1 = ps[i*img_strides+j+1]; - pd[i*img_strided + j] = (s0 << shift1) | (s1 >> shift2); - s0 = s1; - } - } - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides+amount-1]; - s1 = ps[i*img_strides+amount]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided + amount - 1] = (pd[i*img_strided + amount - 1] & ~mask) | - (tmp & mask); - } - } - } - - if (dx_r > 0) { - pd = pdst + (img_width + bitoffd - dx_r) / 8; - ps = psrc + (img_width + bitoffd - dx_r) / 8; - bitoffd = (img_width + bitoffd - dx_r) & 7; - ps -= test; - - if (bitoffd + dx_r <= 8) { - mask = (0xFF >> bitoffd) & (0xFF << ((8 - (bitoffd + dx_r)) & 7)); - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides]; - s1 = ps[i*img_strides+1]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (tmp & mask); - } - - } else { - mask = (0xFF >> bitoffd); - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides]; - s1 = ps[i*img_strides+1]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided] = (pd[i*img_strided] & ~mask) | (tmp & mask); - } - - amount = (bitoffd + dx_r + 7) >> 3; - mask = (0xFF << ((8 - (bitoffd + dx_r)) & 7)); - - for (j = 1; j < amount - 1; j++) { - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides+j]; - s1 = ps[i*img_strides+j+1]; - pd[i*img_strided + j] = (s0 << shift1) | (s1 >> shift2); - } - } - - for (i = dy_t; i < (img_height - dy_b); i++) { - s0 = ps[i*img_strides+amount-1]; - s1 = ps[i*img_strides+amount]; - tmp = (s0 << shift1) | (s1 >> shift2); - pd[i*img_strided + amount - 1] = (pd[i*img_strided + amount - 1] & ~mask) | - (tmp & mask); - } - } - } - - bitoffd = mlib_ImageGetBitOffset(dst); - bitoff_end = (bitoffd + img_width) & 7; - amount = (bitoffd + img_width + 7) >> 3; - mask = (0xFF >> bitoffd); - mask_end = (0xFF << ((8 - bitoff_end) & 7)); - - pd = pdst; - ps = psrc-test; - - for (i = 0; i < dy_t; i++) { - tmp_start = pd[i*img_strided]; - tmp_end = pd[i*img_strided+amount-1]; - s0 = ps[i*img_strides]; - for (j = 0; j < amount; j++) { - s1 = ps[i*img_strides+j+1]; - pd[i*img_strided + j] = (s0 << shift1) | (s1 >> shift2); - s0 = s1; - } - - pd[i*img_strided] = (tmp_start & (~mask)) | (pd[i*img_strided] & mask); - pd[i*img_strided+amount-1] = (tmp_end & (~mask_end)) | - (pd[i*img_strided+amount-1] & mask_end); - } - - pd = pdst + (img_height-1)*img_strided; - ps = psrc + (img_height-1)*img_strides - test; - - for (i = 0; i < dy_b; i++) { - tmp_start = pd[-i*img_strided]; - tmp_end = pd[-i*img_strided+amount-1]; - s0 = ps[-i*img_strides]; - for (j = 0; j < amount; j++) { - s1 = ps[-i*img_strides+j+1]; - pd[-i*img_strided + j] = (s0 << shift1) | (s1 >> shift2); - s0 = s1; - } - - pd[-i*img_strided] = (tmp_start & (~mask)) | (pd[-i*img_strided] & mask); - pd[-i*img_strided+amount-1] = (tmp_end & (~mask_end)) | - (pd[-i*img_strided+amount-1] & mask_end); - } - } - - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvEdge.h b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvEdge.h index 86108443371..7aaabfcc20b 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvEdge.h +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvEdge.h @@ -62,30 +62,6 @@ mlib_status mlib_ImageConvZeroEdge(mlib_image *dst, mlib_s32 dy_b, mlib_s32 cmask); -mlib_status mlib_ImageConvCopyEdge_Bit(mlib_image *dst, - const mlib_image *src, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask); - -mlib_status mlib_ImageConvCopyEdge(mlib_image *dst, - const mlib_image *src, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask); - -mlib_status mlib_ImageConvCopyEdge_Fp(mlib_image *dst, - const mlib_image *src, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN.c index bdc0bb0372a..7723b3c6191 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN.c @@ -216,9 +216,6 @@ mlib_status mlib_ImageConvMxN_f(mlib_image *dst, case MLIB_EDGE_DST_FILL_ZERO: mlib_ImageConvZeroEdge(dst_e, dx_l, dx_r, dy_t, dy_b, cmask); break; - case MLIB_EDGE_DST_COPY_SRC: - mlib_ImageConvCopyEdge(dst_e, src_e, dx_l, dx_r, dy_t, dy_b, cmask); - break; default: /* Other edge conditions do not need additional handling. * Note also that they are not exposed in public Java API @@ -226,56 +223,6 @@ mlib_status mlib_ImageConvMxN_f(mlib_image *dst, break; } } - else { /* MLIB_EDGE_SRC_EXTEND */ - /* adjust src_e image */ - mlib_ImageSetSubimage(src_e, src_e, dx_l - dm, dy_t - dn, - mlib_ImageGetWidth(src_e), mlib_ImageGetHeight(src_e)); - - switch (type) { - case MLIB_BYTE: - ret = - mlib_convMxNext_u8(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, scale, - cmask); - break; - case MLIB_SHORT: - if (mlib_ImageConvVersion(m, n, scale, type) == 0) - ret = - mlib_convMxNext_s16(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, scale, - cmask); - else - ret = - mlib_i_convMxNext_s16(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, - scale, cmask); - break; - case MLIB_USHORT: - if (mlib_ImageConvVersion(m, n, scale, type) == 0) - ret = - mlib_convMxNext_u16(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, scale, - cmask); - else - ret = - mlib_i_convMxNext_u16(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, - scale, cmask); - break; - case MLIB_INT: - ret = - mlib_convMxNext_s32(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, scale, - cmask); - break; - case MLIB_FLOAT: - mlib_convMxNext_f32(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, cmask); - break; - case MLIB_DOUBLE: - mlib_convMxNext_d64(dst_e, src_e, kernel, m, n, dx_l, dx_r, dy_t, dy_b, cmask); - break; - default: - /* For some reasons, there is no convolution routine for type MLIB_BIT. - * For now, we silently ignore it (because this image type is not used by java), - * but probably we have to report an error. - */ - break; - } - } return ret; } diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c index bc2df9b0b1a..11d7e9d3387 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c @@ -78,60 +78,6 @@ #include "mlib_ImageConv.h" #include "safe_math.h" -/***************************************************************/ -static void mlib_ImageConvMxNMulAdd_F32(mlib_f32 *dst, - const mlib_f32 *src, - const mlib_d64 *kernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch); - -static void mlib_ImageConvMxNF322F32_ext(mlib_f32 *dst, - const mlib_f32 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r); - -static void mlib_ImageConvMxNMulAdd_D64(mlib_d64 *dst, - const mlib_d64 *src, - const mlib_d64 *kernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch); - -static void mlib_ImageConvMxND642D64_ext(mlib_d64 *dst, - const mlib_d64 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r); - -/***************************************************************/ -#if 0 -static void mlib_ImageConvMxNMulAdd2_F32(mlib_f32 *hdst, - mlib_f32 *vdst, - const mlib_f32 *src, - const mlib_d64 *hfilter, - const mlib_d64 *vfilter, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch); - -static void mlib_ImageConvMxNMulAdd2_D64(mlib_d64 *hdst, - mlib_d64 *vdst, - const mlib_d64 *src, - const mlib_d64 *hfilter, - const mlib_d64 *vfilter, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch); -#endif /* 0 */ - /***************************************************************/ mlib_status mlib_ImageConvMxN_Fp(mlib_image *dst, const mlib_image *src, @@ -155,519 +101,3 @@ mlib_status mlib_ImageConvMxN_Fp(mlib_image *dst, } /***************************************************************/ -void mlib_ImageConvMxNMulAdd_F32(mlib_f32 *dst, - const mlib_f32 *src, - const mlib_d64 *kernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch) -{ - mlib_f32 *hdst1 = dst + dnch; - mlib_s32 i, j; - - for (j = 0; j < m - 2; j += 3, src += 3 * nch, kernel += 3) { - const mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) kernel[0]; - mlib_f32 hval1 = (mlib_f32) kernel[1]; - mlib_f32 hval2 = (mlib_f32) kernel[2]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = dst[0]; - - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - hdvl0 += val1 * hval1; - hdvl0 += val2 * hval2; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - } - - if (j < m - 1) { - const mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) kernel[0]; - mlib_f32 hval1 = (mlib_f32) kernel[1]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = dst[0]; - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - hdvl0 += val1 * hval1; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - - } - else if (j < m) { - const mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) kernel[0]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = dst[0]; - - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - } -} - -/***************************************************************/ -void mlib_ImageConvMxNF322F32_ext(mlib_f32 *dst, - const mlib_f32 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r) -{ - mlib_s32 i; - mlib_f32 val = src[0]; - - for (i = 0; i < dx_l; i++) - dst[i] = val; - for (; i < n - dx_r; i++) - dst[i] = src[nch * (i - dx_l)]; - val = dst[n - dx_r - 1]; - for (; i < n; i++) - dst[i] = val; -} - -/***************************************************************/ -mlib_status mlib_convMxNext_f32(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - mlib_d64 dspace[1024], *dsa = dspace; - mlib_s32 wid_e = mlib_ImageGetWidth(src); - mlib_f32 *fsa; - mlib_f32 *da = mlib_ImageGetData(dst); - mlib_f32 *sa = mlib_ImageGetData(src); - mlib_s32 dlb = mlib_ImageGetStride(dst) >> 2; - mlib_s32 slb = mlib_ImageGetStride(src) >> 2; - mlib_s32 dw = mlib_ImageGetWidth(dst); - mlib_s32 dh = mlib_ImageGetHeight(dst); - mlib_s32 nch = mlib_ImageGetChannels(dst); - mlib_s32 i, j, j1, k; - - if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { - return MLIB_FAILURE; - } - if (3 * wid_e + m > 1024) { - dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); - - if (dsa == NULL) - return MLIB_FAILURE; - } - - fsa = (mlib_f32 *) dsa; - - for (j = 0; j < dh; j++, da += dlb) { - for (k = 0; k < nch; k++) - if (cmask & (1 << (nch - 1 - k))) { - const mlib_f32 *sa1 = sa + k; - mlib_f32 *da1 = da + k; - const mlib_d64 *kernel1 = kernel; - - for (i = 0; i < dw; i++) - da1[i * nch] = 0.f; - for (j1 = 0; j1 < n; j1++, kernel1 += m) { - mlib_ImageConvMxNF322F32_ext(fsa, sa1, dw + m - 1, nch, dx_l, dx_r); - mlib_ImageConvMxNMulAdd_F32(da1, fsa, kernel1, dw, m, 1, nch); - - if ((j + j1 >= dy_t) && (j + j1 < dh + n - dy_b - 2)) - sa1 += slb; - } - } - - if ((j >= dy_t) && (j < dh + n - dy_b - 2)) - sa += slb; - } - - if (dsa != dspace) - mlib_free(dsa); - return MLIB_SUCCESS; -} - -/***************************************************************/ -#if 0 - -void mlib_ImageConvMxNMulAdd2_F32(mlib_f32 *hdst, - mlib_f32 *vdst, - const mlib_f32 *src, - const mlib_d64 *hfilter, - const mlib_d64 *vfilter, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch) -{ - mlib_f32 *hdst1 = hdst + dnch, *vdst1 = vdst + dnch; - mlib_s32 i, j; - - for (j = 0; j < m - 2; j += 3, src += 3 * nch, hfilter += 3, vfilter += 3) { - mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) hfilter[0]; - mlib_f32 vval0 = (mlib_f32) vfilter[0]; - mlib_f32 hval1 = (mlib_f32) hfilter[1]; - mlib_f32 vval1 = (mlib_f32) vfilter[1]; - mlib_f32 hval2 = (mlib_f32) hfilter[2]; - mlib_f32 vval2 = (mlib_f32) vfilter[2]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = hdst[0]; - mlib_f32 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 vdvl0 = val0 * vval0 + vdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - hdvl0 += val1 * hval1; - vdvl0 += val1 * vval1; - hdvl0 += val2 * hval2; - vdvl0 += val2 * vval2; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - } - - if (j < m - 1) { - mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) hfilter[0]; - mlib_f32 vval0 = (mlib_f32) vfilter[0]; - mlib_f32 hval1 = (mlib_f32) hfilter[1]; - mlib_f32 vval1 = (mlib_f32) vfilter[1]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = hdst[0]; - mlib_f32 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 vdvl0 = val0 * vval0 + vdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - hdvl0 += val1 * hval1; - vdvl0 += val1 * vval1; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - - } - else if (j < m) { - mlib_f32 *src2 = src + 2 * nch; - mlib_f32 hval0 = (mlib_f32) hfilter[0]; - mlib_f32 vval0 = (mlib_f32) vfilter[0]; - mlib_f32 val0 = src[0]; - mlib_f32 val1 = src[nch]; - mlib_f32 hdvl = hdst[0]; - mlib_f32 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_f32 hdvl0 = val0 * hval0 + hdvl; - mlib_f32 vdvl0 = val0 * vval0 + vdvl; - mlib_f32 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - } -} - -/***************************************************************/ -void mlib_ImageConvMxNMulAdd2_D64(mlib_d64 *hdst, - mlib_d64 *vdst, - const mlib_d64 *src, - const mlib_d64 *hfilter, - const mlib_d64 *vfilter, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch) -{ - mlib_d64 *hdst1 = hdst + dnch, *vdst1 = vdst + dnch; - mlib_s32 i, j; - - for (j = 0; j < m - 2; j += 3, src += 3 * nch, hfilter += 3, vfilter += 3) { - mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = hfilter[0]; - mlib_d64 vval0 = vfilter[0]; - mlib_d64 hval1 = hfilter[1]; - mlib_d64 vval1 = vfilter[1]; - mlib_d64 hval2 = hfilter[2]; - mlib_d64 vval2 = vfilter[2]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = hdst[0]; - mlib_d64 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 vdvl0 = val0 * vval0 + vdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - hdvl0 += val1 * hval1; - vdvl0 += val1 * vval1; - hdvl0 += val2 * hval2; - vdvl0 += val2 * vval2; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - } - - if (j < m - 1) { - mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = hfilter[0]; - mlib_d64 vval0 = vfilter[0]; - mlib_d64 hval1 = hfilter[1]; - mlib_d64 vval1 = vfilter[1]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = hdst[0]; - mlib_d64 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 vdvl0 = val0 * vval0 + vdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - hdvl0 += val1 * hval1; - vdvl0 += val1 * vval1; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - - } - else if (j < m) { - mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = hfilter[0]; - mlib_d64 vval0 = vfilter[0]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = hdst[0]; - mlib_d64 vdvl = vdst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 vdvl0 = val0 * vval0 + vdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - vdvl = vdst1[i * dnch]; - val0 = val1; - val1 = val2; - - hdst[i * dnch] = hdvl0; - vdst[i * dnch] = vdvl0; - } - } -} - -#endif /* 0 */ - -/***************************************************************/ -void mlib_ImageConvMxNMulAdd_D64(mlib_d64 *dst, - const mlib_d64 *src, - const mlib_d64 *kernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch, - mlib_s32 dnch) -{ - mlib_d64 *hdst1 = dst + dnch; - mlib_s32 i, j; - - for (j = 0; j < m - 2; j += 3, src += 3 * nch, kernel += 3) { - const mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = kernel[0]; - mlib_d64 hval1 = kernel[1]; - mlib_d64 hval2 = kernel[2]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = dst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - hdvl0 += val1 * hval1; - hdvl0 += val2 * hval2; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - } - - if (j < m - 1) { - const mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = kernel[0]; - mlib_d64 hval1 = kernel[1]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = dst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - hdvl0 += val1 * hval1; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - - } - else if (j < m) { - const mlib_d64 *src2 = src + 2 * nch; - mlib_d64 hval0 = kernel[0]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 hdvl = dst[0]; - - for (i = 0; i < n; i++) { - mlib_d64 hdvl0 = val0 * hval0 + hdvl; - mlib_d64 val2 = src2[i * nch]; - - hdvl = hdst1[i * dnch]; - val0 = val1; - val1 = val2; - - dst[i * dnch] = hdvl0; - } - } -} - -/***************************************************************/ -void mlib_ImageConvMxND642D64_ext(mlib_d64 *dst, - const mlib_d64 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r) -{ - mlib_s32 i; - mlib_d64 val = src[0]; - - for (i = 0; i < dx_l; i++) - dst[i] = val; - for (; i < n - dx_r; i++) - dst[i] = src[nch * (i - dx_l)]; - val = dst[n - dx_r - 1]; - for (; i < n; i++) - dst[i] = val; -} - -/***************************************************************/ -mlib_status mlib_convMxNext_d64(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - mlib_d64 dspace[1024], *dsa = dspace; - mlib_s32 wid_e = mlib_ImageGetWidth(src); - mlib_d64 *da = mlib_ImageGetData(dst); - mlib_d64 *sa = mlib_ImageGetData(src); - mlib_s32 dlb = mlib_ImageGetStride(dst) >> 3; - mlib_s32 slb = mlib_ImageGetStride(src) >> 3; - mlib_s32 dw = mlib_ImageGetWidth(dst); - mlib_s32 dh = mlib_ImageGetHeight(dst); - mlib_s32 nch = mlib_ImageGetChannels(dst); - mlib_s32 i, j, j1, k; - - if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { - return MLIB_FAILURE; - } - if (3 * wid_e + m > 1024) { - dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); - - if (dsa == NULL) - return MLIB_FAILURE; - } - - for (j = 0; j < dh; j++, da += dlb) { - for (k = 0; k < nch; k++) - if (cmask & (1 << (nch - 1 - k))) { - mlib_d64 *sa1 = sa + k; - mlib_d64 *da1 = da + k; - const mlib_d64 *kernel1 = kernel; - - for (i = 0; i < dw; i++) - da1[i * nch] = 0.; - for (j1 = 0; j1 < n; j1++, kernel1 += m) { - mlib_ImageConvMxND642D64_ext(dsa, sa1, dw + m - 1, nch, dx_l, dx_r); - mlib_ImageConvMxNMulAdd_D64(da1, dsa, kernel1, dw, m, 1, nch); - - if ((j + j1 >= dy_t) && (j + j1 < dh + n - dy_b - 2)) - sa1 += slb; - } - } - - if ((j >= dy_t) && (j < dh + n - dy_b - 2)) - sa += slb; - } - - if (dsa != dspace) - mlib_free(dsa); - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c deleted file mode 100644 index 5869b0a54af..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTION - * mlib_ImageConvMxN - image convolution with edge condition - * - * SYNOPSIS - * mlib_status mlib_ImageConvMxN(mlib_image *dst, - * const mlib_image *src, - * const mlib_s32 *kernel, - * mlib_s32 m, - * mlib_s32 n, - * mlib_s32 dm, - * mlib_s32 dn, - * mlib_s32 scale, - * mlib_s32 cmask, - * mlib_edge edge) - * - * ARGUMENTS - * dst Pointer to destination image. - * src Pointer to source image. - * m Kernel width (m must be not less than 1). - * n Kernel height (n must be not less than 1). - * dm, dn Position of key element in convolution kernel. - * kernel Pointer to convolution kernel. - * scale The scaling factor to convert the input integer - * coefficients into floating-point coefficients: - * floating-point coefficient = integer coefficient * 2^(-scale) - * cmask Channel mask to indicate the channels to be convolved. - * Each bit of which represents a channel in the image. The - * channels corresponded to 1 bits are those to be processed. - * edge Type of edge condition. - * - * DESCRIPTION - * 2-D convolution, MxN kernel. - * - * The center of the source image is mapped to the center of the - * destination image. - * The unselected channels are not overwritten. If both src and dst have - * just one channel, cmask is ignored. - * - * The edge condition can be one of the following: - * MLIB_EDGE_DST_NO_WRITE (default) - * MLIB_EDGE_DST_FILL_ZERO - * MLIB_EDGE_DST_COPY_SRC - * MLIB_EDGE_SRC_EXTEND - * - * RESTRICTION - * The src and the dst must be the same type and have same number - * of channels (1, 2, 3, or 4). - * m >= 1, n >= 1, - * 0 <= dm < m, 0 <= dn < n. - * For data type MLIB_BYTE: 16 <= scale <= 31 (to be compatible with VIS version) - * For data type MLIB_USHORT: 17 <= scale <= 32 (to be compatible with VIS version) - * For data type MLIB_SHORT: 17 <= scale <= 32 (to be compatible with VIS version) - * For data type MLIB_INT: scale >= 0 - */ - -#include "mlib_image.h" -#include "mlib_ImageConv.h" -#include "safe_math.h" - -/***************************************************************/ -static void mlib_ImageConvMxNMulAdd_S32(mlib_d64 *dst, - const mlib_s32 *src, - const mlib_d64 *dkernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch); - -static void mlib_ImageConvMxNMedian_S32(mlib_s32 *dst, - mlib_d64 *src, - mlib_s32 n, - mlib_s32 nch); - -static void mlib_ImageConvMxNS322S32_ext(mlib_s32 *dst, - const mlib_s32 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r); - -/***************************************************************/ -#ifdef MLIB_USE_FTOI_CLAMPING - -#define CLAMP_S32(dst, src) \ - dst = (mlib_s32)(src) - -#else - -#define CLAMP_S32(dst, src) { \ - mlib_d64 s0 = (mlib_d64)(src); \ - if (s0 > (mlib_d64)MLIB_S32_MAX) s0 = (mlib_d64)MLIB_S32_MAX; \ - if (s0 < (mlib_d64)MLIB_S32_MIN) s0 = (mlib_d64)MLIB_S32_MIN; \ - dst = (mlib_s32)s0; \ -} - -#endif /* MLIB_USE_FTOI_CLAMPING */ - -/***************************************************************/ -void mlib_ImageConvMxNMulAdd_S32(mlib_d64 *dst, - const mlib_s32 *src, - const mlib_d64 *dkernel, - mlib_s32 n, - mlib_s32 m, - mlib_s32 nch) -{ - mlib_d64 *dst1 = dst + 1; - mlib_s32 i, j; - - for (j = 0; j < m; j += 3, src += 3 * nch, dkernel += 3) { - const mlib_s32 *src2 = src + 2 * nch; - mlib_d64 hval0 = dkernel[0]; - mlib_d64 hval1 = dkernel[1]; - mlib_d64 hval2 = dkernel[2]; - mlib_d64 val0 = src[0]; - mlib_d64 val1 = src[nch]; - mlib_d64 dval = dst[0]; - - if (j == m - 2) { - hval2 = 0.f; - } - else if (j == m - 1) { - hval1 = 0.f; - hval2 = 0.f; - } - - for (i = 0; i < n; i++) { - mlib_d64 dval0 = val0 * hval0 + dval; - mlib_d64 val2 = src2[i * nch]; - - dval = dst1[i]; - dval0 += val1 * hval1; - dval0 += val2 * hval2; - val0 = val1; - val1 = val2; - - dst[i] = dval0; - } - } -} - -/***************************************************************/ -void mlib_ImageConvMxNMedian_S32(mlib_s32 *dst, - mlib_d64 *src, - mlib_s32 n, - mlib_s32 nch) -{ - mlib_s32 i; - - for (i = 0; i < n; i++) { - mlib_s32 res; - - CLAMP_S32(res, src[i]); - src[i] = 0.5; - dst[i * nch] = res; - } -} - -/***************************************************************/ -void mlib_ImageConvMxNS322S32_ext(mlib_s32 *dst, - const mlib_s32 *src, - mlib_s32 n, - mlib_s32 nch, - mlib_s32 dx_l, - mlib_s32 dx_r) -{ - mlib_s32 i; - mlib_d64 val = src[0]; - - for (i = 0; i < dx_l; i++) - dst[i] = (mlib_s32) val; - for (; i < n - dx_r; i++) - dst[i] = src[nch * (i - dx_l)]; - val = dst[n - dx_r - 1]; - for (; i < n; i++) - dst[i] = (mlib_s32) val; -} - -/***************************************************************/ -mlib_status mlib_convMxNext_s32(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask) -{ - mlib_d64 dspace[1024], *dsa = dspace; - mlib_d64 akernel[256], *dkernel = akernel, fscale = 1.0; - mlib_s32 wid_e = mlib_ImageGetWidth(src); - mlib_d64 *dsh, *dsv; - mlib_s32 *isa; - mlib_s32 *da = mlib_ImageGetData(dst); - mlib_s32 *sa = mlib_ImageGetData(src); - mlib_s32 dlb = mlib_ImageGetStride(dst) >> 2; - mlib_s32 slb = mlib_ImageGetStride(src) >> 2; - mlib_s32 dw = mlib_ImageGetWidth(dst); - mlib_s32 dh = mlib_ImageGetHeight(dst); - mlib_s32 nch = mlib_ImageGetChannels(dst); - mlib_s32 i, j, j1, k, mn; - - /* internal buffer */ - - if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { - return MLIB_FAILURE; - } - if (3 * wid_e + m > 1024) { - dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); - - if (dsa == NULL) - return MLIB_FAILURE; - } - - isa = (mlib_s32 *) dsa; - - /* load kernel */ - mn = m * n; - - if (mn > 256) { - dkernel = mlib_malloc(mn * sizeof(mlib_d64)); - - if (dkernel == NULL) { - if (dsa != dspace) mlib_free(dsa); - return MLIB_FAILURE; - } - } - - while (scale > 30) { - fscale /= (1 << 30); - scale -= 30; - } - - fscale /= (1 << scale); - - for (i = 0; i < mn; i++) { - dkernel[i] = ((mlib_s32 *) kernel)[i] * fscale; - } - - dsh = dsa + dw + m; - dsv = dsh + dw; - - for (i = 0; i < dw; i++) { - dsh[i] = 0.5; - dsv[i] = 0.5; - } - - for (j = 0; j < dh; j++, da += dlb) { - for (k = 0; k < nch; k++) - if (cmask & (1 << (nch - 1 - k))) { - mlib_s32 *sa1 = sa + k; - mlib_d64 *dkernel1 = dkernel; - - for (j1 = 0; j1 < n; j1++, dkernel1 += m) { - mlib_ImageConvMxNS322S32_ext(isa, sa1, dw + m - 1, nch, dx_l, dx_r); - mlib_ImageConvMxNMulAdd_S32(dsh, isa, dkernel1, dw, m, 1); - - if ((j + j1 >= dy_t) && (j + j1 < dh + n - dy_b - 2)) - sa1 += slb; - } - - mlib_ImageConvMxNMedian_S32(da + k, dsh, dw, nch); - } - - if ((j >= dy_t) && (j < dh + n - dy_b - 2)) - sa += slb; - } - - if (dkernel != akernel) - mlib_free(dkernel); - if (dsa != dspace) - mlib_free(dsa); - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c deleted file mode 100644 index 00469d25719..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c +++ /dev/null @@ -1,1340 +0,0 @@ -/* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTION - * Internal functions for mlib_ImageConv* on U8/S16/U16 type and - * MLIB_EDGE_SRC_EXTEND mask - */ - -#include "mlib_image.h" -#include "mlib_ImageConv.h" -#include "mlib_c_ImageConv.h" -#include "safe_math.h" - -/* - * This define switches between functions of different data types - */ - -#define IMG_TYPE 2 - -/***************************************************************/ -#if IMG_TYPE == 1 - -#define DTYPE mlib_u8 -#define CONV_FUNC(KERN) mlib_c_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN mlib_c_convMxNext_u8(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u8(PARAM_MxN) -#define DSCALE (1 << 24) -#define FROM_S32(x) (((x) >> 24) ^ 128) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#elif IMG_TYPE == 2 - -#define DTYPE mlib_s16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_s16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_s16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) ((x) >> 16) -#define S64TOS32(x) ((x) & 0xffffffff) -#define SAT_OFF - -#elif IMG_TYPE == 3 - -#define DTYPE mlib_u16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_u16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) (((x) >> 16) ^ 0x8000) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define PARAM \ - mlib_image *dst, \ - const mlib_image *src, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - const mlib_s32 *kern, \ - mlib_s32 scalef_expon, \ - mlib_s32 cmask - -/***************************************************************/ -#define PARAM_MxN \ - mlib_image *dst, \ - const mlib_image *src, \ - const mlib_s32 *kernel, \ - mlib_s32 m, \ - mlib_s32 n, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - mlib_s32 scale, \ - mlib_s32 cmask - -/***************************************************************/ -#define FTYPE mlib_d64 - -#ifndef MLIB_USE_FTOI_CLAMPING - -#define CLAMP_S32(x) \ - (((x) <= MLIB_S32_MIN) ? MLIB_S32_MIN : (((x) >= MLIB_S32_MAX) ? MLIB_S32_MAX : (mlib_s32)(x))) - -#else - -#define CLAMP_S32(x) ((mlib_s32)(x)) - -#endif /* MLIB_USE_FTOI_CLAMPING */ - -/***************************************************************/ -#define D2I(x) CLAMP_S32((x) SAT_OFF) - -/***************************************************************/ -#ifdef _LITTLE_ENDIAN - -#define STORE2(res0, res1) \ - dp[0 ] = res1; \ - dp[chan1] = res0 - -#else - -#define STORE2(res0, res1) \ - dp[0 ] = res0; \ - dp[chan1] = res1 - -#endif /* _LITTLE_ENDIAN */ - -/***************************************************************/ -#ifdef _NO_LONGLONG - -#define LOAD_BUFF(buff) \ - buff[i ] = sp[0]; \ - buff[i + 1] = sp[chan1] - -#else /* _NO_LONGLONG */ - -#ifdef _LITTLE_ENDIAN - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[chan1]) << 32) | S64TOS32((mlib_s64)sp[0]) - -#else /* _LITTLE_ENDIAN */ - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[0]) << 32) | S64TOS32((mlib_s64)sp[chan1]) - -#endif /* _LITTLE_ENDIAN */ -#endif /* _NO_LONGLONG */ - -/***************************************************************/ -typedef union { - mlib_d64 d64; - struct { - mlib_s32 i0; - mlib_s32 i1; - } i32s; -} d64_2x32; - -/***************************************************************/ -#define GET_SRC_DST_PARAMETERS(type) \ - hgt = mlib_ImageGetHeight(src); \ - wid = mlib_ImageGetWidth(src); \ - nchannel = mlib_ImageGetChannels(src); \ - sll = mlib_ImageGetStride(src) / sizeof(type); \ - dll = mlib_ImageGetStride(dst) / sizeof(type); \ - adr_src = (type *)mlib_ImageGetData(src); \ - adr_dst = (type *)mlib_ImageGetData(dst) - -/***************************************************************/ -#if IMG_TYPE == 1 - -/* - * Test for the presence of any "1" bit in bits - 8 to 31 of val. If present, then val is either - negative or >255. If over/underflows of 8 bits - are uncommon, then this technique can be a win, - since only a single test, rather than two, is - necessary to determine if clamping is needed. - On the other hand, if over/underflows are common, - it adds an extra test. -*/ -#define CLAMP_STORE(dst, val) \ - if (val & 0xffffff00) { \ - if (val < MLIB_U8_MIN) \ - dst = MLIB_U8_MIN; \ - else \ - dst = MLIB_U8_MAX; \ - } else { \ - dst = (mlib_u8)val; \ - } - -#elif IMG_TYPE == 2 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_S16_MAX) \ - dst = MLIB_S16_MAX; \ - else if (val <= MLIB_S16_MIN) \ - dst = MLIB_S16_MIN; \ - else \ - dst = (mlib_s16)val - -#elif IMG_TYPE == 3 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_U16_MAX) \ - dst = MLIB_U16_MAX; \ - else if (val <= MLIB_U16_MIN) \ - dst = MLIB_U16_MIN; \ - else \ - dst = (mlib_u16)val - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define MAX_KER 7 -#define MAX_N 15 -#define BUFF_SIZE 1600 -#define CACHE_SIZE (64*1024) - -static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *k, - mlib_s32 n, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - DTYPE *adr_src, *sl; - DTYPE *adr_dst, *dl, *dp; - FTYPE buff[BUFF_SIZE]; - FTYPE *buffd; - FTYPE *pbuff = buff; - const FTYPE *pk; - FTYPE k0, k1, k2, k3; - FTYPE p0, p1, p2, p3, p4; - FTYPE *sbuff; - mlib_s32 l, k_off, off, bsize; - mlib_s32 max_hsize, smax_hsize, shgt, hsize, kh; - mlib_s32 d0, d1, ii; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel; - mlib_s32 i, j, c; - GET_SRC_DST_PARAMETERS(DTYPE); - - max_hsize = ((CACHE_SIZE/sizeof(DTYPE))/sll) - (n - 1); - - if (max_hsize < 1) max_hsize = 1; - if (max_hsize > hgt) max_hsize = hgt; - - shgt = hgt + (n - 1); - if (!SAFE_TO_ADD(max_hsize, (n - 1))) { - return MLIB_FAILURE; - } - smax_hsize = max_hsize + (n - 1); - - if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { - return MLIB_FAILURE; - } - bsize = 2 * (smax_hsize + 1); - - if (bsize > BUFF_SIZE) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize); - - if (pbuff == NULL) return MLIB_FAILURE; - } - - sbuff = pbuff; - buffd = sbuff + smax_hsize; - - shgt -= (dy_t + dy_b); - k_off = 0; - - for (l = 0; l < hgt; l += hsize) { - hsize = hgt - l; - - if (hsize > max_hsize) hsize = max_hsize; - - smax_hsize = hsize + (n - 1); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (i = 0; i < hsize; i++) buffd[i] = 0.0; - - for (j = 0; j < wid; j++) { - FTYPE *buff = sbuff; - - for (i = k_off, ii = 0; (i < dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[0]; - } - - for (; (i < shgt + dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(i - dy_t)*sll]; - } - - for (; (i < shgt + dy_t + dy_b) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(shgt - 1)*sll]; - } - - pk = k; - - for (off = 0; off < (n - 4); off += 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i < hsize; i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - pk += 4; - buff += 4; - } - - dp = dl; - kh = n - off; - - if (kh == 4) { - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; p2 = p4; - p3 = buff[i + 3]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i]); - dp[0] = FROM_S32(d0); - buffd[i] = 0.0; - } - - } else if (kh == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; - p2 = buff[i + 2]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else if (kh == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; - p1 = buff[i + 1]; - d0 = D2I(p0*k0 + p1*k1 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else /* kh == 1 */{ - - k0 = pk[0]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - d0 = D2I(p0*k0 + buffd[i ]); - d1 = D2I(p1*k0 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = buff[i]; - d0 = D2I(p0*k0 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - } - - /* next line */ - sl += nchannel; - dl += nchannel; - } - } - - k_off += max_hsize; - adr_dst += max_hsize*dll; - } - - if (pbuff != buff) mlib_free(pbuff); - - return MLIB_SUCCESS; -} - -/***************************************************************/ -mlib_status CONV_FUNC_MxN -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - FTYPE buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - FTYPE **buffs = buffs_arr, *buffd; - FTYPE akernel[256], *k = akernel, fscale = DSCALE; - FTYPE *pbuff = buff; - FTYPE k0, k1, k2, k3, k4, k5, k6; - FTYPE p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 *buffi; - mlib_s32 mn, l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1, chan2; - mlib_s32 i, j, c, swid; - d64_2x32 dd; - mlib_status status = MLIB_SUCCESS; - - GET_SRC_DST_PARAMETERS(DTYPE); - - if (scale > 30) { - fscale *= 1.0/(1 << 30); - scale -= 30; - } - - fscale /= (1 << scale); - - mn = m*n; - - if (mn > 256) { - k = mlib_malloc(mn*sizeof(mlib_d64)); - - if (k == NULL) return MLIB_FAILURE; - } - - for (i = 0; i < mn; i++) { - k[i] = kernel[i]*fscale; - } - - if (m == 1) { - status = mlib_ImageConv1xN_ext(dst, src, k, n, dy_t, dy_b, cmask); - FREE_AND_RETURN_STATUS; - } - - if (!SAFE_TO_ADD(wid, (m - 1))) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 3), swid)) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - bsize = (n + 3)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize + sizeof(FTYPE *)*2*(n + 1)); - - if (pbuff == NULL) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - buffs = (FTYPE **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - buffi = (mlib_s32*)(buffd + swid); - - chan1 = nchannel; - chan2 = chan1 + chan1; - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (chan1 - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (FTYPE)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (FTYPE)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0.0; - - for (j = 0; j < hgt; j++) { - FTYPE **buffc = buffs + buff_ind; - FTYPE *buffn = buffc[n]; - FTYPE *pk = k; - - for (l = 0; l < n; l++) { - FTYPE *buff_l = buffc[l]; - - for (off = 0; off < m;) { - FTYPE *buff = buff_l + off; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - sp = sl; - dp = dl; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else /* if (kw == 2) */ { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - FTYPE *pk = k, s = 0; - mlib_s32 x, d0; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - d0 = D2I(s); - dp[0] = FROM_S32(d0); - - buffn[i + dx_l] = (FTYPE)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (FTYPE)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - FREE_AND_RETURN_STATUS; -} - -/***************************************************************/ -/* for x86, using integer multiplies is faster */ - -#define STORE_RES(res, x) \ - x >>= shift2; \ - CLAMP_STORE(res, x) - -mlib_status CONV_FUNC_MxN_I -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - mlib_s32 buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - mlib_s32 *pbuff = buff; - mlib_s32 **buffs = buffs_arr, *buffd; - mlib_s32 l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1, shift1, shift2; - mlib_s32 k0, k1, k2, k3, k4, k5, k6; - mlib_s32 p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1; - mlib_s32 i, j, c, swid; - mlib_s32 chan2; - mlib_s32 k_locl[MAX_N*MAX_N], *k = k_locl; - GET_SRC_DST_PARAMETERS(DTYPE); - -#if IMG_TYPE != 1 - shift1 = 16; -#else - shift1 = 8; -#endif /* IMG_TYPE != 1 */ - shift2 = scale - shift1; - - chan1 = nchannel; - chan2 = chan1 + chan1; - - if (!SAFE_TO_ADD(wid, (m - 1))) { - return MLIB_FAILURE; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 2), swid)) { - return MLIB_FAILURE; - } - bsize = (n + 2)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(mlib_s32)*bsize + sizeof(mlib_s32 *)*2*(n + 1)); - - if (pbuff == NULL) return MLIB_FAILURE; - buffs = (mlib_s32 **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - - if (m*n > MAX_N*MAX_N) { - k = mlib_malloc(sizeof(mlib_s32)*(m*n)); - - if (k == NULL) { - if (pbuff != buff) mlib_free(pbuff); - return MLIB_FAILURE; - } - } - - for (i = 0; i < m*n; i++) { - k[i] = kernel[i] >> shift1; - } - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (mlib_s32)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (mlib_s32)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0; - - for (j = 0; j < hgt; j++) { - mlib_s32 **buffc = buffs + buff_ind; - mlib_s32 *buffn = buffc[n]; - mlib_s32 *pk = k; - - for (l = 0; l < n; l++) { - mlib_s32 *buff_l = buffc[l]; - - for (off = 0; off < m;) { - mlib_s32 *buff = buff_l + off; - - sp = sl; - dp = dl; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else /* kw == 1 */{ - - k0 = pk[0]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffd[i ] += p0*k0; - buffd[i + 1] += p1*k0; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + buffd[i ]); - d1 = (p1*k0 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - mlib_s32 *pk = k, x, s = 0; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - STORE_RES(dp[0], s); - - buffn[i + dx_l] = (mlib_s32)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (mlib_s32)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - if (pbuff != buff) mlib_free(pbuff); - if (k != k_locl) mlib_free(k); - - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c deleted file mode 100644 index 136d5a2b814..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c +++ /dev/null @@ -1,1325 +0,0 @@ -/* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTION - * Internal functions for mlib_ImageConv* on U8/S16/U16 type and - * MLIB_EDGE_SRC_EXTEND mask - */ - -#include "mlib_image.h" -#include "mlib_ImageConv.h" -#include "mlib_c_ImageConv.h" -#include "safe_math.h" - -/* - * This define switches between functions of different data types - */ - -#define IMG_TYPE 1 - -/***************************************************************/ -#if IMG_TYPE == 1 - -#define DTYPE mlib_u8 -#define CONV_FUNC(KERN) mlib_c_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN mlib_c_convMxNext_u8(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u8(PARAM_MxN) -#define DSCALE (1 << 24) -#define FROM_S32(x) (((x) >> 24) ^ 128) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#elif IMG_TYPE == 2 - -#define DTYPE mlib_s16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_s16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_s16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) ((x) >> 16) -#define S64TOS32(x) ((x) & 0xffffffff) -#define SAT_OFF - -#elif IMG_TYPE == 3 - -#define DTYPE mlib_u16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_u16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) (((x) >> 16) ^ 0x8000) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define PARAM \ - mlib_image *dst, \ - const mlib_image *src, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - const mlib_s32 *kern, \ - mlib_s32 scalef_expon, \ - mlib_s32 cmask - -/***************************************************************/ -#define PARAM_MxN \ - mlib_image *dst, \ - const mlib_image *src, \ - const mlib_s32 *kernel, \ - mlib_s32 m, \ - mlib_s32 n, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - mlib_s32 scale, \ - mlib_s32 cmask - -/***************************************************************/ -#define FTYPE mlib_d64 - -#ifndef MLIB_USE_FTOI_CLAMPING - -#define CLAMP_S32(x) \ - (((x) <= MLIB_S32_MIN) ? MLIB_S32_MIN : (((x) >= MLIB_S32_MAX) ? MLIB_S32_MAX : (mlib_s32)(x))) - -#else - -#define CLAMP_S32(x) ((mlib_s32)(x)) - -#endif /* MLIB_USE_FTOI_CLAMPING */ - -/***************************************************************/ -#define D2I(x) CLAMP_S32((x) SAT_OFF) - -/***************************************************************/ -#ifdef _NO_LONGLONG - -#define LOAD_BUFF(buff) \ - buff[i ] = sp[0]; \ - buff[i + 1] = sp[chan1] - -#else /* _NO_LONGLONG */ - -#ifdef _LITTLE_ENDIAN - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[chan1]) << 32) | S64TOS32((mlib_s64)sp[0]) - -#else /* _LITTLE_ENDIAN */ - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[0]) << 32) | S64TOS32((mlib_s64)sp[chan1]) - -#endif /* _LITTLE_ENDIAN */ -#endif /* _NO_LONGLONG */ - -/***************************************************************/ -typedef union { - mlib_d64 d64; - struct { - mlib_s32 i0; - mlib_s32 i1; - } i32s; -} d64_2x32; - -/***************************************************************/ -#define GET_SRC_DST_PARAMETERS(type) \ - hgt = mlib_ImageGetHeight(src); \ - wid = mlib_ImageGetWidth(src); \ - nchannel = mlib_ImageGetChannels(src); \ - sll = mlib_ImageGetStride(src) / sizeof(type); \ - dll = mlib_ImageGetStride(dst) / sizeof(type); \ - adr_src = (type *)mlib_ImageGetData(src); \ - adr_dst = (type *)mlib_ImageGetData(dst) - -/***************************************************************/ -#if IMG_TYPE == 1 - -/* - * Test for the presence of any "1" bit in bits - 8 to 31 of val. If present, then val is either - negative or >255. If over/underflows of 8 bits - are uncommon, then this technique can be a win, - since only a single test, rather than two, is - necessary to determine if clamping is needed. - On the other hand, if over/underflows are common, - it adds an extra test. -*/ -#define CLAMP_STORE(dst, val) \ - if (val & 0xffffff00) { \ - if (val < MLIB_U8_MIN) \ - dst = MLIB_U8_MIN; \ - else \ - dst = MLIB_U8_MAX; \ - } else { \ - dst = (mlib_u8)val; \ - } - -#elif IMG_TYPE == 2 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_S16_MAX) \ - dst = MLIB_S16_MAX; \ - else if (val <= MLIB_S16_MIN) \ - dst = MLIB_S16_MIN; \ - else \ - dst = (mlib_s16)val - -#elif IMG_TYPE == 3 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_U16_MAX) \ - dst = MLIB_U16_MAX; \ - else if (val <= MLIB_U16_MIN) \ - dst = MLIB_U16_MIN; \ - else \ - dst = (mlib_u16)val - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define MAX_KER 7 -#define MAX_N 15 -#define BUFF_SIZE 1600 -#define CACHE_SIZE (64*1024) - -static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *k, - mlib_s32 n, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - DTYPE *adr_src, *sl; - DTYPE *adr_dst, *dl, *dp; - FTYPE buff[BUFF_SIZE]; - FTYPE *buffd; - FTYPE *pbuff = buff; - const FTYPE *pk; - FTYPE k0, k1, k2, k3; - FTYPE p0, p1, p2, p3, p4; - FTYPE *sbuff; - mlib_s32 l, k_off, off, bsize; - mlib_s32 max_hsize, smax_hsize, shgt, hsize, kh; - mlib_s32 d0, d1, ii; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel; - mlib_s32 i, j, c; - GET_SRC_DST_PARAMETERS(DTYPE); - - max_hsize = ((CACHE_SIZE/sizeof(DTYPE))/sll) - (n - 1); - - if (max_hsize < 1) max_hsize = 1; - if (max_hsize > hgt) max_hsize = hgt; - - shgt = hgt + (n - 1); - if (!SAFE_TO_ADD(max_hsize, (n - 1))) { - return MLIB_FAILURE; - } - smax_hsize = max_hsize + (n - 1); - - if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { - return MLIB_FAILURE; - } - bsize = 2 * (smax_hsize + 1); - - if (bsize > BUFF_SIZE) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize); - - if (pbuff == NULL) return MLIB_FAILURE; - } - - sbuff = pbuff; - buffd = sbuff + smax_hsize; - - shgt -= (dy_t + dy_b); - k_off = 0; - - for (l = 0; l < hgt; l += hsize) { - hsize = hgt - l; - - if (hsize > max_hsize) hsize = max_hsize; - - smax_hsize = hsize + (n - 1); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (i = 0; i < hsize; i++) buffd[i] = 0.0; - - for (j = 0; j < wid; j++) { - FTYPE *buff = sbuff; - - for (i = k_off, ii = 0; (i < dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[0]; - } - - for (; (i < shgt + dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(i - dy_t)*sll]; - } - - for (; (i < shgt + dy_t + dy_b) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(shgt - 1)*sll]; - } - - pk = k; - - for (off = 0; off < (n - 4); off += 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i < hsize; i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - pk += 4; - buff += 4; - } - - dp = dl; - kh = n - off; - - if (kh == 4) { - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; p2 = p4; - p3 = buff[i + 3]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i]); - dp[0] = FROM_S32(d0); - buffd[i] = 0.0; - } - - } else if (kh == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; - p2 = buff[i + 2]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else if (kh == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; - p1 = buff[i + 1]; - d0 = D2I(p0*k0 + p1*k1 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else /* kh == 1 */{ - - k0 = pk[0]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - d0 = D2I(p0*k0 + buffd[i ]); - d1 = D2I(p1*k0 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = buff[i]; - d0 = D2I(p0*k0 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - } - - /* next line */ - sl += nchannel; - dl += nchannel; - } - } - - k_off += max_hsize; - adr_dst += max_hsize*dll; - } - - if (pbuff != buff) mlib_free(pbuff); - - return MLIB_SUCCESS; -} - -/***************************************************************/ -mlib_status CONV_FUNC_MxN -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - FTYPE buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - FTYPE **buffs = buffs_arr, *buffd; - FTYPE akernel[256], *k = akernel, fscale = DSCALE; - FTYPE *pbuff = buff; - FTYPE k0, k1, k2, k3, k4, k5, k6; - FTYPE p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 *buffi; - mlib_s32 mn, l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1, chan2; - mlib_s32 i, j, c, swid; - d64_2x32 dd; - mlib_status status = MLIB_SUCCESS; - - GET_SRC_DST_PARAMETERS(DTYPE); - - if (scale > 30) { - fscale *= 1.0/(1 << 30); - scale -= 30; - } - - fscale /= (1 << scale); - - mn = m*n; - - if (mn > 256) { - k = mlib_malloc(mn*sizeof(mlib_d64)); - - if (k == NULL) return MLIB_FAILURE; - } - - for (i = 0; i < mn; i++) { - k[i] = kernel[i]*fscale; - } - - if (m == 1) { - status = mlib_ImageConv1xN_ext(dst, src, k, n, dy_t, dy_b, cmask); - FREE_AND_RETURN_STATUS; - } - - if (!SAFE_TO_ADD(wid, (m - 1))) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 3), swid)) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - bsize = (n + 3)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize + sizeof(FTYPE *)*2*(n + 1)); - - if (pbuff == NULL) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - buffs = (FTYPE **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - buffi = (mlib_s32*)(buffd + swid); - - chan1 = nchannel; - chan2 = chan1 + chan1; - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (chan1 - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (FTYPE)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (FTYPE)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0.0; - - for (j = 0; j < hgt; j++) { - FTYPE **buffc = buffs + buff_ind; - FTYPE *buffn = buffc[n]; - FTYPE *pk = k; - - for (l = 0; l < n; l++) { - FTYPE *buff_l = buffc[l]; - - for (off = 0; off < m;) { - FTYPE *buff = buff_l + off; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - sp = sl; - dp = dl; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else /* if (kw == 2) */ { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - FTYPE *pk = k, s = 0; - mlib_s32 x, d0; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - d0 = D2I(s); - dp[0] = FROM_S32(d0); - - buffn[i + dx_l] = (FTYPE)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (FTYPE)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - FREE_AND_RETURN_STATUS; -} - -/***************************************************************/ -/* for x86, using integer multiplies is faster */ - -#define STORE_RES(res, x) \ - x >>= shift2; \ - CLAMP_STORE(res, x) - -mlib_status CONV_FUNC_MxN_I -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - mlib_s32 buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - mlib_s32 *pbuff = buff; - mlib_s32 **buffs = buffs_arr, *buffd; - mlib_s32 l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1, shift1, shift2; - mlib_s32 k0, k1, k2, k3, k4, k5, k6; - mlib_s32 p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1; - mlib_s32 i, j, c, swid; - mlib_s32 chan2; - mlib_s32 k_locl[MAX_N*MAX_N], *k = k_locl; - GET_SRC_DST_PARAMETERS(DTYPE); - -#if IMG_TYPE != 1 - shift1 = 16; -#else - shift1 = 8; -#endif /* IMG_TYPE != 1 */ - shift2 = scale - shift1; - - chan1 = nchannel; - chan2 = chan1 + chan1; - - if (!SAFE_TO_ADD(wid, (m - 1))) { - return MLIB_FAILURE; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 2), swid)) { - return MLIB_FAILURE; - } - bsize = (n + 2)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(mlib_s32)*bsize + sizeof(mlib_s32 *)*2*(n + 1)); - - if (pbuff == NULL) return MLIB_FAILURE; - buffs = (mlib_s32 **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - - if (m*n > MAX_N*MAX_N) { - k = mlib_malloc(sizeof(mlib_s32)*(m*n)); - - if (k == NULL) { - if (pbuff != buff) mlib_free(pbuff); - return MLIB_FAILURE; - } - } - - for (i = 0; i < m*n; i++) { - k[i] = kernel[i] >> shift1; - } - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (mlib_s32)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (mlib_s32)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0; - - for (j = 0; j < hgt; j++) { - mlib_s32 **buffc = buffs + buff_ind; - mlib_s32 *buffn = buffc[n]; - mlib_s32 *pk = k; - - for (l = 0; l < n; l++) { - mlib_s32 *buff_l = buffc[l]; - - for (off = 0; off < m;) { - mlib_s32 *buff = buff_l + off; - - sp = sl; - dp = dl; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else /* kw == 1 */{ - - k0 = pk[0]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffd[i ] += p0*k0; - buffd[i + 1] += p1*k0; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + buffd[i ]); - d1 = (p1*k0 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - mlib_s32 *pk = k, x, s = 0; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - STORE_RES(dp[0], s); - - buffn[i + dx_l] = (mlib_s32)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (mlib_s32)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - if (pbuff != buff) mlib_free(pbuff); - if (k != k_locl) mlib_free(k); - - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c deleted file mode 100644 index 81a06f2fc28..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTION - * Internal functions for mlib_ImageConv* on U8/S16/U16 type and - * MLIB_EDGE_SRC_EXTEND mask - */ - -#include "mlib_image.h" -#include "mlib_ImageConv.h" -#include "mlib_c_ImageConv.h" -#include "safe_math.h" - -/* - * This define switches between functions of different data types - */ - -#define IMG_TYPE 3 - -/***************************************************************/ -#if IMG_TYPE == 1 - -#define DTYPE mlib_u8 -#define CONV_FUNC(KERN) mlib_c_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN mlib_c_convMxNext_u8(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u8(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u8(PARAM_MxN) -#define DSCALE (1 << 24) -#define FROM_S32(x) (((x) >> 24) ^ 128) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#elif IMG_TYPE == 2 - -#define DTYPE mlib_s16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_s16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_s16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_s16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) ((x) >> 16) -#define S64TOS32(x) ((x) & 0xffffffff) -#define SAT_OFF - -#elif IMG_TYPE == 3 - -#define DTYPE mlib_u16 -#define CONV_FUNC(KERN) mlib_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN mlib_convMxNext_u16(PARAM_MxN) -#define CONV_FUNC_I(KERN) mlib_i_conv##KERN##ext_u16(PARAM) -#define CONV_FUNC_MxN_I mlib_i_convMxNext_u16(PARAM_MxN) -#define DSCALE 65536.0 -#define FROM_S32(x) (((x) >> 16) ^ 0x8000) -#define S64TOS32(x) (x) -#define SAT_OFF -(1u << 31) - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define PARAM \ - mlib_image *dst, \ - const mlib_image *src, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - const mlib_s32 *kern, \ - mlib_s32 scalef_expon, \ - mlib_s32 cmask - -/***************************************************************/ -#define PARAM_MxN \ - mlib_image *dst, \ - const mlib_image *src, \ - const mlib_s32 *kernel, \ - mlib_s32 m, \ - mlib_s32 n, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - mlib_s32 scale, \ - mlib_s32 cmask - -/***************************************************************/ -#define FTYPE mlib_d64 - -#ifndef MLIB_USE_FTOI_CLAMPING - -#define CLAMP_S32(x) \ - (((x) <= MLIB_S32_MIN) ? MLIB_S32_MIN : (((x) >= MLIB_S32_MAX) ? MLIB_S32_MAX : (mlib_s32)(x))) - -#else - -#define CLAMP_S32(x) ((mlib_s32)(x)) - -#endif /* MLIB_USE_FTOI_CLAMPING */ - -/***************************************************************/ -#define D2I(x) CLAMP_S32((x) SAT_OFF) - -/***************************************************************/ -#ifdef _LITTLE_ENDIAN - -#define STORE2(res0, res1) \ - dp[0 ] = res1; \ - dp[chan1] = res0 - -#else - -#define STORE2(res0, res1) \ - dp[0 ] = res0; \ - dp[chan1] = res1 - -#endif /* _LITTLE_ENDIAN */ - -/***************************************************************/ -#ifdef _NO_LONGLONG - -#define LOAD_BUFF(buff) \ - buff[i ] = sp[0]; \ - buff[i + 1] = sp[chan1] - -#else /* _NO_LONGLONG */ - -#ifdef _LITTLE_ENDIAN - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[chan1]) << 32) | S64TOS32((mlib_s64)sp[0]) - -#else /* _LITTLE_ENDIAN */ - -#define LOAD_BUFF(buff) \ - *(mlib_s64*)(buff + i) = (((mlib_s64)sp[0]) << 32) | S64TOS32((mlib_s64)sp[chan1]) - -#endif /* _LITTLE_ENDIAN */ -#endif /* _NO_LONGLONG */ - -/***************************************************************/ -typedef union { - mlib_d64 d64; - struct { - mlib_s32 i0; - mlib_s32 i1; - } i32s; -} d64_2x32; - -/***************************************************************/ -#define DEF_VARS(type) \ - type *adr_src, *sl, *sp, *sl1; \ - type *adr_dst, *dl, *dp; \ - FTYPE *pbuff = buff; \ - mlib_s32 *buffi, *buffo; \ - mlib_s32 wid, hgt, sll, dll; \ - mlib_s32 nchannel, chan1, chan2; \ - mlib_s32 i, j, c, swid - -/***************************************************************/ -#define GET_SRC_DST_PARAMETERS(type) \ - hgt = mlib_ImageGetHeight(src); \ - wid = mlib_ImageGetWidth(src); \ - nchannel = mlib_ImageGetChannels(src); \ - sll = mlib_ImageGetStride(src) / sizeof(type); \ - dll = mlib_ImageGetStride(dst) / sizeof(type); \ - adr_src = (type *)mlib_ImageGetData(src); \ - adr_dst = (type *)mlib_ImageGetData(dst) - -/***************************************************************/ -#if IMG_TYPE == 1 - -/* - * Test for the presence of any "1" bit in bits - 8 to 31 of val. If present, then val is either - negative or >255. If over/underflows of 8 bits - are uncommon, then this technique can be a win, - since only a single test, rather than two, is - necessary to determine if clamping is needed. - On the other hand, if over/underflows are common, - it adds an extra test. -*/ -#define CLAMP_STORE(dst, val) \ - if (val & 0xffffff00) { \ - if (val < MLIB_U8_MIN) \ - dst = MLIB_U8_MIN; \ - else \ - dst = MLIB_U8_MAX; \ - } else { \ - dst = (mlib_u8)val; \ - } - -#elif IMG_TYPE == 2 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_S16_MAX) \ - dst = MLIB_S16_MAX; \ - else if (val <= MLIB_S16_MIN) \ - dst = MLIB_S16_MIN; \ - else \ - dst = (mlib_s16)val - -#elif IMG_TYPE == 3 - -#define CLAMP_STORE(dst, val) \ - if (val >= MLIB_U16_MAX) \ - dst = MLIB_U16_MAX; \ - else if (val <= MLIB_U16_MIN) \ - dst = MLIB_U16_MIN; \ - else \ - dst = (mlib_u16)val - -#endif /* IMG_TYPE == 1 */ - -/***************************************************************/ -#define MAX_KER 7 -#define MAX_N 15 -#define BUFF_SIZE 1600 -#define CACHE_SIZE (64*1024) - -static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, - const mlib_image *src, - const mlib_d64 *k, - mlib_s32 n, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - DTYPE *adr_src, *sl; - DTYPE *adr_dst, *dl, *dp; - FTYPE buff[BUFF_SIZE]; - FTYPE *buffd; - FTYPE *pbuff = buff; - const FTYPE *pk; - FTYPE k0, k1, k2, k3; - FTYPE p0, p1, p2, p3, p4; - FTYPE *sbuff; - mlib_s32 l, k_off, off, bsize; - mlib_s32 max_hsize, smax_hsize, shgt, hsize, kh; - mlib_s32 d0, d1, ii; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel; - mlib_s32 i, j, c; - GET_SRC_DST_PARAMETERS(DTYPE); - - max_hsize = ((CACHE_SIZE/sizeof(DTYPE))/sll) - (n - 1); - - if (max_hsize < 1) max_hsize = 1; - if (max_hsize > hgt) max_hsize = hgt; - - shgt = hgt + (n - 1); - if (!SAFE_TO_ADD(max_hsize, (n - 1))) { - return MLIB_FAILURE; - } - smax_hsize = max_hsize + (n - 1); - - if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { - return MLIB_FAILURE; - } - bsize = 2 * (smax_hsize + 1); - - if (bsize > BUFF_SIZE) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize); - - if (pbuff == NULL) return MLIB_FAILURE; - } - - sbuff = pbuff; - buffd = sbuff + smax_hsize; - - shgt -= (dy_t + dy_b); - k_off = 0; - - for (l = 0; l < hgt; l += hsize) { - hsize = hgt - l; - - if (hsize > max_hsize) hsize = max_hsize; - - smax_hsize = hsize + (n - 1); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (i = 0; i < hsize; i++) buffd[i] = 0.0; - - for (j = 0; j < wid; j++) { - FTYPE *buff = sbuff; - - for (i = k_off, ii = 0; (i < dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[0]; - } - - for (; (i < shgt + dy_t) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(i - dy_t)*sll]; - } - - for (; (i < shgt + dy_t + dy_b) && (ii < smax_hsize); i++, ii++) { - sbuff[i - k_off] = (FTYPE)sl[(shgt - 1)*sll]; - } - - pk = k; - - for (off = 0; off < (n - 4); off += 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i < hsize; i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - pk += 4; - buff += 4; - } - - dp = dl; - kh = n - off; - - if (kh == 4) { - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; p2 = p4; - p3 = buff[i + 3]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i]); - dp[0] = FROM_S32(d0); - buffd[i] = 0.0; - } - - } else if (kh == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; p1 = p3; - p2 = buff[i + 2]; - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else if (kh == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = p2; - p1 = buff[i + 1]; - d0 = D2I(p0*k0 + p1*k1 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - - } else /* kh == 1 */{ - - k0 = pk[0]; - - for (i = 0; i <= (hsize - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - d0 = D2I(p0*k0 + buffd[i ]); - d1 = D2I(p1*k0 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[dll] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - dp += 2*dll; - } - - if (i < hsize) { - p0 = buff[i]; - d0 = D2I(p0*k0 + buffd[i]); - dp[0] = FROM_S32(d0); - - buffd[i] = 0.0; - } - } - - /* next line */ - sl += nchannel; - dl += nchannel; - } - } - - k_off += max_hsize; - adr_dst += max_hsize*dll; - } - - if (pbuff != buff) mlib_free(pbuff); - - return MLIB_SUCCESS; -} - -/***************************************************************/ -mlib_status CONV_FUNC_MxN -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - FTYPE buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - FTYPE **buffs = buffs_arr, *buffd; - FTYPE akernel[256], *k = akernel, fscale = DSCALE; - FTYPE *pbuff = buff; - FTYPE k0, k1, k2, k3, k4, k5, k6; - FTYPE p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 *buffi; - mlib_s32 mn, l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1, chan2; - mlib_s32 i, j, c, swid; - d64_2x32 dd; - mlib_status status = MLIB_SUCCESS; - - GET_SRC_DST_PARAMETERS(DTYPE); - - if (scale > 30) { - fscale *= 1.0/(1 << 30); - scale -= 30; - } - - fscale /= (1 << scale); - - mn = m*n; - - if (mn > 256) { - k = mlib_malloc(mn*sizeof(mlib_d64)); - - if (k == NULL) return MLIB_FAILURE; - } - - for (i = 0; i < mn; i++) { - k[i] = kernel[i]*fscale; - } - - if (m == 1) { - status = mlib_ImageConv1xN_ext(dst, src, k, n, dy_t, dy_b, cmask); - FREE_AND_RETURN_STATUS; - } - - if (!SAFE_TO_ADD(wid, (m - 1))) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 3), swid)) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - bsize = (n + 3)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(FTYPE)*bsize + sizeof(FTYPE *)*2*(n + 1)); - - if (pbuff == NULL) { - status = MLIB_FAILURE; - FREE_AND_RETURN_STATUS; - } - buffs = (FTYPE **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - buffi = (mlib_s32*)(buffd + swid); - - chan1 = nchannel; - chan2 = chan1 + chan1; - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (chan1 - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (FTYPE)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (FTYPE)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0.0; - - for (j = 0; j < hgt; j++) { - FTYPE **buffc = buffs + buff_ind; - FTYPE *buffn = buffc[n]; - FTYPE *pk = k; - - for (l = 0; l < n; l++) { - FTYPE *buff_l = buffc[l]; - - for (off = 0; off < m;) { - FTYPE *buff = buff_l + off; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - sp = sl; - dp = dl; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - - } else /* if (kw == 2) */ { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - LOAD_BUFF(buffi); - - dd.d64 = *(FTYPE *)(buffi + i); - buffn[i + dx_l ] = (FTYPE)dd.i32s.i0; - buffn[i + dx_l + 1] = (FTYPE)dd.i32s.i1; - - d0 = D2I(p0*k0 + p1*k1 + buffd[i ]); - d1 = D2I(p1*k0 + p2*k1 + buffd[i + 1]); - - dp[0 ] = FROM_S32(d0); - dp[chan1] = FROM_S32(d1); - - buffd[i ] = 0.0; - buffd[i + 1] = 0.0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - FTYPE *pk = k, s = 0; - mlib_s32 x, d0; - - for (l = 0; l < n; l++) { - FTYPE *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - d0 = D2I(s); - dp[0] = FROM_S32(d0); - - buffn[i + dx_l] = (FTYPE)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (FTYPE)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - FREE_AND_RETURN_STATUS; -} - -/***************************************************************/ -#define STORE_RES(res, x) \ - x >>= shift2; \ - CLAMP_STORE(res, x) - -mlib_status CONV_FUNC_MxN_I -{ - DTYPE *adr_src, *sl, *sp = NULL; - DTYPE *adr_dst, *dl, *dp = NULL; - mlib_s32 buff[BUFF_SIZE], *buffs_arr[2*(MAX_N + 1)]; - mlib_s32 *pbuff = buff; - mlib_s32 **buffs = buffs_arr, *buffd; - mlib_s32 l, off, kw, bsize, buff_ind; - mlib_s32 d0, d1, shift1, shift2; - mlib_s32 k0, k1, k2, k3, k4, k5, k6; - mlib_s32 p0, p1, p2, p3, p4, p5, p6, p7; - mlib_s32 wid, hgt, sll, dll; - mlib_s32 nchannel, chan1; - mlib_s32 i, j, c, swid; - mlib_s32 chan2; - mlib_s32 k_locl[MAX_N*MAX_N], *k = k_locl; - GET_SRC_DST_PARAMETERS(DTYPE); - -#if IMG_TYPE != 1 - shift1 = 16; -#else - shift1 = 8; -#endif /* IMG_TYPE != 1 */ - shift2 = scale - shift1; - - chan1 = nchannel; - chan2 = chan1 + chan1; - - if (!SAFE_TO_ADD(wid, (m - 1))) { - return MLIB_FAILURE; - } - swid = wid + (m - 1); - - if (!SAFE_TO_MULT((n + 2), swid)) { - return MLIB_FAILURE; - } - bsize = (n + 2)*swid; - - if ((bsize > BUFF_SIZE) || (n > MAX_N)) { - pbuff = mlib_malloc(sizeof(mlib_s32)*bsize + sizeof(mlib_s32 *)*2*(n + 1)); - - if (pbuff == NULL) return MLIB_FAILURE; - buffs = (mlib_s32 **)(pbuff + bsize); - } - - for (l = 0; l < (n + 1); l++) buffs[l] = pbuff + l*swid; - for (l = 0; l < (n + 1); l++) buffs[l + (n + 1)] = buffs[l]; - buffd = buffs[n] + swid; - - if (m*n > MAX_N*MAX_N) { - k = mlib_malloc(sizeof(mlib_s32)*(m*n)); - - if (k == NULL) { - if (pbuff != buff) mlib_free(pbuff); - return MLIB_FAILURE; - } - } - - for (i = 0; i < m*n; i++) { - k[i] = kernel[i] >> shift1; - } - - swid -= (dx_l + dx_r); - - for (c = 0; c < nchannel; c++) { - if (!(cmask & (1 << (nchannel - 1 - c)))) continue; - - sl = adr_src + c; - dl = adr_dst + c; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffs[l]; - - for (i = 0; i < dx_l; i++) { - buff[i] = (mlib_s32)sl[0]; - } - - for (i = 0; i < swid; i++) { - buff[i + dx_l] = (mlib_s32)sl[i*chan1]; - } - - for (i = 0; i < dx_r; i++) { - buff[swid + dx_l + i] = buff[swid + dx_l - 1]; - } - - if ((l >= dy_t) && (l < hgt + n - dy_b - 2)) sl += sll; - } - - buff_ind = 0; - - for (i = 0; i < wid; i++) buffd[i] = 0; - - for (j = 0; j < hgt; j++) { - mlib_s32 **buffc = buffs + buff_ind; - mlib_s32 *buffn = buffc[n]; - mlib_s32 *pk = k; - - for (l = 0; l < n; l++) { - mlib_s32 *buff_l = buffc[l]; - - for (off = 0; off < m;) { - mlib_s32 *buff = buff_l + off; - - sp = sl; - dp = dl; - - kw = m - off; - - if (kw > 2*MAX_KER) kw = MAX_KER; else - if (kw > MAX_KER) kw = kw/2; - off += kw; - - if (kw == 7) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; p7 = buff[5]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; k6 = pk[6]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; p5 = p7; - - p6 = buff[i + 6]; p7 = buff[i + 7]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + p6*k6 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + p7*k6 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 6) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; p6 = buff[4]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; k5 = pk[5]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; p4 = p6; - - p5 = buff[i + 5]; p6 = buff[i + 6]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + p5*k5 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + p6*k5 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 5) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - p5 = buff[3]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - k4 = pk[4]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; p3 = p5; - - p4 = buff[i + 4]; p5 = buff[i + 5]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + p4*k4 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + p5*k4 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 4) { - - p2 = buff[0]; p3 = buff[1]; p4 = buff[2]; - - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; k3 = pk[3]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2 + p3*k3; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2 + p4*k3; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; p2 = p4; - - p3 = buff[i + 3]; p4 = buff[i + 4]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + p3*k3 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + p4*k3 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 3) { - - p2 = buff[0]; p3 = buff[1]; - k0 = pk[0]; k1 = pk[1]; k2 = pk[2]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffd[i ] += p0*k0 + p1*k1 + p2*k2; - buffd[i + 1] += p1*k0 + p2*k1 + p3*k2; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; p1 = p3; - - p2 = buff[i + 2]; p3 = buff[i + 3]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + p2*k2 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + p3*k2 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else if (kw == 2) { - - p2 = buff[0]; - k0 = pk[0]; k1 = pk[1]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffd[i ] += p0*k0 + p1*k1; - buffd[i + 1] += p1*k0 + p2*k1; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = p2; - - p1 = buff[i + 1]; p2 = buff[i + 2]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + p1*k1 + buffd[i ]); - d1 = (p1*k0 + p2*k1 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - - } else /* kw == 1 */{ - - k0 = pk[0]; - - if (l < (n - 1) || off < m) { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffd[i ] += p0*k0; - buffd[i + 1] += p1*k0; - } - - } else { - for (i = 0; i <= (wid - 2); i += 2) { - p0 = buff[i]; p1 = buff[i + 1]; - - buffn[i + dx_l ] = (mlib_s32)sp[0]; - buffn[i + dx_l + 1] = (mlib_s32)sp[chan1]; - - d0 = (p0*k0 + buffd[i ]); - d1 = (p1*k0 + buffd[i + 1]); - - STORE_RES(dp[0 ], d0); - STORE_RES(dp[chan1], d1); - - buffd[i ] = 0; - buffd[i + 1] = 0; - - sp += chan2; - dp += chan2; - } - } - } - - pk += kw; - } - } - - /* last pixels */ - for (; i < wid; i++) { - mlib_s32 *pk = k, x, s = 0; - - for (l = 0; l < n; l++) { - mlib_s32 *buff = buffc[l] + i; - - for (x = 0; x < m; x++) s += buff[x] * (*pk++); - } - - STORE_RES(dp[0], s); - - buffn[i + dx_l] = (mlib_s32)sp[0]; - - sp += chan1; - dp += chan1; - } - - for (; i < swid; i++) { - buffn[i + dx_l] = (mlib_s32)sp[0]; - sp += chan1; - } - - for (i = 0; i < dx_l; i++) buffn[i] = buffn[dx_l]; - for (i = 0; i < dx_r; i++) buffn[swid + dx_l + i] = buffn[swid + dx_l - 1]; - - /* next line */ - - if (j < hgt - dy_b - 2) sl += sll; - dl += dll; - - buff_ind++; - - if (buff_ind >= n + 1) buff_ind = 0; - } - } - - if (pbuff != buff) mlib_free(pbuff); - if (k != k_locl) mlib_free(k); - - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv.h b/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv.h index 0f311a4d484..81a367578d2 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv.h +++ b/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv.h @@ -49,18 +49,6 @@ mlib_status mlib_c_convMxNnw_u8(mlib_image *dst, mlib_s32 scale, mlib_s32 cmask); -mlib_status mlib_c_convMxNext_u8(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kern, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - /* for x86, using integer multiplies is faster */ mlib_status mlib_i_convMxNnw_s16(mlib_image *dst, @@ -93,42 +81,6 @@ mlib_status mlib_i_convMxNnw_u8(mlib_image *dst, mlib_s32 scale, mlib_s32 cmask); -mlib_status mlib_i_convMxNext_u8(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kern, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - -mlib_status mlib_i_convMxNext_s16(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - -mlib_status mlib_i_convMxNext_u16(mlib_image *dst, - const mlib_image *src, - const mlib_s32 *kernel, - mlib_s32 m, - mlib_s32 n, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 scale, - mlib_s32 cmask); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConvCopyEdge.c b/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConvCopyEdge.c deleted file mode 100644 index 7b463934fe5..00000000000 --- a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConvCopyEdge.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -/* - * FUNCTIONS - * mlib_ImageConvCopyEdge - Copy src edges to dst edges - * - * - * SYNOPSIS - * mlib_status mlib_ImageConvCopyEdge(mlib_image *dst, - * const mlib_image *src, - * mlib_s32 dx_l, - * mlib_s32 dx_r, - * mlib_s32 dy_t, - * mlib_s32 dy_b, - * mlib_s32 cmask) - * - * ARGUMENT - * dst Pointer to an dst image. - * src Pointer to an src image. - * dx_l Number of columns on the left side of the - * image to be copied. - * dx_r Number of columns on the right side of the - * image to be copied. - * dy_t Number of rows on the top edge of the - * image to be copied. - * dy_b Number of rows on the top edge of the - * image to be copied. - * cmask Channel mask to indicate the channels to be convolved. - * Each bit of which represents a channel in the image. The - * channels corresponded to 1 bits are those to be processed. - * - * RESTRICTION - * The src and the dst must be the same type, same width, same height and have same number - * of channels (1, 2, 3, or 4). The unselected channels are not - * overwritten. If both src and dst have just one channel, - * cmask is ignored. - * - * DESCRIPTION - * Copy src edges to dst edges. - * - * The unselected channels are not overwritten. - * If src and dst have just one channel, - * cmask is ignored. - */ - -#include "mlib_image.h" -#include "mlib_ImageConvEdge.h" - -/***************************************************************/ -#define EDGES(chan, type, mask) \ - { \ - type *pdst = (type *) mlib_ImageGetData(dst); \ - type *psrc = (type *) mlib_ImageGetData(src); \ - mlib_s32 dst_stride = mlib_ImageGetStride(dst) / sizeof(type); \ - mlib_s32 src_stride = mlib_ImageGetStride(src) / sizeof(type); \ - mlib_s32 i, j, l; \ - mlib_s32 testchan; \ - \ - testchan = 1; \ - for (l = chan - 1; l >= 0; l--) { \ - if ((mask & testchan) == 0) { \ - testchan <<= 1; \ - continue; \ - } \ - testchan <<= 1; \ - for (j = 0; j < dx_l; j++) { \ - for (i = dy_t; i < (img_height - dy_b); i++) { \ - pdst[i*dst_stride + l + j*chan] = psrc[i*src_stride + l + j*chan]; \ - } \ - } \ - for (j = 0; j < dx_r; j++) { \ - for (i = dy_t; i < (img_height - dy_b); i++) { \ - pdst[i*dst_stride + l+(img_width-1 - j)*chan] = \ - psrc[i*src_stride + l+(img_width-1 - j)*chan]; \ - } \ - } \ - for (i = 0; i < dy_t; i++) { \ - for (j = 0; j < img_width; j++) { \ - pdst[i*dst_stride + l + j*chan] = psrc[i*src_stride + l + j*chan]; \ - } \ - } \ - for (i = 0; i < dy_b; i++) { \ - for (j = 0; j < img_width; j++) { \ - pdst[(img_height-1 - i)*dst_stride + l + j*chan] = \ - psrc[(img_height-1 - i)*src_stride + l + j*chan]; \ - } \ - } \ - } \ - } - -/***************************************************************/ -mlib_status mlib_ImageConvCopyEdge(mlib_image *dst, - const mlib_image *src, - mlib_s32 dx_l, - mlib_s32 dx_r, - mlib_s32 dy_t, - mlib_s32 dy_b, - mlib_s32 cmask) -{ - mlib_s32 img_width = mlib_ImageGetWidth(dst); - mlib_s32 img_height = mlib_ImageGetHeight(dst); - mlib_s32 channel = mlib_ImageGetChannels(dst); - - if (dx_l + dx_r > img_width) { - dx_l = img_width; - dx_r = 0; - } - - if (dy_t + dy_b > img_height) { - dy_t = img_height; - dy_b = 0; - } - - if (channel == 1) - cmask = 1; - - switch (mlib_ImageGetType(src)) { - case MLIB_BIT: - return mlib_ImageConvCopyEdge_Bit(dst, src, dx_l, dx_r, dy_t, dy_b, cmask); - case MLIB_BYTE: - EDGES(channel, mlib_u8, cmask) - break; - case MLIB_SHORT: - case MLIB_USHORT: - EDGES(channel, mlib_u16, cmask) - break; - case MLIB_INT: - case MLIB_FLOAT: - EDGES(channel, mlib_u32, cmask) - break; - case MLIB_DOUBLE: - EDGES(channel, mlib_d64, cmask) - break; - default: - return MLIB_FAILURE; - } - - return MLIB_SUCCESS; -} - -/***************************************************************/ diff --git a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv_f.c b/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv_f.c index 5b65422c4c2..79c2b031da3 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv_f.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_c_ImageConv_f.c @@ -44,25 +44,6 @@ #define MLIB_CALL_PARAMS_CONV_MN_NW \ dst, src, kern, m, n, dm, dn, scale, cmask -/***************************************************************/ -#define MLIB_PARAMS_CONV_MN_EXT \ - mlib_image *dst, \ - const mlib_image *src, \ - const mlib_s32 *kern, \ - mlib_s32 m, \ - mlib_s32 n, \ - mlib_s32 dx_l, \ - mlib_s32 dx_r, \ - mlib_s32 dy_t, \ - mlib_s32 dy_b, \ - mlib_s32 scale, \ - mlib_s32 cmask - -/***************************************************************/ -#define MLIB_CALL_PARAMS_CONV_MN_EXT \ - dst, src, kern, m, n, dx_l, dx_r, dy_t, dy_b, scale, cmask - - /***************************************************************/ mlib_status mlib_convMxNnw_u8(MLIB_PARAMS_CONV_MN_NW) { @@ -73,12 +54,3 @@ mlib_status mlib_convMxNnw_u8(MLIB_PARAMS_CONV_MN_NW) } /***************************************************************/ -mlib_status mlib_convMxNext_u8(MLIB_PARAMS_CONV_MN_EXT) -{ - if (mlib_ImageConvVersion(m, n, scale, MLIB_BYTE) == 0) - return mlib_c_convMxNext_u8(MLIB_CALL_PARAMS_CONV_MN_EXT); - else - return mlib_i_convMxNext_u8(MLIB_CALL_PARAMS_CONV_MN_EXT); -} - -/***************************************************************/ From b560c2024e834275a039324664f73a148184ebfd Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 1 Oct 2026 20:39:01 +0000 Subject: [PATCH 14/98] 6986565: sun/java2d/SunGraphics2D/PolyVertTest.java failed on Windows and Solaris Reviewed-by: kizune, azvegint --- test/jdk/ProblemList.txt | 3 +- .../java2d/SunGraphics2D/PolyVertTest.java | 230 ++++++++++++------ 2 files changed, 152 insertions(+), 81 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 6d2b9a80d45..8d10abb2df5 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -210,7 +210,8 @@ sun/java2d/OpenGL/OpaqueDest.java#id1 8367574 macosx-all sun/java2d/OpenGL/ScaleParamsOOB.java#id0 8377908 linux-all sun/java2d/SunGraphics2D/EmptyClipRenderingTest.java 8144029 macosx-all,linux-all sun/java2d/SunGraphics2D/DrawImageBilinear.java 8297175 linux-all -sun/java2d/SunGraphics2D/PolyVertTest.java 6986565 generic-all +sun/java2d/SunGraphics2D/PolyVertTest.java#default 8392800,8392802 macosx-all,windows-all +sun/java2d/SunGraphics2D/PolyVertTest.java#opengl 8392801 linux-all sun/java2d/SunGraphics2D/SimplePrimQuality.java#default 6322554,8392436 windows-all,macosx-all sun/java2d/SunGraphics2D/SimplePrimQuality.java#nod3d 6322554 windows-all sun/java2d/SunGraphics2D/SourceClippingBlitTest/SourceClippingBlitTest.java 8392471 linux-all diff --git a/test/jdk/sun/java2d/SunGraphics2D/PolyVertTest.java b/test/jdk/sun/java2d/SunGraphics2D/PolyVertTest.java index 9f9b9f610dd..e3daf0fbd2c 100644 --- a/test/jdk/sun/java2d/SunGraphics2D/PolyVertTest.java +++ b/test/jdk/sun/java2d/SunGraphics2D/PolyVertTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,74 @@ */ /* - * @test + * @test id=default * @key headful * @bug 4678208 4771101 6328481 6588884 8198613 * @summary verify the pixelization of degenerate polylines and polygons - * @run main PolyVertTest - * @run main/othervm -Dsun.java2d.d3d=True PolyVertTest -hwonly + * @run main/othervm -Dsun.java2d.uiScale=1 PolyVertTest */ -import java.awt.*; -import java.awt.event.*; -import java.awt.geom.*; -import java.awt.image.*; +/* + * @test id=noxrender + * @key headful + * @requires (os.family == "linux") + * @bug 4678208 4771101 6328481 6588884 8198613 + * @summary verify the pixelization of degenerate polylines and polygons + * @run main/othervm -Dsun.java2d.uiScale=1 -Dsun.java2d.xrender=false PolyVertTest -hwonly + */ + +/* + * @test id=dno3d + * @key headful + * @requires (os.family == "windows") + * @bug 4678208 4771101 6328481 6588884 8198613 + * @summary verify the pixelization of degenerate polylines and polygons + * @run main/othervm -Dsun.java2d.uiScale=1 -Dsun.java2d.d3d=false PolyVertTest -hwonly + */ + +/* + * @test id=opengl + * @key headful + * @bug 4678208 4771101 6328481 6588884 8198613 + * @summary verify the pixelization of degenerate polylines and polygons + * @run main/othervm -Dsun.java2d.uiScale=1 -Dsun.java2d.opengl=True PolyVertTest -hwonly + */ + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Transparency; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; public class PolyVertTest { - static int TESTWIDTH; - static int TESTHEIGHT; + + static volatile int TESTWIDTH; + static volatile int TESTHEIGHT; static final int REG_TEST_WIDTH = 10; static final int REG_TEST_HEIGHT = 10; static final int FULL_TEST_WIDTH = 50; @@ -47,29 +99,29 @@ public class PolyVertTest { static final int GREEN = Color.green.getRGB(); static final int RED = Color.red.getRGB(); - static BufferedImage refImg; - static BufferedImage errorImg; - static Graphics errorG; - static Component testCanvas; - - static int totalbadpixels; - static int totalfuzzypixels; - static int numbadtests; - static int numfuzzytests; - static int numframes; - static int fuzzystarty; - - static boolean counting; - static boolean showerrors; - static boolean showresults; - static boolean fringe; - static boolean forceerror; - static boolean fulltest = true; - static boolean hwonly; + static volatile BufferedImage refImg; + static volatile BufferedImage errorImg; + static volatile Graphics errorG; + static volatile TestCanvas testCanvas; + + static volatile int totalbadpixels; + static volatile int totalfuzzypixels; + static volatile int numbadtests; + static volatile int numfuzzytests; + static volatile int numframes; + static volatile int fuzzystarty; + + static volatile boolean counting; + static volatile boolean showerrors; + static volatile boolean showresults; + static volatile boolean fringe; + static volatile boolean forceerror; + static volatile boolean fulltest = true; + static volatile boolean hwonly; static WindowListener windowCloser = new WindowAdapter() { public void windowClosing(WindowEvent e) { - e.getWindow().hide(); + e.getWindow().setVisible(false); if (--numframes <= 0) { System.exit(0); } @@ -260,7 +312,7 @@ public static void usage(int exitcode) { System.exit(exitcode); } - public static void main(String argv[]) { + public static void main(String argv[]) throws Exception { for (int i = 0; i < argv.length; i++) { String arg = argv[i]; if (arg.equalsIgnoreCase("-count")) { @@ -300,14 +352,22 @@ public static void main(String argv[]) { // finished our tests. numframes++; - makeReferenceImage(); - testScreen(); - testVolatileImage(); - if (!hwonly) { - testBufferedImage(); - testOffscreen(); - testCompatibleImages(); + try { + makeReferenceImage(); + testCanvas = new TestCanvas(); + testScreen(); + testVolatileImage(); + if (!hwonly) { + testBufferedImage(); + testOffscreen(); + testCompatibleImages(); + } + } finally { + if (frame != null) { + EventQueue.invokeAndWait(frame::dispose); + } } + if (totalfuzzypixels > 0) { System.err.println(totalfuzzypixels+" fuzzy pixels found in "+ numfuzzytests+" tests"); @@ -466,6 +526,11 @@ public static void verify(BufferedImage bimg, String name) { errorG.setColor(isfuzzy ? Color.blue : Color.red); errorG.fillRect(x, y, 1, 1); } else if (!counting && !isfuzzy) { + try { + ImageIO.write(refImg, "png", new File("refimg" + ".png")); + ImageIO.write(bimg, "png", new File(name+".png")); + } catch (Exception e) { + } throw new RuntimeException("Error at "+x+", "+y+ " while testing: "+name); } @@ -520,7 +585,16 @@ public static void displaydiffs(BufferedImage bimg, String name) { } } } - Frame f = new Frame("Results for "+name); + try { + EventQueue.invokeAndWait(() -> createDiffsUI(bimg, name)); + } catch (Exception e) { + System.err.println("error creating diffs UI for " + name); + } + } + + static void createDiffsUI(BufferedImage bimg, String name) { + + Frame f = new Frame("Results for " + name); f.setLayout(new BorderLayout()); f.addWindowListener(windowCloser); ++numframes; @@ -530,8 +604,9 @@ public static void displaydiffs(BufferedImage bimg, String name) { p.add(new ImageCanvas(refImg)); f.add(p, "Center"); droperrorbuf(); - f.pack(); - f.show(); + f.setSize(500, FULL_TEST_HEIGHT + 50); + f.setLocationRelativeTo(null); + f.setVisible(true); } public static void testBufferedImage() { @@ -549,16 +624,35 @@ public static void testBufferedImage(int type, String name) { test(bimg, name); } - public static void testScreen() { - Frame f = new Frame("PolyVertTest"); - TestCanvas child = new TestCanvas(); - testCanvas = child; - f.add(child); - f.pack(); - f.show(); - BufferedImage bimg = child.getImage(); - f.hide(); - verify(bimg, "Screen"); + static volatile Frame frame; + + public static void testScreen() throws Exception { + EventQueue.invokeAndWait(PolyVertTest::createUI); + try { + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(2000); + Point p = testCanvas.getLocationOnScreen(); + Rectangle r = new Rectangle(p.x, p.y, TESTWIDTH, TESTHEIGHT); + BufferedImage bimg = robot.createScreenCapture(r); + verify(bimg, "Screen"); + } catch (AWTException e) { + throw new RuntimeException(e); + } + } + + public static void createUI() { + frame = new Frame("PolyVertTest"); + frame.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(50, 50, 50, 50); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + frame.add(testCanvas, c); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); } public static void testOffscreen() { @@ -600,6 +694,7 @@ public static void testCompatibleImages(GraphicsConfiguration gconfig) { public static void testVolatileImage() { Image img = testCanvas.createVolatileImage(TESTWIDTH, TESTHEIGHT); + System.err.println("img="+img); test(img, "Volatile"); } @@ -620,42 +715,17 @@ public void paint(Graphics g) { } public static class TestCanvas extends Canvas { - BufferedImage bimg; public Dimension getPreferredSize() { return new Dimension(TESTWIDTH, TESTHEIGHT); } public void paint(Graphics g) { - if (bimg != null || - getWidth() < TESTWIDTH || - getHeight() < TESTHEIGHT) - { + if (getWidth() < TESTWIDTH || + getHeight() < TESTHEIGHT) { return; } render((Graphics2D) g); - Toolkit.getDefaultToolkit().sync(); - Point p = getLocationOnScreen(); - Rectangle r = new Rectangle(p.x, p.y, TESTWIDTH, TESTHEIGHT); - try { - bimg = new Robot().createScreenCapture(r); - } catch (AWTException e) { - e.printStackTrace(); - } - synchronized (this) { - notifyAll(); - } - } - - public synchronized BufferedImage getImage() { - while (bimg == null) { - try { - wait(); - } catch (InterruptedException e) { - return null; - } - } - return bimg; - } - } + } + } } From 01b363841a39e858628201adfa601ec5c8ffbba6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 1 Oct 2026 20:39:42 +0000 Subject: [PATCH 15/98] 8385017: Null pointer dereference in java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java:204 Reviewed-by: kizune, serb --- .../com/sun/media/sound/ModelByteBuffer.java | 7 ++++++- .../media/sound/ModelByteBufferWavetable.java | 17 +++++++++++++---- .../sun/media/sound/SoftAbstractResampler.java | 3 +++ .../classes/com/sun/media/sound/SoftVoice.java | 13 ++++++++++--- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java index 2476ff09bbc..0437a9f9556 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,6 +201,9 @@ public ModelByteBuffer(File file, long offset, long len) { public void writeTo(OutputStream out) throws IOException { if (root.file != null && root.buffer == null) { try (InputStream is = getInputStream()) { + if (is == null) { + throw new IOException("null stream"); + } is.transferTo(out); } } else @@ -321,6 +324,8 @@ public void load() throws IOException { buffer = new byte[(int) capacity()]; offset = 0; dis.readFully(buffer); + } catch (NullPointerException npe) { + throw new IOException("input stream is null", npe); } } diff --git a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java index bc8829d288c..ab12dad7ca5 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,10 +195,14 @@ public AudioFormat getFormat() { @Override public AudioFloatInputStream openStream() { - if (buffer == null) + if (buffer == null) { return null; + } if (format == null) { InputStream is = buffer.getInputStream(); + if (is == null) { + return null; + } AudioInputStream ais = null; try { ais = AudioSystem.getAudioInputStream(is); @@ -209,8 +213,12 @@ public AudioFloatInputStream openStream() { return AudioFloatInputStream.getInputStream(ais); } if (buffer.array() == null) { + InputStream is = buffer.getInputStream(); + if (is == null) { + return null; + } return AudioFloatInputStream.getInputStream(new AudioInputStream( - buffer.getInputStream(), format, + is, format, buffer.capacity() / format.getFrameSize())); } if (buffer8 != null) { @@ -237,7 +245,8 @@ public AudioFloatInputStream openStream() { @Override public int getChannels() { - return getFormat().getChannels(); + AudioFormat format = getFormat(); + return (format != null) ? format.getChannels() : 1; } @Override diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftAbstractResampler.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftAbstractResampler.java index ef9eac6dd03..ce04ee1272a 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftAbstractResampler.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftAbstractResampler.java @@ -96,6 +96,9 @@ public void open(ModelWavetable osc, float outputsamplerate) } stream = osc.openStream(); + if (stream == null) { + throw new IOException("null stream"); + } streampos = 0; stream_eof = false; pitchcorrection = osc.getPitchcorrection(); diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftVoice.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftVoice.java index 3808b263c4b..7f7642d005c 100644 --- a/src/java.desktop/share/classes/com/sun/media/sound/SoftVoice.java +++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftVoice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -590,6 +590,8 @@ void processControlLogic() { osc_stream.close(); } catch (IOException e) { //e.printStackTrace(); + } finally { + osc_stream = null; } if (stealer_channel != null) { @@ -662,7 +664,9 @@ void processControlLogic() { for (int i = 0; i < performer.ctrl_connections.length; i++) processConnection(performer.ctrl_connections[i]); - osc_stream.setPitch((float)co_osc_pitch[0]); + if (osc_stream != null) { + osc_stream.setPitch((float)co_osc_pitch[0]); + } int filter_type = (int)co_filter_type[0]; double filter_freq; @@ -829,7 +833,10 @@ void processAudioLogic(SoftAudioBuffer[] buffer) { osc_buff[0] = buffer[SoftMainMixer.CHANNEL_LEFT_DRY].array(); if (nrofchannels != 1) osc_buff[1] = buffer[SoftMainMixer.CHANNEL_RIGHT_DRY].array(); - int ret = osc_stream.read(osc_buff, 0, bufferlen); + int ret = -1; + if (osc_stream != null) { + ret = osc_stream.read(osc_buff, 0, bufferlen); + } if (ret == -1) { stopping = true; return; From 8de3be8411997635a8e382ac74199b4a58b5bb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20B=C3=A1lek?= Date: Fri, 2 Oct 2026 06:41:34 +0000 Subject: [PATCH 16/98] 8393242: jlink fails with "Multiple modules contain non-empty package" for a non-package resource directory in more than one module Reviewed-by: alanb --- .../jlink/internal/ImageResourcesTree.java | 8 +- .../jlink/NonPackageResourceDirTest.java | 114 ++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 test/jdk/tools/jlink/NonPackageResourceDirTest.java diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java index ab0809348ca..d323cc8ed39 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageResourcesTree.java @@ -26,6 +26,7 @@ import jdk.internal.jimage.ImageLocation; import jdk.internal.jimage.ModuleLink; +import jdk.internal.module.Checks; import java.io.DataOutputStream; import java.io.IOException; @@ -204,9 +205,10 @@ private void buildTree() { } // We've collected information for all "packages", including the root - // (empty) package and anything under "META-INF". However, these should - // not have entries in the "/packages" directory. - packageToModules.keySet().removeIf(p -> p.isEmpty() || p.equals("META-INF") || p.startsWith("META-INF.")); + // (empty) package and anything under non-package resource directories + // (e.g. "META-INF"). However, these should not have entries in the + // "/packages" directory. + packageToModules.keySet().removeIf(p -> !Checks.isPackageName(p)); packageToModules.forEach((pkgName, modLinks) -> { // Merge multiple links for the same module. List pkgModules = modLinks.stream() diff --git a/test/jdk/tools/jlink/NonPackageResourceDirTest.java b/test/jdk/tools/jlink/NonPackageResourceDirTest.java new file mode 100644 index 00000000000..9e4e16f3fb0 --- /dev/null +++ b/test/jdk/tools/jlink/NonPackageResourceDirTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8393242 + * @summary Test jlink with a non-package resource directory in several modules + * @library /test/lib + * @modules java.base/jdk.internal.module + * jdk.jlink + * jdk.jartool + * @run junit/othervm NonPackageResourceDirTest + */ + +import java.lang.module.ModuleDescriptor; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.spi.ToolProvider; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.ModuleInfoWriter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import static org.junit.jupiter.api.Assertions.*; + +class NonPackageResourceDirTest { + + @BeforeAll + static void setup() throws Exception { + Path src = Files.createDirectory(Path.of(".").resolve("src")); + // Create module a containing + // module-info.class + // a/C.class + // OSGI-INF/MANIFEST.MF + String moduleName = "a"; + ModuleDescriptor descriptor = ModuleDescriptor.newModule(moduleName) + .exports("a") + .build(); + byte[] moduleInfo = ModuleInfoWriter.toBytes(descriptor); + Path dir = Files.createDirectory(Path.of(".").resolve(moduleName)); + Files.write(dir.resolve("module-info.class"), moduleInfo); + Path srcA = Files.writeString(Files.createDirectory(src.resolve("a")).resolve("C.java"), + """ + package a; + public class C {} + """); + ToolProvider javac = ToolProvider.findFirst("javac").orElseThrow(); + javac.run(System.out, System.err, "-d", dir.toString(), srcA.toString()); + Files.createFile(Files.createDirectory(dir.resolve("OSGI-INF")).resolve("MANIFEST.MF")); + + // Create modular jar of a + ToolProvider jar = ToolProvider.findFirst("jar").orElseThrow(); + jar.run(System.out, System.err, "--create", "--file", moduleName + ".jar", "-C", moduleName, "."); + + // Create module b containing + // module-info.class + // b/C.class + // OSGI-INF/MANIFEST.MF + moduleName = "b"; + descriptor = ModuleDescriptor.newModule(moduleName) + .exports("b") + .build(); + moduleInfo = ModuleInfoWriter.toBytes(descriptor); + dir = Files.createDirectory(Path.of(".").resolve(moduleName)); + Files.write(dir.resolve("module-info.class"), moduleInfo); + Path srcB = Files.writeString(Files.createDirectory(src.resolve("b")).resolve("C.java"), + """ + package b; + public class C {} + """); + javac.run(System.out, System.err, "-d", dir.toString(), srcB.toString()); + Files.createFile(Files.createDirectory(dir.resolve("OSGI-INF")).resolve("MANIFEST.MF")); + + // Create modular jar of b + jar.run(System.out, System.err, "--create", "--file", moduleName + ".jar", "-C", moduleName, "."); + } + + @Test + void testJavaValidate() throws Exception { + ProcessTools.executeProcess(ProcessTools.createTestJavaProcessBuilder( + "--module-path", "a.jar" + File.pathSeparator + "b.jar", + "--validate-modules")).shouldHaveExitValue(0); + } + + @Test + void testJlink() throws Exception { + ToolProvider jlink = ToolProvider.findFirst("jlink").orElseThrow(); + int res = jlink.run(System.out, System.err, "--module-path", "a.jar" + File.pathSeparator + "b.jar", + "--add-modules", "a,b", "--output", "image"); + assertEquals(0, res); + } +} From d2064c5923d0787ad39da0116f8c552e49ddd5f9 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Fri, 2 Oct 2026 07:07:02 +0000 Subject: [PATCH 17/98] 8393231: C2: assert(old_raw_mem_proj != nullptr) failed: should have found raw mem Proj Reviewed-by: chagedorn, thartmann --- src/hotspot/share/opto/cfgnode.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 18f6e21671a..fe6ccd7f23b 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -30,6 +30,7 @@ #include "opto/addnode.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" @@ -1094,9 +1095,15 @@ uint PhiNode::hash() const { const Type* at = _adr_type; return TypeNode::hash() + (at ? at->hash() : 0); } -bool PhiNode::cmp( const Node &n ) const { - return TypeNode::cmp(n) && _adr_type == ((PhiNode&)n)._adr_type; + +bool PhiNode::cmp(const Node& n) const { + const PhiNode& other = static_cast(n); + if (!TypeNode::cmp(other) || _adr_type != other._adr_type) { + return false; + } + return _adr_type != TypePtr::BOTTOM || _excluded_idx == other._excluded_idx; } + static inline const TypePtr* flatten_phi_adr_type(const TypePtr* at) { if (at == nullptr || at == TypePtr::BOTTOM) return at; @@ -3479,6 +3486,22 @@ void PhiNode::dump_spec(outputStream *st) const { if (is_tripcount(T_INT) || is_tripcount(T_LONG)) { st->print(" #tripcount"); } + if (type() == Type::MEMORY && adr_type() == TypePtr::BOTTOM && _excluded_idx != nullptr) { + st->print(" #excluded_alias_idx:{"); + bool first = true; + for (int alias_idx = Compile::AliasIdxRaw; alias_idx < Compile::current()->num_alias_types(); alias_idx++) { + if (_excluded_idx->contains(alias_idx)) { + if (first) { + first = false; + } else { + st->print(","); + } + + st->print("%d", alias_idx); + } + } + st->print("}"); + } } #endif From 740639a4981feb0f046058303567d204c154bb87 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Fri, 2 Oct 2026 07:29:10 +0000 Subject: [PATCH 18/98] 8391971: C2 hits assert because split-If does not correctly handle EncodeISOArray Reviewed-by: qamai, thartmann --- src/hotspot/share/opto/loopnode.hpp | 3 + src/hotspot/share/opto/split_if.cpp | 38 ++++++++++ .../splitif/TestSplitIfSCMemProj.java | 73 +++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/splitif/TestSplitIfSCMemProj.java diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index dc4383b6917..54ea9c75f3f 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1792,6 +1792,9 @@ class PhaseIdealLoop : public PhaseTransform { // Split Node 'n' through merge point RegionNode* split_thru_region(Node* n, RegionNode* region); + + static ProjNode* unique_scmem_proj_if_any(Node* n); + // Split Node 'n' through merge point if there is enough win. Node *split_thru_phi( Node *n, Node *region, int policy ); // Found an If getting its condition-code input from a Phi in the diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 84a53211d09..284752b87af 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -69,6 +69,20 @@ RegionNode* PhaseIdealLoop::split_thru_region(Node* n, RegionNode* region) { return r; } +ProjNode* PhaseIdealLoop::unique_scmem_proj_if_any(Node* n) { + ProjNode* proj = nullptr; + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + if (u->Opcode() == Op_SCMemProj) { + assert(proj == nullptr, "only one SCMemProj"); + proj = u->as_Proj(); + } else { + assert(!u->is_Proj(), "we can't split nodes with Proj uses other than SCMemProj"); + } + } + return proj; +} + //------------------------------split_up--------------------------------------- // Split block-local op up through the phis to empty the current block bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { @@ -155,6 +169,15 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { tty->print_cr(" Splitting up: %d %s", n->_idx, n->Name()); } #endif + // If n has a SCMemProj use, pushing n through the Phi and then the SCMemProj (which has then a Phi input) through the + // Phi doesn't work as split if needs the adr_type of an SCMemProj to create its Phi which Proj::addr_type() gets from + // its input: that works as long as the input of the SCMemProj is n but not if it's a Phi. Prepare the Phi for the + // SCMemProj here before n is cloned. + ProjNode* mem_proj = unique_scmem_proj_if_any(n); + Node* phi_mem_proj = nullptr; + if (mem_proj != nullptr) { + phi_mem_proj = PhiNode::make_blank(blk1, mem_proj); + } Node *phi = PhiNode::make_blank(blk1, n); for( uint j = 1; j < blk1->req(); j++ ) { Node *x = n->clone(); @@ -178,6 +201,21 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { // Remove cloned-up value from optimizer; use phi instead _igvn.replace_node( n, phi ); + if (mem_proj != nullptr) { + for (uint j = 1; j < blk1->req(); j++) { + Node* x = mem_proj->clone(); + x->set_req(0, phi->in(j)); + register_new_node(x, blk1->in(j)); + phi_mem_proj->init_req(j, x); + } + // Announce phi to optimizer + register_new_node(phi_mem_proj, blk1); + + // Remove cloned-up value from optimizer; use phi instead + _igvn.replace_node(mem_proj, phi_mem_proj); + } + assert(!phi->has_out_with(Op_SCMemProj), "only works with a single SCMemProj use"); + // (There used to be a self-recursive call to split_up() here, // but it is not needed. All necessary forward walking is done // by do_split_if() below.) diff --git a/test/hotspot/jtreg/compiler/splitif/TestSplitIfSCMemProj.java b/test/hotspot/jtreg/compiler/splitif/TestSplitIfSCMemProj.java new file mode 100644 index 00000000000..a4d9bb4bc2e --- /dev/null +++ b/test/hotspot/jtreg/compiler/splitif/TestSplitIfSCMemProj.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8391971 + * @modules java.base/java.lang:+open + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test ${test.main.class} + * @run main ${test.main.class} + */ + +import java.lang.invoke.*; + +public class TestSplitIfSCMemProj { + static final MethodHandle MH; + static int x = 0; + + static { + try { + Class c = Class.forName("java.lang.StringCoding"); + MethodType mt = MethodType.methodType(int.class, byte[].class, int.class, byte[].class, int.class, int.class); + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c, MethodHandles.lookup()); + MethodHandle mh; + try { + mh = lookup.findStatic(c, "encodeISOArray0", mt); + } catch (NoSuchMethodException exception) { + // Older JDKs + mh = lookup.findStatic(c, "implEncodeISOArray", mt); + } + MH = mh; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static void test(boolean b) throws Throwable { + byte[] src = new byte[2]; + byte[] dst = new byte[1]; + if (b) { + x = 42; + } + int res = (int)MH.invokeExact(src, 0, dst, 0, 1); + if (b) { + x = 43; + } + } + + public static void main(String[] args) throws Throwable { + for (int i = 0; i < 50_000; i++) { + test((i % 2) == 0); + } + } +} From d6d6c8fc358a43ed58bc498d523eac7828f1dbff Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 2 Oct 2026 08:48:15 +0000 Subject: [PATCH 19/98] 8392841: C1: Failed OSR compilation should not block future normal compilation Reviewed-by: mhaessig, thartmann, kvn --- src/hotspot/share/oops/method.cpp | 1 + src/hotspot/share/oops/method.hpp | 6 +----- src/hotspot/share/oops/methodFlags.hpp | 13 +++++++------ src/hotspot/share/prims/whitebox.cpp | 1 + 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index dfbef61dd3f..ce76c1dd82f 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -1303,6 +1303,7 @@ void Method::remove_unshareable_flags() { set_is_not_c2_compilable(false); set_is_not_c1_compilable(false); set_is_not_c2_osr_compilable(false); + set_is_not_c1_osr_compilable(false); set_on_stack_flag(false); set_has_scalarized_args(false); } diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index 8c0f9affa60..10a6fd9d8ac 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -837,13 +837,9 @@ class Method : public Metadata { void clear_is_not_c1_compilable() { set_is_not_c1_compilable(false); } void clear_is_not_c2_compilable() { set_is_not_c2_compilable(false); } + void clear_is_not_c1_osr_compilable() { set_is_not_c1_osr_compilable(false); } void clear_is_not_c2_osr_compilable() { set_is_not_c2_osr_compilable(false); } - // not_c1_osr_compilable == not_c1_compilable - bool is_not_c1_osr_compilable() const { return is_not_c1_compilable(); } - void set_is_not_c1_osr_compilable() { set_is_not_c1_compilable(); } - void clear_is_not_c1_osr_compilable() { clear_is_not_c1_compilable(); } - // Background compilation support void clear_queued_for_compilation() { set_queued_for_compilation(false); } diff --git a/src/hotspot/share/oops/methodFlags.hpp b/src/hotspot/share/oops/methodFlags.hpp index f023fa743bb..e8ed9853884 100644 --- a/src/hotspot/share/oops/methodFlags.hpp +++ b/src/hotspot/share/oops/methodFlags.hpp @@ -52,12 +52,13 @@ class MethodFlags { status(is_not_c2_compilable , 1 << 8) \ status(is_not_c1_compilable , 1 << 9) \ status(is_not_c2_osr_compilable , 1 << 10) \ - status(force_inline , 1 << 11) /* Annotations but also set/reset at runtime */ \ - status(dont_inline , 1 << 12) \ - status(has_loops_flag , 1 << 13) /* Method has loops */ \ - status(has_loops_flag_init , 1 << 14) /* The loop flag has been initialized */ \ - status(on_stack_flag , 1 << 15) /* RedefineClasses support to keep Metadata from being cleaned */ \ - status(has_scalarized_args , 1 << 16) \ + status(is_not_c1_osr_compilable , 1 << 11) \ + status(force_inline , 1 << 12) /* Annotations but also set/reset at runtime */ \ + status(dont_inline , 1 << 13) \ + status(has_loops_flag , 1 << 14) /* Method has loops */ \ + status(has_loops_flag_init , 1 << 15) /* The loop flag has been initialized */ \ + status(on_stack_flag , 1 << 16) /* RedefineClasses support to keep Metadata from being cleaned */ \ + status(has_scalarized_args , 1 << 17) \ /* end of list */ #define M_STATUS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index c3d419c62d2..0963ba23bf7 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -1288,6 +1288,7 @@ WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) mh->clear_is_not_c1_compilable(); mh->clear_is_not_c2_compilable(); + mh->clear_is_not_c1_osr_compilable(); mh->clear_is_not_c2_osr_compilable(); NOT_PRODUCT(mh->set_compiled_invocation_count(0)); if (mcs != nullptr) { From 86e538937adbbcf8c61b1394ede022f5664426b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Fri, 2 Oct 2026 10:40:09 +0000 Subject: [PATCH 20/98] 8392815: SystemDictionaryShared::check_linking_constraints can avoid more unnecessary vtable/itable reinitializations Reviewed-by: fbredberg, cnorrbin, iklam, matsaave --- src/hotspot/share/classfile/systemDictionaryShared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index ea9747f7240..67c64a5c9a2 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -1272,8 +1272,8 @@ bool SystemDictionaryShared::check_linking_constraints(Thread* current, Instance log.print(" succeeded]"); } } - return true; // for all recorded constraints added successfully. } + return true; // Recorded constraints, if any, have all been added successfully. } if (log.is_enabled()) { ResourceMark rm(current); From bd9e82618c6415eaa16269e02a78741bb34d1566 Mon Sep 17 00:00:00 2001 From: Lee Jiwon Date: Fri, 2 Oct 2026 11:20:43 +0000 Subject: [PATCH 21/98] 8392709: (fc) FileChannelImpl.write(ByteBuffer src, long position) checks FileReadEvent.enabled() Reviewed-by: vyazici --- .../classes/sun/nio/ch/FileChannelImpl.java | 2 +- .../jfr/event/io/TestFileChannelEvents.java | 83 ++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index a440e646026..5d0dfd38757 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -1214,7 +1214,7 @@ private int readInternal(ByteBuffer dst, long position) throws IOException { @Override public int write(ByteBuffer src, long position) throws IOException { - if (jfrTracing && FileReadEvent.enabled()) { + if (jfrTracing && FileWriteEvent.enabled()) { return traceImplWrite(src, position); } return implWrite(src, position); diff --git a/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java index 490e16d3e52..5907fc00d20 100644 --- a/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * 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,6 +40,7 @@ /** * @test + * @bug 8199712 8392709 * @requires vm.flagless * @requires vm.hasJFR * @library /test/lib /test/jdk @@ -47,6 +48,11 @@ */ public class TestFileChannelEvents { public static void main(String[] args) throws Throwable { + testReadWriteAndForceEvents(); + testNoInterference(); + } + + private static void testReadWriteAndForceEvents() throws Throwable { File tmp = Utils.createTempFile("TestFileChannelEvents", ".tmp").toFile(); try (Recording recording = new Recording()) { List expectedEvents = new ArrayList<>(); @@ -108,7 +114,7 @@ public static void main(String[] args) throws Throwable { ch.position(ch.size()); bufA.clear(); size = ch.read(bufA); - assertEquals(size, -1L, "Expected size -1 when read at EOF"); + assertEquals(-1L, size, "Expected size -1 when read at EOF"); expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); ch.close(); @@ -118,4 +124,77 @@ public static void main(String[] args) throws Throwable { } } } + + // Tests that FileRead and FileWrite events are recorded independently + // when the other event type is disabled. + private static void testNoInterference() throws Throwable { + testNoInterferenceCase("positional write", IOEvent.EVENT_FILE_WRITE, IOEvent.EVENT_FILE_READ, + (file, ch, buffer, other) -> { + long size = ch.write(buffer, 0); + assertEquals(10L, size, "Unexpected size for positional write"); + return IOEvent.createFileWriteEvent(size, file); + }); + + testNoInterferenceCase("current-position write", IOEvent.EVENT_FILE_WRITE, IOEvent.EVENT_FILE_READ, + (file, ch, buffer, other) -> { + long size = ch.write(buffer); + assertEquals(10L, size, "Unexpected size for current-position write"); + return IOEvent.createFileWriteEvent(size, file); + }); + + testNoInterferenceCase("gathering write", IOEvent.EVENT_FILE_WRITE, IOEvent.EVENT_FILE_READ, + (file, ch, buffer, other) -> { + long size = ch.write(new ByteBuffer[] { buffer, other }); + assertEquals(20L, size, "Unexpected size for gathering write"); + return IOEvent.createFileWriteEvent(size, file); + }); + + testNoInterferenceCase("positional read", IOEvent.EVENT_FILE_READ, IOEvent.EVENT_FILE_WRITE, + (file, ch, buffer, other) -> { + long size = ch.read(buffer, 0); + assertEquals(10L, size, "Unexpected size for positional read"); + return IOEvent.createFileReadEvent(size, file); + }); + + testNoInterferenceCase("current-position read", IOEvent.EVENT_FILE_READ, IOEvent.EVENT_FILE_WRITE, + (file, ch, buffer, other) -> { + long size = ch.read(buffer); + assertEquals(10L, size, "Unexpected size for current-position read"); + return IOEvent.createFileReadEvent(size, file); + }); + + testNoInterferenceCase("scattering read", IOEvent.EVENT_FILE_READ, IOEvent.EVENT_FILE_WRITE, + (file, ch, buffer, other) -> { + long size = ch.read(new ByteBuffer[] { buffer, other }); + assertEquals(20L, size, "Unexpected size for scattering read"); + return IOEvent.createFileReadEvent(size, file); + }); + } + + private static void testNoInterferenceCase(String name, String enabledEvent, String disabledEvent, + FileChannelOperation operation) throws Throwable { + System.out.println("Testing " + name + " with " + enabledEvent + + " enabled and " + disabledEvent + " disabled"); + File file = Utils.createTempFile("TestFileChannelEvents", ".tmp").toFile(); + try (RandomAccessFile rf = new RandomAccessFile(file, "rw"); + FileChannel ch = rf.getChannel(); + Recording recording = new Recording()) { + rf.writeBytes("1234567890abcdefghij"); + ch.position(0); + ByteBuffer buffer = ByteBuffer.allocateDirect(10); + ByteBuffer other = ByteBuffer.allocateDirect(10); + + recording.disable(disabledEvent); + recording.enable(enabledEvent).withThreshold(Duration.ofMillis(0)); + recording.start(); + IOEvent expectedEvent = operation.execute(file, ch, buffer, other); + recording.stop(); + IOHelper.verifyEquals(Events.fromRecording(recording), List.of(expectedEvent)); + } + } + + @FunctionalInterface + private interface FileChannelOperation { + IOEvent execute(File file, FileChannel ch, ByteBuffer buffer, ByteBuffer other) throws Exception; + } } From 4464e946a802eb83b54f9a97bbead0e769a7fff5 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 2 Oct 2026 12:38:30 +0000 Subject: [PATCH 22/98] 8393377: JFR: Improve documentation for 'jfr query' Reviewed-by: mgronlun --- .../jdk/jfr/internal/query/QueryPrinter.java | 25 +++++++++++++------ .../classes/jdk/jfr/internal/tool/Query.java | 4 ++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryPrinter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryPrinter.java index 9d0ce279abd..c60432ca4d6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryPrinter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryPrinter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -245,13 +245,13 @@ public static String getGrammarText() { column ::= "COLUMN" text ("," text)* format ::= "FORMAT" formatter ("," formatter)* formatter ::= property (";" property)* - select ::= "SELECT" "*" | expression ("," expression)* + select ::= "SELECT" ("*" | expression ("," expression)*) expression ::= (aggregator | field) [alias] aggregator ::= function "(" (field | "*") ")" alias ::= "AS" symbol from ::= "FROM" source ("," source)* source ::= type [alias] - where ::= condition ("AND" condition)* + where ::= "WHERE" condition ("AND" condition)* condition ::= field "=" text groupBy ::= "GROUP BY" field ("," field)* orderBy ::= "ORDER BY" orderField ("," orderField)* @@ -263,10 +263,14 @@ public static String getGrammarText() { - symbol, alphabetic characters - type, the event type name, for example SystemGC. To avoid ambiguity, the name may be qualified, for example jdk.SystemGC - - field, the event field name, for example stackTrace. + - field, an event field name or a dot-separated name to a nested + field, for example, stackTrace or stackTrace.topFrame.method.type. To avoid ambiguity, the name may be qualified, for example jdk.SystemGC.stackTrace. A type alias declared in a FROM clause - can be used instead of the type, for example S.eventThread + can be used instead of the type, for example S.eventThread. + To select the same field from a subset of event types, put + the event types or aliases in brackets and separate them by '|', + for example, [A|B].duration. - function, determines how fields are aggregated when using GROUP BY. Aggregate functions are: AVG: The numeric average @@ -289,14 +293,19 @@ public static String getGrammarText() { - property, any of the following: none No formatting for the column cell-height: Maximum height of a table cell - missing:whitespace Replace missing values (N/A) with blank space + missing: Replaces missing values (N/A) with the specified text, + for example "null-bootstrap". Use missing:whitespace to replace with + blank space. normalized Normalize values between 0 and 1.0 for the column truncate-beginning if value can't fit a table cell, remove the first characters truncate-end if value can't fit a table cell, remove the last characters If no value exist, or a numeric value can't be aggregated, the result is 'N/A', - unless missing:whitespace is used. The top frame of a stack trace can be referred' - to as stackTrace.topFrame. When multiple event types are specified in a FROM clause, + unless missing: is used. The top frame of a stack trace can be referred + to as stackTrace.topFrame, the name of an event type as eventType.name, and + its label as eventType.label. + + When multiple event types are specified in a FROM clause, the union of the event types are used (not the cartesian product) To see all available events, use the query '"SHOW EVENTS"'. To see all fields for diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Query.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Query.java index 280bef55af4..c8cc0b5cec2 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Query.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Query.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,6 +94,8 @@ public void displayOptionUsage(PrintStream p) { p.println(" GROUP BY gcId"); p.println(" ORDER BY gcId\" recording.jfr"); p.println(); + p.println(" To see more queries and their output: $ jfr view --verbose all-views recording.jfr"); + p.println(); p.println("************************************ WARNING ******************************************"); p.println("The query command is only available in debug builds and is targeted towards OpenJDK"); p.println("developers. The tool may be removed or its syntax changed at any time."); From d13fcae6b732900ff444d1ae72a238f3c0140d8e Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 2 Oct 2026 12:45:54 +0000 Subject: [PATCH 23/98] 8393171: JFR: -XX:StartFlightRecording can't handle leading space for jfc options Reviewed-by: mgronlun --- .../classes/jdk/jfr/internal/jfc/model/Utilities.java | 7 ++++--- test/jdk/jdk/jfr/tool/TestConfigure.java | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java index 281a0c57949..223c383aff5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/jfc/model/Utilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,6 +100,7 @@ static void checkValid(String value, Object... valid) { } static String parseTimespan(String s) { + s = s.strip(); StringBuilder sb = new StringBuilder(); try { for (TimespanUnit timespan : TimespanUnit.values()) { @@ -118,7 +119,7 @@ static String parseTimespan(String s) { } private static String parseForUnit(String s, String unit) { - String number = s.trim().substring(0, s.length() - unit.length()); - return Long.parseLong(number.trim()) + " " + unit; + String number = s.substring(0, s.length() - unit.length()); + return Long.parseLong(number.strip()) + " " + unit; } } diff --git a/test/jdk/jdk/jfr/tool/TestConfigure.java b/test/jdk/jdk/jfr/tool/TestConfigure.java index 404c657fd98..2171ea640ab 100644 --- a/test/jdk/jdk/jfr/tool/TestConfigure.java +++ b/test/jdk/jdk/jfr/tool/TestConfigure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,6 +108,9 @@ private static void testTimespan() throws Throwable { expected.put("com.example.Lion#period", "3 s"); aseertEqual(outputSetting, expected); + + OutputAnalyzer o = jfrConfigure("locking-threshold= 20ms "); + o.shouldHaveExitValue(0); } private static void testUnification() throws Throwable { From b15e0c1d96d6645aa9e7ae221027b48f846e83e8 Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Fri, 2 Oct 2026 13:11:53 +0000 Subject: [PATCH 24/98] 8393184: RISC-V: Use compiler atomic memory ordering for add and xchg Reviewed-by: ayang, fyang, gli --- .../linux_riscv/atomicAccess_linux_riscv.hpp | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp index 81cd2c51c65..07caf1774d9 100644 --- a/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/atomicAccess_linux_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,8 +30,9 @@ // Implementation of class AtomicAccess -// Note that memory_order_conservative requires a full barrier after atomic stores. -// See https://patchwork.kernel.org/patch/3575821/ +// Note that memory_order_conservative requires a strong two-way barrier. +// For 32- and 64-bit add and xchg that barrier is provided by an explicit +// AMO with .aqrl. Cmpxchg still uses explicit full barriers. #if defined(__clang_major__) #define FULL_COMPILER_ATOMIC_SUPPORT @@ -42,31 +43,41 @@ template struct AtomicAccess::PlatformAdd { template - D add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { - -#ifndef FULL_COMPILER_ATOMIC_SUPPORT - // If we add add and fetch for sub word and are using older compiler - // it must be added here due to not using lib atomic. - static_assert(byte_size >= 4); -#endif + D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const; - if (order != memory_order_relaxed) { - FULL_MEM_BARRIER; - } + template + D add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const { + D value = fetch_then_add(dest, add_value, order) + add_value; + return value; + } +}; - D res = __atomic_add_fetch(dest, add_value, __ATOMIC_RELAXED); +template +template +inline D AtomicAccess::PlatformAdd::fetch_then_add(D volatile* dest, I add_value, + atomic_memory_order order) const { + static_assert(byte_size == sizeof(D)); + static_assert(byte_size == sizeof(I)); + static_assert(byte_size == 4 || byte_size == 8); - if (order != memory_order_relaxed) { - FULL_MEM_BARRIER; - } - return res; + if (order == memory_order_relaxed) { + return __atomic_fetch_add(dest, add_value, __ATOMIC_RELAXED); } - template - D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const { - return add_then_fetch(dest, add_value, order) - add_value; + D old_value; + if constexpr (byte_size == 4) { + __asm__ __volatile__ ("amoadd.w.aqrl %0, %2, %1" + : "=r" (old_value), "+A" (*dest) + : "r" (add_value) + : "memory"); + } else { + __asm__ __volatile__ ("amoadd.d.aqrl %0, %2, %1" + : "=r" (old_value), "+A" (*dest) + : "r" (add_value) + : "memory"); } -}; + return old_value; +} #ifndef FULL_COMPILER_ATOMIC_SUPPORT template<> @@ -160,25 +171,26 @@ template inline T AtomicAccess::PlatformXchg::operator()(T volatile* dest, T exchange_value, atomic_memory_order order) const { -#ifndef FULL_COMPILER_ATOMIC_SUPPORT - // If we add xchg for sub word and are using older compiler - // it must be added here due to not using lib atomic. - static_assert(byte_size >= 4); -#endif - static_assert(byte_size == sizeof(T)); static_assert(byte_size == 4 || byte_size == 8); - if (order != memory_order_relaxed) { - FULL_MEM_BARRIER; + if (order == memory_order_relaxed) { + return __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); } - T res = __atomic_exchange_n(dest, exchange_value, __ATOMIC_RELAXED); - - if (order != memory_order_relaxed) { - FULL_MEM_BARRIER; + T old_value; + if constexpr (byte_size == 4) { + __asm__ __volatile__ ("amoswap.w.aqrl %0, %2, %1" + : "=r" (old_value), "+A" (*dest) + : "r" (exchange_value) + : "memory"); + } else { + __asm__ __volatile__ ("amoswap.d.aqrl %0, %2, %1" + : "=r" (old_value), "+A" (*dest) + : "r" (exchange_value) + : "memory"); } - return res; + return old_value; } // __attribute__((unused)) on dest is to get rid of spurious GCC warnings. From 5c097cf61ef42078c04dc6e238c23459a41e111d Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Fri, 2 Oct 2026 13:47:44 +0000 Subject: [PATCH 25/98] 8392856: RISC-V: Remove redundant register copy in narrow-value Zacas cmpxchg Reviewed-by: gli, fyang, ayang --- src/hotspot/cpu/riscv/macroAssembler_riscv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 8295f9f15a4..6e6ca69da08 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -4643,7 +4643,7 @@ void MacroAssembler::cmpxchg_narrow_value(Register addr, Register expected, // Or in the new value to create complete new value. orr(scratch0, scratch0, new_val); - mv(scratch1, result); // save our expected value + // scratch1 holds the expected word. atomic_cas(result, scratch0, aligned_addr, operand_size::int32, acquire, release); bne(scratch1, result, retry); } else { @@ -4726,7 +4726,7 @@ void MacroAssembler::weak_cmpxchg_narrow_value(Register addr, Register expected, // Or in the new value to create complete new value. orr(scratch0, scratch0, new_val); - mv(scratch1, result); // save our expected value + // scratch1 holds the expected word. atomic_cas(result, scratch0, aligned_addr, operand_size::int32, acquire, release); bne(scratch1, result, fail); // This weak, so just bail-out. } else { From e90e46919999fb8e65140efe2d9bb2a870ec99a6 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 2 Oct 2026 14:41:23 +0000 Subject: [PATCH 26/98] 8393446: [Valhalla] The assert added by JDK-8389623 is too strong Reviewed-by: thartmann, roland --- src/hotspot/share/opto/cfgnode.cpp | 3 +- .../valhalla/valuetypes/TestNullPtrPhi.java | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullPtrPhi.java diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index fe6ccd7f23b..9fdbb72c131 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2919,7 +2919,8 @@ class PushValueTypeDown { for (int i = _nodes_from_phi.size() - 1; i >= 0; i--) { Node* n = _nodes_from_phi.at(i); if (!n->is_ValueType()) { - assert(!n->is_Phi() || n->bottom_type()->make_oopptr() != nullptr, "broken graph"); + assert(!n->is_Phi() || _phase->type(n)->is_zero_type() || + n->bottom_type()->make_oopptr() != nullptr, "broken graph"); _nodes_from_phi.remove(i); } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullPtrPhi.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullPtrPhi.java new file mode 100644 index 00000000000..b5cd14a061e --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullPtrPhi.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8393446 + * @summary [Valhalla] The assert added by JDK-8389623 is too strong + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::test -Xbatch + * ${test.main.class} + */ + +package compiler.valhalla.valuetypes; + +public class TestNullPtrPhi { + + static value class Val {} + + interface SomeObject { + Object val(); + } + + // To avoid creating new exception on each call. + static final RuntimeException EXCEPTION = new RuntimeException(); + + static final class NotValue implements SomeObject { + public Object val() { + throw EXCEPTION; + } + } + + static final class NotNullValue implements SomeObject { + public Object val() { + return new Val(); + } + } + + public static void main(String[] args) { + SomeObject notVal = new NotValue(); + SomeObject notNullVal = new NotNullValue(); + for (int i = 0; i < 20_000; i++) { + boolean notNull = (i & 1) == 0; + Object result = test(notNull ? notNullVal : notVal); + if (notNull != (result != null)) { + throw new RuntimeException("wrong result"); + } + } + } + + static Object test(SomeObject obj) { + Object result = null; + try { + result = obj.val(); + } catch (Throwable t) { + // Keep result == null + } + return result; + } +} From ac4da2a1d77bba952b0066608b748c37a923a6e6 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Fri, 2 Oct 2026 16:31:17 +0000 Subject: [PATCH 27/98] 8392198: JFR: JFR.stop again allows a missing recording name Reviewed-by: mgronlun --- src/jdk.jcmd/share/man/jcmd.md | 5 +- .../jdk/jfr/internal/dcmd/DCmdStop.java | 8 +++- .../jdk/jfr/jcmd/TestJcmdStopMissingName.java | 47 +++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 test/jdk/jdk/jfr/jcmd/TestJcmdStopMissingName.java diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index 97e7385138b..00813bad360 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -509,7 +509,7 @@ The following commands are available: **Note:** The *options* must be specified using either *key* or *key*`=`*value* - syntax. If no parameters are entered, then no recording is stopped. + syntax. *options*: @@ -519,7 +519,8 @@ The following commands are available: timestamp, respectively. If no path is provided, the data from the recording is discarded. (FILE, no default value) - - `name`: (Optional) Name of the recording (STRING, no default value) + - `name`: Name of the recording. Use `JFR.check` to list the names of + the running recordings. (STRING, no default value) `JFR.view` \[*options*\] diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java index cda6a03e596..5d00534e83d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,9 @@ final class DCmdStop extends AbstractDCmd { @Override protected void execute(ArgumentParser parser) throws DCmdException { parser.checkUnknownArguments(); + if (!parser.checkMandatory()) { + throw new DCmdException("Missing mandatory argument 'name'. Use JFR.check to list recording names."); + } String name = parser.getOption("name"); String filename = parser.getOption("filename"); try { @@ -85,7 +88,8 @@ public String[] getHelp() { Note: If a path is given, '%%p' in the path will be replaced by the PID, and '%%t' will be replaced by the time in 'yyyy_MM_dd_HH_mm_ss' format. - name Name of the recording (STRING, no default value) + name (Mandatory) Name of the recording. Use JFR.check to list the names of the + running recordings. (STRING, no default value) Options must be specified using the or = syntax. diff --git a/test/jdk/jdk/jfr/jcmd/TestJcmdStopMissingName.java b/test/jdk/jdk/jfr/jcmd/TestJcmdStopMissingName.java new file mode 100644 index 00000000000..063dca7c433 --- /dev/null +++ b/test/jdk/jdk/jfr/jcmd/TestJcmdStopMissingName.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.jcmd; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify JFR.stop reports an error when the name parameter is missing + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.jcmd.TestJcmdStopMissingName + */ +public class TestJcmdStopMissingName { + public static void main(String[] args) throws Exception { + String name = "foo"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + output = JcmdHelper.jcmd("JFR.stop"); + output.shouldContain("Missing mandatory argument 'name'. Use JFR.check to list recording names."); + JcmdHelper.assertRecordingIsRunning(name); + JcmdHelper.stopAndCheck(name); + } +} From 59b2293abc5bb4db9ff0fe4958dd3e7c08911eab Mon Sep 17 00:00:00 2001 From: Patrick Fontanilla Date: Fri, 2 Oct 2026 16:56:14 +0000 Subject: [PATCH 28/98] 8393152: Shenandoah: Further optimize HdrSeq memory footprint Reviewed-by: wkemper, xpeng, ruili --- .../gc/shenandoah/shenandoahNumberSeq.cpp | 39 +++++++++++++------ .../gc/shenandoah/shenandoahNumberSeq.hpp | 6 ++- .../shenandoah/shenandoahScanRemembered.hpp | 2 +- .../shenandoah/test_shenandoahNumberSeq.cpp | 14 +++++++ 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp index 381861cd4f7..1abac823bdc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.cpp @@ -32,14 +32,16 @@ #include #include -HdrSeq::HdrSeq() : _minimum(DBL_MAX) { - _hdr = NEW_C_HEAP_ARRAY(int*, MagBuckets, mtGC); - for (int c = 0; c < MagBuckets; c++) { - _hdr[c] = nullptr; - } +HdrSeq::HdrSeq() : + _hdr(nullptr), + _minimum(DBL_MAX) { } HdrSeq::~HdrSeq() { + if (_hdr == nullptr) { + return; + } + for (int c = 0; c < MagBuckets; c++) { int* sub = _hdr[c]; if (sub != nullptr) { @@ -49,7 +51,18 @@ HdrSeq::~HdrSeq() { FREE_C_HEAP_ARRAY(_hdr); } +void HdrSeq::allocate_hdr() { + if (_hdr == nullptr) { + _hdr = NEW_C_HEAP_ARRAY(int*, MagBuckets, mtGC); + for (int c = 0; c < MagBuckets; c++) { + _hdr[c] = nullptr; + } + } +} + void HdrSeq::add(double val) { + allocate_hdr(); + if (val < 0) { assert (false, "value (%8.2f) is not negative", val); val = 0; @@ -112,7 +125,7 @@ double HdrSeq::minimum() const { } double HdrSeq::percentile(double level) const { - if (level == 0) { + if (level == 0 || num() == 0) { return minimum(); } @@ -145,6 +158,8 @@ void HdrSeq::add(const HdrSeq& other) { return; } + allocate_hdr(); + for (int mag = 0; mag < MagBuckets; mag++) { int* other_bucket = other._hdr[mag]; if (other_bucket == nullptr) { @@ -185,11 +200,13 @@ void HdrSeq::add(const HdrSeq& other) { void HdrSeq::clear() { // Clear the storage - for (int mag = 0; mag < MagBuckets; mag++) { - int* bucket = _hdr[mag]; - if (bucket != nullptr) { - for (int c = 0; c < ValBuckets; c++) { - bucket[c] = 0; + if (_hdr != nullptr) { + for (int mag = 0; mag < MagBuckets; mag++) { + int* bucket = _hdr[mag]; + if (bucket != nullptr) { + for (int c = 0; c < ValBuckets; c++) { + bucket[c] = 0; + } } } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp index 0d3c515680a..02361f07fdb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNumberSeq.hpp @@ -27,6 +27,7 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHNUMBERSEQ_HPP #include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/numberSeq.hpp" // HDR sequence stores the low-resolution high-dynamic-range values. @@ -46,8 +47,11 @@ class HdrSeq: public NumberSeq { MagBuckets = 56, MagMinimum = -32 }; - int** _hdr; + int** _hdr; // Lazily allocated on first add() and freed in the destructor. double _minimum; + NONCOPYABLE(HdrSeq); + + void allocate_hdr(); public: HdrSeq(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index 2c60d4340f5..e5ed4c056a2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -749,7 +749,7 @@ class ShenandoahScanRemembered: public CHeapObj { delete _scc; if (ShenandoahEnableCardStats) { for (uint i = 0; i < ParallelGCThreads; i++) { - delete _card_stats[i]; + delete[] _card_stats[i]; } FREE_C_HEAP_ARRAY(_card_stats); _card_stats = nullptr; diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp index a7340040999..6b726d2b54c 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahNumberSeq.cpp @@ -191,3 +191,17 @@ TEST_VM(ShenandoahNumberSeq, large_value) { EXPECT_EQ(seq.maximum(), max_dirty_scan_obj_cnt); EXPECT_EQ(seq.percentile(100), max_dirty_scan_obj_cnt); } + +TEST_VM(ShenandoahNumberSeq, empty_hdr) { + HdrSeq empty; + EXPECT_EQ(empty.num(), 0); + for (int i = 0; i <= 100; i += 10) { + EXPECT_EQ(empty.percentile(i), 0); + } + empty.clear(); + EXPECT_EQ(empty.num(), 0); + + HdrSeq other; + empty.add(other); + EXPECT_EQ(empty.num(), 0); +} From b22ccb9af5fc7d097f5bf24e0e87a1c02c5810dd Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 2 Oct 2026 18:13:02 +0000 Subject: [PATCH 29/98] 8393280: Shenandoah: Nested GC state resetter is stuck in wrong state Reviewed-by: wkemper, xpeng, ruili --- .../gc/shenandoah/shenandoahRootVerifier.cpp | 42 ++++++++++--------- .../gc/shenandoah/shenandoahRootVerifier.hpp | 8 ++-- .../gc/shenandoah/shenandoahVerifier.cpp | 2 + 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index eb4eb78fa6d..5a4be901cc0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -46,19 +46,23 @@ #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" -Atomic ShenandoahGCStateResetter::_active_count; +Atomic ShenandoahGCStateResetter::_is_active; ShenandoahGCStateResetter::ShenandoahGCStateResetter() : - _heap(ShenandoahHeap::heap()), - _saved_gc_state(_heap->gc_state()), - _saved_gc_state_changed(_heap->_gc_state_changed) { + _heap(ShenandoahHeap::heap()) { + + shenandoah_assert_safepoint(); + assert(!_is_active.load_relaxed(), "No nested resets"); + + // Capture the GC state right away. + _saved_gc_state = _heap->gc_state(); + _saved_gc_state_changed = _heap->_gc_state_changed; // Need to complete GC processing before deactivating the barriers. // Once the GC state is dropped, we cannot allow GC-state dependent fixups, // that would patch barriers or process the oops incorrectly. Verifier code // can enter stack watermark processing as part of regular thread root work. // This pretends Java threads have fixed up all state before we go for verification. - // Do this unconditionally in all callers to get to the same consensus point. for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { StackWatermarkSet::finish_processing(jt, nullptr, StackWatermarkKind::gc); } @@ -68,29 +72,27 @@ ShenandoahGCStateResetter::ShenandoahGCStateResetter() : ShenandoahSATBMarkQueueSet& satb_qs = ShenandoahBarrierSet::satb_mark_queue_set(); satb_qs.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(Thread::current())); - // From this moment on, level-1 resetter is active. - if (_active_count.compare_set(0, 1, memory_order_relaxed)) { - // Clear state to deactivate barriers. Indicate that state has changed - // so that verifier threads will use this value, rather than thread local - // values (which we are _not_ changing here). - _heap->_gc_state.clear(); - _heap->_gc_state_changed = true; - } + // Flip the state to zero. Indicate that state has changed so that verifier threads + // will use this value, rather than thread local values (which we are _not_ changing here). + _heap->_gc_state.clear(); + _heap->_gc_state_changed = true; + + // GC state is now reset. + _is_active.store_relaxed(true); + + assert(_heap->gc_state() == 0, "Should have been cleared"); } ShenandoahGCStateResetter::~ShenandoahGCStateResetter() { - if (_active_count.add_then_fetch(-1, memory_order_relaxed) > 0) { - // Nested, nothing to do. - return; - } - _heap->_gc_state.set(_saved_gc_state); _heap->_gc_state_changed = _saved_gc_state_changed; assert(_heap->gc_state() == _saved_gc_state, "Should be restored"); + + _is_active.store_relaxed(false); } void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops, ShenandoahGeneration* generation) { - ShenandoahGCStateResetter resetter; + assert(ShenandoahGCStateResetter::is_active(), "Must be active"); shenandoah_assert_safepoint(); NMethodToOopClosure blobs(oops, !NMethodToOopClosure::FixRelocations); @@ -116,7 +118,7 @@ void ShenandoahRootVerifier::roots_do(OopIterateClosure* oops, ShenandoahGenerat } void ShenandoahRootVerifier::strong_roots_do(OopIterateClosure* oops, ShenandoahGeneration* generation) { - ShenandoahGCStateResetter resetter; + assert(ShenandoahGCStateResetter::is_active(), "Must be active"); shenandoah_assert_safepoint(); CLDToOopClosure clds(oops, ClassLoaderData::_claim_none); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp index a41249abb3c..a240be96c47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.hpp @@ -32,17 +32,17 @@ class ShenandoahGCStateResetter : public StackObj { private: - static Atomic _active_count; + static Atomic _is_active; + char _saved_gc_state; + bool _saved_gc_state_changed; ShenandoahHeap* const _heap; - const char _saved_gc_state; - const bool _saved_gc_state_changed; public: ShenandoahGCStateResetter(); ~ShenandoahGCStateResetter(); - static bool is_active() { return _active_count.load_relaxed() > 0; } + static bool is_active() { return _is_active.load_relaxed(); } }; class ShenandoahRootVerifier : public AllStatic { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index be24582de91..43cc7b73792 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -1318,11 +1318,13 @@ class ShenandoahVerifyInToSpaceClosure : public BasicOopIterateClosure { void ShenandoahVerifier::verify_roots_in_to_space(ShenandoahGeneration* generation) { ShenandoahVerifyInToSpaceClosure cl; + ShenandoahGCStateResetter resetter; ShenandoahRootVerifier::roots_do(&cl, generation); } void ShenandoahVerifier::verify_roots_no_forwarded(ShenandoahGeneration* generation) { ShenandoahVerifyNoForwarded cl; + ShenandoahGCStateResetter resetter; ShenandoahRootVerifier::roots_do(&cl, generation); } From 661bd45630e169e44c89ca7c306b56c0c95ab247 Mon Sep 17 00:00:00 2001 From: Min Choi Date: Sat, 3 Oct 2026 10:11:30 +0000 Subject: [PATCH 30/98] 8393337: (tz) Update Timezone Data to 2026e Reviewed-by: naoto, andrew --- src/java.base/share/data/tzdata/VERSION | 2 +- src/java.base/share/data/tzdata/europe | 30 ++++- src/java.base/share/data/tzdata/northamerica | 118 +++++++++++++++--- test/jdk/java/util/TimeZone/Bug6329116.java | 7 +- .../java/util/TimeZone/TimeZoneData/VERSION | 2 +- .../TimeZone/TimeZoneData/displaynames.txt | 2 +- 6 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/java.base/share/data/tzdata/VERSION b/src/java.base/share/data/tzdata/VERSION index 35a783728e8..3261b6ad2f3 100644 --- a/src/java.base/share/data/tzdata/VERSION +++ b/src/java.base/share/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2026d +tzdata2026e diff --git a/src/java.base/share/data/tzdata/europe b/src/java.base/share/data/tzdata/europe index ca09f4fa3ed..0634d475e3f 100644 --- a/src/java.base/share/data/tzdata/europe +++ b/src/java.base/share/data/tzdata/europe @@ -401,13 +401,33 @@ # as a negative daylight saving time in winter. So, in this database # IST stands for Irish Summer Time for timestamps before 1968, and for # Irish Standard Time after that. See: -# http://www.irishstatutebook.ie/eli/1971/act/17/enacted/en/print +# https://www.irishstatutebook.ie/eli/1971/act/17/enacted/en/print # Michael Deckers (2017-06-01) gave the following URLs for Ireland's # Summer Time Act, 1925 and Summer Time Orders, 1926 and 1947: -# http://www.irishstatutebook.ie/eli/1925/act/8/enacted/en/print -# http://www.irishstatutebook.ie/eli/1926/sro/919/made/en/print -# http://www.irishstatutebook.ie/eli/1947/sro/71/made/en/print +# https://www.irishstatutebook.ie/eli/1925/act/8/enacted/en/print +# https://www.irishstatutebook.ie/eli/1926/sro/919/made/en/print +# https://www.irishstatutebook.ie/eli/1947/sro/71/made/en/print + +# From Stan Ulbrych (2026-09-12): +# Ireland's Summer Time Act, 1925 (No. 8 of 1925, see above) +# ended summer time at 02:00 GMT on the day after the third Saturday in +# September, that is, 1925-09-20. The UK's Summer Time Act, 1925, however, +# moved the UK's end date to the day after the first Saturday in October with +# immediate effect, but it did not apply to the Irish Free State. +# Ireland matched the October date only from 1926, via the Summer Time Order, +# 1926, whose recital replaces the period fixed by section 2 of the 1925 Act +# and mentions no earlier order. Moving approval of that order in the +# Dáil on 1926-03-24, Minister Kevin O'Higgins said: +# +# Deputies will remember that last year there was a discrepancy of a +# fortnight between the termination of Summer Time in the Saorstát and +# in Great Britain and Northern Ireland. As a result of that, serious +# inconvenience was caused to railway companies, and there was a certain +# amount of dislocation of postal services in the country. +# +# (See: https://www.oireachtas.ie/en/debates/debate/dail/1926-03-24/18/) +# So Dublin fell back on 1925-09-20, two weeks before London. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S # Summer Time Act, 1916 @@ -560,6 +580,8 @@ Zone Europe/Dublin -0:25:21 - LMT 1880 Aug 2 -0:25:21 - DMT 1916 May 21 2:00s -0:25:21 1:00 IST 1916 Oct 1 2:00s 0:00 GB-Eire %s 1921 Dec 6 # independence + 0:00 GB-Eire GMT/IST 1925 Sep Sun>=16 2:00s + 0:00 - GMT 1926 0:00 GB-Eire GMT/IST 1940 Feb 25 2:00s 0:00 1:00 IST 1946 Oct 6 2:00s 0:00 - GMT 1947 Mar 16 2:00s diff --git a/src/java.base/share/data/tzdata/northamerica b/src/java.base/share/data/tzdata/northamerica index 8be797d61d9..0b053aaefc2 100644 --- a/src/java.base/share/data/tzdata/northamerica +++ b/src/java.base/share/data/tzdata/northamerica @@ -1769,7 +1769,36 @@ Zone America/Toronto -5:17:32 - LMT 1895 -5:00 Canada E%sT 1946 -5:00 Toronto E%sT 1974 -5:00 Canada E%sT -# For Atikokan see America/Panama. +# From Tim Parenti (2026-09-21), per Paul Eggert (2000-10-02, 2006-07-17): +# In the northwest of the province, most portions of Kenora, Rainy River, and +# Thunder Bay Districts lying broadly west of 90°W longitude have historically +# been aligned with neighboring Manitoba and use America/Winnipeg, in +# accordance with section 2(2) of Ontario's Time Act, RSO 1990 chapter T.9: +# https://www.ontario.ca/laws/statute/90t09 +# With Manitoba now opting to stay on -05 year-round, if any of these areas do +# not follow, it would require a new zone; however, given the short leadtime +# ahead of a potential divergence on 2026-11-01 and in the interest of not +# creating zones that may not be needed, assume for the time being that they +# will. +# +# In practice, there are several exceptions to the 90°W dividing line set by +# the province, all of which technically contravene the official Ontario rules: +# - An area around Big Trout Lake in Kenora District observes Central Time +# (-06/-05) despite extending east of 90°W. +# - An area around Upsala in the southwest of Thunder Bay District observes +# Eastern Time (-05/-04) like America/Toronto despite extending west of +# 90°W. +# - Atikokan in the east of Rainy River District has observed year-round -05 +# (EST) since at least 1952; this matches America/Panama since our 1970 +# cutoff. +# - Pickle Lake and the Mishkeegogamang First Nation (formerly New Osnaburgh) +# in Kenora District have also observed year-round -05 (EST) since at least +# 1998, though it is less clear how far back that arrangement goes; assume +# the area is covered in the same way as Atikokan. +# See the 'backzone' file for further commentary. +# At time of writing, it is not yet known to what extent Manitoba's 2026-09-17 +# announcement may spur further adaptations in northwestern Ontario; see +# Manitoba, below. # Manitoba @@ -1804,6 +1833,40 @@ Zone America/Toronto -5:17:32 - LMT 1895 # the 1987 version would apply - the changeover was at 2:00 Central # Standard Time (i.e. not until 3:00 Central Daylight Time). +# From Tim Parenti (2026-09-29): +# As much of western Canada began announcing year-round DST in early 2026, +# Manitoba Premier Wab Kinew publicly mused on forming a "Prairie time zone" by +# joining Saskatchewan and Alberta on year-round -06, while committing to +# conducting a public engagement survey on the matter. The results, however, +# showed that while "[a] total of 92 per cent of Manitobans surveyed wanted to +# see an end to seasonal time changes", "[a] majority of Manitobans (58 per +# cent) preferred to stay on permanent DST, while 34 per cent voted to +# eliminate the time change but stay on standard time." +# +# The survey results were published on Thursday 2026-09-17 alongside a policy +# announcement adopting permanent DST, or year-round -05, just over 6 weeks +# before clocks would otherwise fall back on 2026-11-01. +# https://news.gov.mb.ca/news/?item=75397 +# https://www.cbc.ca/news/canada/manitoba/daylight-time-manitoba-9.7347844 +# +# While other potential options would have required further legislation, the +# Official Time Amendment Act (Bill 2 of the 42nd Legislature's 5th Session), +# assented to 2023-05-30, provides for year-round -05 upon proclamation. +# https://web2.gov.mb.ca/laws/statutes/2023/pdf/c00423.pdf +# Order in Council 221/2026, issued 2026-09-23, directed the proclamation +# bringing the Act into force on 2026-10-31, 26 hours before clocks would +# otherwise fall back. +# https://oic.gov.mb.ca/OICDocs/2026/09/Municipal%20and%20Northern%20Relations.260923.%20Official%20Time%20Amendment%20Act.2212026.pdf +# https://web2.gov.mb.ca/laws/statutes/proclamations/2023c4(2026-10-31).pdf +# +# The Act further allows for the Lieutenant Governor in Council to, on +# recommendation of the relevant minister, "make regulations varying the +# reckoning" of official time in Manitoba, provided the minister has "due +# regard for the applicable time zone in neighbouring jurisdictions." +# +# Use a temporary hack like Alberta’s to work around potential CLDR +# limitations. + # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Winn 1916 only - Apr 23 0:00 1:00 D Rule Winn 1916 only - Sep 17 0:00 0 S @@ -1832,7 +1895,11 @@ Rule Winn 1987 2005 - Apr Sun>=1 2:00s 1:00 D # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Winnipeg -6:28:36 - LMT 1887 Jul 16 -6:00 Winn C%sT 2006 - -6:00 Canada C%sT + -6:00 Canada C%sT 2026 Oct 31 + # Temporary hack; see above. + -6:00 Canada CDT 2026 Nov 1 2:00 + # End of temporary hack. + -5:00 - EST # Saskatchewan @@ -1910,6 +1977,12 @@ Zone America/Winnipeg -6:28:36 - LMT 1887 Jul 16 # for those areas to observe either "UTC-5, UTC-6 or UTC-7 for all or part of # the year" on at least 30 days' notice. # The new Act comes into force by order of the Lieutenant Governor in Council. +# From Tim Parenti (2026-09-21): +# With Manitoba now adopting year-round -05, the area around Creighton will +# likely wish to follow. As the area doesn't have a special charter like +# Lloydminster, the existing de facto arrangement technically violates +# provincial law already, but once the new Act takes effect it will better +# allow for continued alignment with Manitoba, per the above. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Regina 1918 only - Apr 14 2:00 1:00 D @@ -2068,13 +2141,13 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # on March 8 will be the last time change, ending twice-yearly clock changes.” # https://news.gov.bc.ca/releases/2026AG0013-000209 # -# From Paul Eggert (2026-07-02): +# From Paul Eggert (2026-09-23): # The law says that 21 hours after the usual 2026-03-08 02:00 switch from -# PST to PDT, the next day inaugurates the new standard time Pacific Time, +# PST to PDT, the next day inaugurated the new standard time Pacific Time, # i.e., just one clock change but two name changes separated by 21 hours. # PT, the obvious abbreviation for Pacific Time, is one letter too short # to conform to TZDB’s (and POSIX’s) [-+[:alnum:]]{3,6} requirements. -# I asked the BC government for advice, with no response. For now, do this: +# For now, do this: # 1. As a temporary hack, pretend that the BC law takes effect # not on 2026-03-09 at 00:00, but on 2026-11-01 at 02:00. # This pretense works around a limitation in CLDR 48.1 (2026-01-08), @@ -2092,10 +2165,10 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # and already used in parts of BC and in Yukon # PDT - almost as software-friendly, but confusing because it implies # it is DST and is paired with PST, whereas PT is standard time -# PST - straightforward but even more confusing, -# and will likely break much software that assumes PST is -08 # -07 - accurate and clear in itself, but makes BC look odd vs neighbors -# PacT - straightforward but novel abbreviation for Pacific Time +# PCT - as I understand it, inspired by the phrase “Pacific Coast Time” +# though legally it’s “Pacific Time”; suggested by the BC government +# # From Tim Parenti (2026-08-21): # In the southeast of the province, the Regional District of East Kootenay @@ -2121,18 +2194,23 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep # 2026-08-14 meeting. # https://www.cbc.ca/news/canada/british-columbia/east-kootenay-stops-changing-clocks-9.7308201 # -# From Tim Parenti (2026-09-10): -# Although on 2026-09-01 the Town of Golden voted instead to split its -# timekeeping practice from RDEK and rejoin BC on year-round -07, this was met -# with significant pushback from residents in Golden and CSRD Area A. In light -# of these public comments, CSRD voted on 2026-09-10 to defer its decision on -# timekeeping in Area A “to gather and assess further information before a -# decision is implemented”. As such, at time of writing, it seems likely that -# Golden may soon revisit its decision as well. -# -# For now, these areas remain aligned with America/Edmonton, but if they -# diverge, a new zone with a name like 'America/Brit_Columbia/Golden' would be -# needed. +# From Tim Parenti (2026-09-24): +# Echoing RDEK’s earlier false step, the Town of Golden initially opted for +# year-round -07 by a 4–2 Council vote on 2026-09-01. This was met with +# significant pushback from residents preferring to maintain the existing +# alignment with RDEK and Alberta, with a formal petition swiftly gathering +# 2,395 signatures in two weeks from the town of 3,986 and some smaller +# neighboring communities. In response, Council unanimously rescinded the +# resolution at the following meeting on 2026-09-22, and unanimously replaced +# it with year-round -06 to keep the regional alignment. +# Acknowledging that “its initial move was driven in part by concerns about +# dark winter mornings and pedestrian safety for children travelling to +# school”, the new resolution directs town staff to partner on safety supports +# and commits to reviewing the decision and its impacts in October 2027. +# https://www.ctvnews.ca/calgary/article/golden-bc-council-reverses-course-on-time-zone-opts-for-mountain-daylight-time/ +# At time of writing, the surrounding CSRD Area A is yet to formalize its own +# decision following a 2026-09-10 deferral “to gather and assess further +# information”, but is expected to follow. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Vanc 1918 only - Apr 14 2:00 1:00 D diff --git a/test/jdk/java/util/TimeZone/Bug6329116.java b/test/jdk/java/util/TimeZone/Bug6329116.java index e23d9f44fd4..7ed0c555a2f 100644 --- a/test/jdk/java/util/TimeZone/Bug6329116.java +++ b/test/jdk/java/util/TimeZone/Bug6329116.java @@ -26,7 +26,7 @@ * @bug 6329116 6756569 6757131 6758988 6764308 6796489 6834474 6609737 6507067 * 7039469 7090843 7103108 7103405 7158483 8008577 8059206 8064560 8072042 * 8077685 8151876 8166875 8169191 8170316 8176044 8174269 8347841 8347955 - * 8388214 8392377 + * 8388214 8392377 8393337 * @summary Make sure that timezone short display names are identical to Olson's data. * @run junit Bug6329116 */ @@ -49,7 +49,10 @@ public class Bug6329116 { "Canada/Mountain", "MST", "America/Yellowknife", "MST", "America/Vancouver", "PST", - "Canada/Pacific", "PST"); + "Canada/Pacific", "PST", + "America/Winnipeg", "CST", + "Canada/Central", "CST", + "America/Rainy_River", "CST"); // Do not test all locales, as some locales have localized // short names in CLDR. Test only for the US locale diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION index a19030a9b59..376af12e303 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/VERSION +++ b/test/jdk/java/util/TimeZone/TimeZoneData/VERSION @@ -1 +1 @@ -tzdata2026d +tzdata2026e diff --git a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt index 889d1dfc4b7..3236241bf2b 100644 --- a/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt +++ b/test/jdk/java/util/TimeZone/TimeZoneData/displaynames.txt @@ -88,7 +88,7 @@ America/Tijuana PST PDT America/Toronto EST EDT America/Vancouver MST America/Whitehorse MST -America/Winnipeg CST CDT +America/Winnipeg EST America/Yakutat AKST AKDT Antarctica/Macquarie AEST AEDT Asia/Beirut EET EEST From f3701c80216900f3ded26f9de1befe43813be95c Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Sat, 3 Oct 2026 14:25:23 +0000 Subject: [PATCH 31/98] 8255404: jdk/jfr/api/consumer/recordingstream/TestOnEvent.java timed out Reviewed-by: mgronlun --- .../share/jfr/support/jfrJdkJfrEvent.cpp | 8 ++- test/jdk/jdk/jfr/jvm/TestClinitStart.java | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 test/jdk/jdk/jfr/jvm/TestClinitStart.java diff --git a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp index 0e7f706b210..5370938d192 100644 --- a/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp +++ b/src/hotspot/share/jfr/support/jfrJdkJfrEvent.cpp @@ -62,7 +62,7 @@ static bool initialize(TRAPS) { /* * Abstract klasses are filtered out unconditionally. - * If a klass is not yet initialized, i.e yet to run its + * If a klass is not initialized and is not running, * it is also filtered out so we don't accidentally * trigger initialization. */ @@ -72,7 +72,11 @@ static bool is_allowed(const Klass* k) { // Was excluded during initial class load. return false; } - return !(k->is_abstract() || k->should_be_initialized()); + if (k->is_abstract()) { + return false; + } + const InstanceKlass* ik = InstanceKlass::cast(k); + return ik->is_initialized() || ik->is_being_initialized(); } static void fill_klasses(GrowableArray& event_subklasses, const InstanceKlass* event_klass, JavaThread* thread) { diff --git a/test/jdk/jdk/jfr/jvm/TestClinitStart.java b/test/jdk/jdk/jfr/jvm/TestClinitStart.java new file mode 100644 index 00000000000..fae8cdd1cd0 --- /dev/null +++ b/test/jdk/jdk/jfr/jvm/TestClinitStart.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.jvm; + +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Event; +import jdk.jfr.Name; +import jdk.jfr.consumer.RecordingStream; + +/** + * @test TestClinitStart + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.jvm.TestClinitStart + */ +public class TestClinitStart { + private static final String EVENT_NAME = "ClinitStart"; + private static final CountDownLatch registration = new CountDownLatch(1); + private static final CountDownLatch recordingStart = new CountDownLatch(1); + + @Name(EVENT_NAME) + private static class ClinitEvent extends Event { + static { + // FlightRecorder.register(ClinitEvent.class) instrumentation + // is added here by the JVM + registration.countDown(); + try { + recordingStart.await(); + } catch (InterruptedException e) { + throw new InternalError(e); + } + } + } + + public static void main(String... args) throws Exception { + Thread.ofPlatform().start(() -> new ClinitEvent().commit()); + registration.await(); + try (RecordingStream rs = new RecordingStream()) { + rs.enable(EVENT_NAME); + rs.onEvent(EVENT_NAME, e -> rs.close()); + rs.startAsync(); + recordingStart.countDown(); + rs.awaitTermination(); + } + } +} From 0f0b3678b0d9f631b999ec8861329b1fdd048f12 Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Sun, 4 Oct 2026 10:39:32 +0000 Subject: [PATCH 32/98] 8393488: RISC-V: Make float16ToFloat ignore upper argument bits Reviewed-by: fyang, dzhang --- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 9 +- src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 7 +- .../TestFloat16ToFloatUnnormalized.java | 83 +++++++++++++++++++ 3 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/float16/TestFloat16ToFloatUnnormalized.java diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 440ad6f5483..99898e251ee 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2380,8 +2380,11 @@ static void float16_to_float_slow_path(C2_MacroAssembler& masm, C2GeneralStub(dst, src, tmp, 20, float16_to_float_slow_path); + auto stub = C2CodeStub::make(dst, src, tmp, 28, float16_to_float_slow_path); // On riscv, NaN needs a special process as fcvt does not work in that case. // On riscv, Inf does not need a special process as fcvt can handle it correctly. diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 0265f98a733..074f80297aa 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -7473,8 +7473,11 @@ class StubGenerator: public StubCodeGenerator { // construct a NaN in 32 bits from the NaN in 16 bits, // we need the payloads of non-canonical NaNs to be preserved. __ mv(t1, 0x7f800000); - // sign-bit was already set via sign-extension if necessary. - __ slli(t0, src, 13); + // The upper 16 bits of a short argument are unspecified. Sign-extend the + // low 16 bits before shifting so that the float sign bit comes from the + // float16 sign bit. + __ sext(t0, src, 16); + __ slli(t0, t0, 13); __ orr(t1, t0, t1); __ fmv_w_x(dst, t1); diff --git a/test/hotspot/jtreg/compiler/intrinsics/float16/TestFloat16ToFloatUnnormalized.java b/test/hotspot/jtreg/compiler/intrinsics/float16/TestFloat16ToFloatUnnormalized.java new file mode 100644 index 00000000000..075dd13fc58 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/float16/TestFloat16ToFloatUnnormalized.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Alibaba Group Holding Limited. All Rights Reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8393488 + * @summary Verify float16ToFloat ignores bits above the low 16 bits of its argument + * @requires vm.compiler1.enabled & vm.compiler2.enabled + * @run main/othervm -Xverify:all -Xint -XX:+UnlockDiagnosticVMOptions + * -XX:DisableIntrinsic=_float16ToFloat TestFloat16ToFloatUnnormalized + * @run main/othervm -Xverify:all -Xint TestFloat16ToFloatUnnormalized + * @run main/othervm -Xverify:all -Xcomp -Xbatch -XX:TieredStopAtLevel=1 + * -XX:CompileCommand=compileonly,UnnormalizedFloat16::convert TestFloat16ToFloatUnnormalized + * @run main/othervm -Xverify:all -Xcomp -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,UnnormalizedFloat16::convert TestFloat16ToFloatUnnormalized + */ + +import java.lang.classfile.ClassFile; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import static java.lang.constant.ConstantDescs.CD_float; +import static java.lang.constant.ConstantDescs.CD_int; +import static java.lang.constant.ConstantDescs.CD_short; + +public class TestFloat16ToFloatUnnormalized { + private static final int[] HIGH_BITS = { + 0x0000_0000, 0xffff_0000, 0x8000_0000, + 0x0004_0000, 0x5555_0000, 0xaaaa_0000 + }; + + public static void main(String[] args) throws Throwable { + byte[] bytes = ClassFile.of().build(ClassDesc.of("UnnormalizedFloat16"), cb -> + cb.withMethodBody("convert", MethodTypeDesc.of(CD_float, CD_int), + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, code -> { + // Unlike Java source, bytecode accepts int for short parameters; omit i2s to keep upper bits. + code.iload(0) + .invokestatic(ClassDesc.of("java.lang.Float"), "float16ToFloat", + MethodTypeDesc.of(CD_float, CD_short)) + .freturn(); + })); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class generatedClass = lookup.defineClass(bytes); + MethodHandle convert = lookup.findStatic(generatedClass, "convert", + MethodType.methodType(float.class, int.class)); + + for (int lowBits = 0; lowBits <= 0xffff; lowBits++) { + int expected = Float.floatToIntBits(Float.float16ToFloat((short) lowBits)); + for (int highBits : HIGH_BITS) { + int input = highBits | lowBits; + int actual = Float.floatToIntBits((float) convert.invokeExact(input)); + if (actual != expected) { + throw new AssertionError("input=0x%08x: expected=0x%08x, actual=0x%08x" + .formatted(input, expected, actual)); + } + } + } + } +} From 97865d54376df612d3ff876c54cfff055ff0a800 Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Mon, 5 Oct 2026 05:15:46 +0000 Subject: [PATCH 33/98] 8392770: [test] VM properties that checks for the presence of CDS archives do not work on Windows Reviewed-by: iklam, dholmes, adinn --- test/jtreg-ext/requires/VMProps.java | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index a3ce27f484b..185d967487c 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -403,14 +403,24 @@ protected String vmCDS() { return "" + (noJvmtiAdded && WB.isCDSIncluded()); } + // Returns a platform-aware path for the specified archive file. + private Path archivePath(String archiveName) { + String archiveSubdir = (Platform.isWindows() ? "bin" : "lib"); + return Paths.get(System.getProperty("java.home"), archiveSubdir, "server", archiveName); + } + + // Returns true if a CDS archive file with specified name exists. + private boolean archivePathExists(String archiveName) { + return Files.exists(archivePath(archiveName)); + } + /** * Check for CDS default archive existence. * * @return true if CDS default archive classes.jsa exists in the JDK to be tested. */ protected String vmCDSDefaultArchiveAvailable() { - Path archive = Paths.get(System.getProperty("java.home"), "lib", "server", "classes.jsa"); - return "" + ("true".equals(vmCDS()) && Files.exists(archive)); + return "" + ("true".equals(vmCDS()) && archivePathExists("classes.jsa")); } /** @@ -419,8 +429,7 @@ protected String vmCDSDefaultArchiveAvailable() { * @return true if CDS archive classes_nocoops.jsa exists in the JDK to be tested. */ protected String vmCDSNocoopsArchiveAvailable() { - Path archive = Paths.get(System.getProperty("java.home"), "lib", "server", "classes_nocoops.jsa"); - return "" + ("true".equals(vmCDS()) && Files.exists(archive)); + return "" + ("true".equals(vmCDS()) && archivePathExists("classes_nocoops.jsa")); } /** @@ -429,8 +438,7 @@ protected String vmCDSNocoopsArchiveAvailable() { * @return true if CDS archive classes_nocoh.jsa exists in the JDK to be tested. */ protected String vmCDSNocohArchiveAvailable() { - Path archive = Paths.get(System.getProperty("java.home"), "lib", "server", "classes_nocoh.jsa"); - return "" + ("true".equals(vmCDS()) && Files.exists(archive)); + return "" + ("true".equals(vmCDS()) && archivePathExists("classes_nocoh.jsa")); } /** From 5d8b5a11260fbcfb7a5bd52222047fc7c138d06d Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Mon, 5 Oct 2026 05:31:52 +0000 Subject: [PATCH 34/98] 8388474: RISC-V: Relax satp mode check for sv57 Co-authored-by: April Ivy Reviewed-by: fyang, dzhang --- src/hotspot/cpu/riscv/vm_version_riscv.cpp | 8 ------- .../linux_riscv/vm_version_linux_riscv.cpp | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 7f6fd0d1513..fbf61492d4c 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -104,14 +104,6 @@ void VM_Version::common_initialize() { setup_cpu_available_features(); - // check if satp.mode is supported, currently supports up to SV48(RV64) - if (satp_mode.value() > VM_SV48 || satp_mode.value() < VM_MBARE) { - vm_exit_during_initialization( - err_msg( - "Unsupported satp mode: SV%d. Only satp modes up to sv48 are supported for now.", - (int)satp_mode.value())); - } - if (UseRVA20U64) { useRVA20U64Profile(); } diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 934f272036c..4c154f3ea40 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -26,10 +26,13 @@ #include "asm/register.hpp" #include "logging/log.hpp" +#include "os_linux.hpp" #include "riscv_hwprobe.hpp" +#include "runtime/java.hpp" #include "runtime/os.hpp" #include "runtime/os.inline.hpp" #include "runtime/vm_version.hpp" +#include "utilities/formatBuffer.hpp" #include #include @@ -275,6 +278,26 @@ char* VM_Version::os_uarch_additional_features() { mode = VM_MBARE; } fclose(f); + + // The code generator assumes that every address it embeds in an instruction + // sequence fits in 48 bits, see movptr() and mov_metadata(). On SV57 hardware + // that only holds because the kernel clamps the window it allocates from by + // default to the SV48 range: MMAP_VA_BITS_64 is MIN(VA_BITS, VA_BITS_SV48), + // so DEFAULT_MAP_WINDOW and STACK_TOP stay at 2^47 on SV48 and SV57 alike. + // SV57 kernels before Linux v6.6 lack that clamp, allocate from the full SV57 + // range and cannot be supported. + if (mode >= VM_SV57) { + long major, minor, patch; + os::Linux::kernel_version(&major, &minor, &patch); + if (os::Linux::kernel_version_compare(major, minor, patch, 6, 6, 0) == -1) { + vm_exit_during_initialization( + err_msg( + "Satp mode SV%d requires a Linux kernel of 6.6 or later, which restricts " + "the default address space to the sv48 range (current kernel is %ld.%ld.%ld).", + (int)mode, major, minor, patch)); + } + } + satp_mode.enable_feature(mode); return ret; } From 344a6d2b2c947f0d851531a2e2d3e9d519de8f27 Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Mon, 5 Oct 2026 06:00:42 +0000 Subject: [PATCH 35/98] 8393375: Several CDS tests fail in cross-build configurations due to missing classes.jsa Reviewed-by: iklam, sspitsyn, mbaesken --- .../ErrorHandling/AccessZeroNKlassHitsProtectionZone.java | 2 ++ test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java | 1 + .../hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java | 1 + .../jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java | 3 ++- .../jvmti/RedefineClasses/RedefineLeakThrowable.java | 3 ++- .../jvmti/RedefineClasses/RedefineSharedClass.java | 3 ++- 6 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java index 061361de794..c3f6ece3d3b 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java @@ -41,6 +41,7 @@ * @test id=no_coh_cds * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone * @requires vm.cds & vm.bits == 64 & vm.debug == true & vm.flagless + * @requires vm.cds.default.archive.available * @requires os.family != "aix" * @comment This test relies on crashing which conflicts with ASAN checks * @requires !vm.asan @@ -71,6 +72,7 @@ * @test id=coh_cds * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone * @requires vm.cds & vm.bits == 64 & vm.debug == true & vm.flagless + * @requires vm.cds.default.archive.available * @requires os.family != "aix" * @comment This test relies on crashing which conflicts with ASAN checks * @requires !vm.asan diff --git a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java index 2c796243cbf..82c89f5ddfa 100644 --- a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java +++ b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java @@ -25,6 +25,7 @@ * @test TestCDSVMCrash * @summary Verify that an exception is thrown when the VM crashes during executeAndLog * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.flagless * @modules java.base/jdk.internal.misc * @library /test/lib diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java index 2dd9725295a..48b70972851 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java @@ -30,6 +30,7 @@ * via the -vmoptions command line option of JTREG. vm.gc==null will help the test case to discard the explicitly passed * vm options. * @requires (vm.gc=="null") + * @requires vm.cds.default.archive.available * @summary Test command line flag combinations that * could likely affect the behaviour of AppCDS * @library /test/lib diff --git a/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java index 409e37e10a1..4a7e09af98b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/complexURI/ComplexURITest.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024 JetBrains s.r.o. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ * @bug 8339460 * @requires vm.cds * @requires vm.cds.custom.loaders + * @requires vm.cds.default.archive.available * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @compile mypackage/Main.java mypackage/Another.java diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineLeakThrowable.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineLeakThrowable.java index 11aabb2952a..838924cef99 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineLeakThrowable.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineLeakThrowable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ * @requires vm.jvmti * @requires vm.flagless * @requires vm.cds + * @requires vm.cds.default.archive.available * @modules java.base/jdk.internal.misc * @modules java.instrument * java.compiler diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineSharedClass.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineSharedClass.java index 3ff063833c3..73f3e680eac 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineSharedClass.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineSharedClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @summary Verify should_clean_previous_versions when run with retransformation and CDS * @requires vm.jvmti * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.opt.final.ClassUnloading * @requires vm.flagless * @library /test/lib From 782aefe8f7718004343dacfd4492a9d4ca6ee09e Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Mon, 5 Oct 2026 06:05:38 +0000 Subject: [PATCH 36/98] 8393447: Do not limit heap size when creating AOT cache Reviewed-by: kvn, asmehra --- src/hotspot/share/cds/aotMetaspace.cpp | 16 ---------------- src/hotspot/share/cds/heapShared.cpp | 15 ++++++++++++--- src/hotspot/share/cds/heapShared.hpp | 4 ++-- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 69eb6a56f6a..11ad8820984 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1033,22 +1033,6 @@ void AOTMetaspace::init_heap_settings() { if (!CDSConfig::is_dumping_heap() || UseCompressedOops) { return; } - // CDS heap dumping requires all string oops to have an offset - // from the heap bottom that can be encoded in 32-bit. - julong max_heap_size = (julong)(4 * G); - - if (MinHeapSize > max_heap_size) { - log_debug(aot)("Setting MinHeapSize to 4G for CDS dumping, original size = %zuM", MinHeapSize/M); - FLAG_SET_ERGO(MinHeapSize, max_heap_size); - } - if (InitialHeapSize > max_heap_size) { - log_debug(aot)("Setting InitialHeapSize to 4G for CDS dumping, original size = %zuM", InitialHeapSize/M); - FLAG_SET_ERGO(InitialHeapSize, max_heap_size); - } - if (MaxHeapSize > max_heap_size) { - log_debug(aot)("Setting MaxHeapSize to 4G for CDS dumping, original size = %zuM", MaxHeapSize/M); - FLAG_SET_ERGO(MaxHeapSize, max_heap_size); - } } #endif // INCLUDE_CDS_JAVA_HEAP && _LP64 diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 31b3481421c..bb0d1abc3d6 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -518,15 +518,24 @@ int HeapShared::append_root(oop obj) { return obj_info->root_index(); } else { assert(obj_info->root_index() < 0, "must not be zero"); - int i = _pending_roots->append(obj); + int i = add_to_pending_roots(obj); obj_info->set_root_index(i); return i; } } else { - return _pending_roots->append(obj); + return add_to_pending_roots(obj); } } +int HeapShared::add_to_pending_roots(oop obj) { + if (_pending_roots->length() == (int)max_jint) { + // This should never happen, but just in case we have HUGE caches ... + AOTMetaspace::unrecoverable_writing_error("too many pending roots"); + ShouldNotReachHere(); + } + return _pending_roots->append(obj); +} + int HeapShared::get_root_index(oop obj) { if (java_lang_Class::is_instance(obj)) { obj = scratch_java_mirror(obj); @@ -736,7 +745,7 @@ void HeapShared::init_dumping() { _scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable(); _dumptime_resolved_methods = new (mtClassShared) GrowableArray(100, mtClassShared); _pending_roots = new GrowableArrayCHeap(500); - _pending_roots->append(nullptr); // root index 0 represents a null oop + add_to_pending_roots(nullptr); // root index 0 represents a null oop DEBUG_ONLY(_dumptime_classes_with_cached_oops = new (mtClassShared)ArchivableKlassTable()); } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 0ac4758d6ee..afdd819f612 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -334,7 +334,7 @@ class HeapShared: AllStatic { static bool has_been_seen_during_subgraph_recording(oop obj); static void set_has_been_seen_during_subgraph_recording(oop obj); static bool archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgraph_info); - + static int add_to_pending_roots(oop obj); static void resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]); static void resolve_classes_for_subgraph_of(JavaThread* current, Klass* k); static void clear_archived_roots_of(Klass* k); @@ -438,7 +438,7 @@ class HeapShared: AllStatic { // Returns -1 if obj is not in the heap root set. static int get_root_index(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(-1); - static GrowableArrayCHeap* pending_roots() { return _pending_roots; } + static GrowableArrayCHeap const* pending_roots() { return _pending_roots; } // Dump-time and runtime static objArrayOop root_segment(int segment_idx); From 183311f937d5dcf69423f12135da8421869488da Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Mon, 5 Oct 2026 06:10:31 +0000 Subject: [PATCH 37/98] 8393374: java/nio/file/WatchService/DeleteInterference.java fails sometimes on AIX Reviewed-by: mbaesken --- test/jdk/java/nio/file/WatchService/DeleteInterference.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/nio/file/WatchService/DeleteInterference.java b/test/jdk/java/nio/file/WatchService/DeleteInterference.java index fc889ad873e..9dc620e5d51 100644 --- a/test/jdk/java/nio/file/WatchService/DeleteInterference.java +++ b/test/jdk/java/nio/file/WatchService/DeleteInterference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Red Hat, Inc. and/or its affiliates. + * Copyright (c) 2016, 2026, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ */ import java.io.IOException; +import java.nio.file.DirectoryIteratorException; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -107,7 +108,7 @@ private static void deleteFileTree(Path file) { } } Files.delete(file); - } catch (IOException ioe) { + } catch (IOException | DirectoryIteratorException ioe) { // ignore } } From e25c346ae2685b6779328fa987e12f5fdc905daf Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Mon, 5 Oct 2026 06:18:23 +0000 Subject: [PATCH 38/98] 8392661: [s390x] [riscv] Incorrectly attempt to reserve address space for compressed classes using unscaled encoding instead of below 4GiB Reviewed-by: stuefe, fyang --- .../cpu/riscv/compressedKlass_riscv.cpp | 21 +++++++++++++------ src/hotspot/cpu/s390/compressedKlass_s390.cpp | 2 +- ...essedCPUSpecificClassSpaceReservation.java | 4 ++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp b/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp index a2e732025d5..5881b378d60 100644 --- a/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp +++ b/src/hotspot/cpu/riscv/compressedKlass_riscv.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,11 +45,19 @@ char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size // 3) only bits in C: a 4GB-aligned address that is lower than 16TB. // 4) only bits in D: a 16TB-aligned address. - // First, attempt to allocate < 4GB. We do this unconditionally: - // - if can_optimize_for_zero_base, a <4GB mapping start would allow us to run unscaled (base = 0, shift = 0) - // - if !can_optimize_for_zero_base, a <4GB mapping start is still good, the resulting immediate can be encoded - // with one instruction (2) - result = reserve_address_space_for_unscaled_encoding(size, aslr); + // First, attempt to reserve below 4GB, regardless of CDS or COH: + // - With neither CDS nor COH, this permits unscaled encoding (base = 0, shift = 0). + // - With CDS, when the VM includes archived-heap support or COH is enabled, + // the base must match the archive mapping start (including the protection zone), + // and the shift must preserve the precomputed narrow Klass IDs. + // - With COH, unscaled encoding spans only 4MB (22-bit narrowKlass). + // Without CDS, we choose the klass range start as the base to avoid + // unnecessarily large shifts, rather than attempting zero-based encoding. + // In the nonzero-base cases, low addresses keep base materialization cheap, + // though not necessarily a single instruction: on RV64, a nonzero page-aligned + // base below 2GB needs only LUI, while bases in [2GB, 4GB) require additional + // instructions because LUI sign-extends its result. + result = reserve_address_space_below_4G(size, aslr); // Failing that, optimize for case (3) - a base with only bits set between [32-44) if (result == nullptr) { diff --git a/src/hotspot/cpu/s390/compressedKlass_s390.cpp b/src/hotspot/cpu/s390/compressedKlass_s390.cpp index 1fa2c47ad21..2b86334aece 100644 --- a/src/hotspot/cpu/s390/compressedKlass_s390.cpp +++ b/src/hotspot/cpu/s390/compressedKlass_s390.cpp @@ -35,7 +35,7 @@ char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size // First, attempt to allocate < 4GB. We do this unconditionally: // - if optimize_for_zero_base, a <4GB mapping start allows us to use base=0 shift=0 // - if !optimize_for_zero_base, a <4GB mapping start allows us to use algfi - result = reserve_address_space_for_unscaled_encoding(size, aslr); + result = reserve_address_space_below_4G(size, aslr); // Failing that, try optimized for base=0 shift>0 if (result == nullptr && optimize_for_zero_base) { diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java index d4287d34294..89c39b57435 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedCPUSpecificClassSpaceReservation.java @@ -94,13 +94,13 @@ private static void do_test(boolean CDS) throws IOException { } output.shouldMatch(tryReserveFor16bitMoveIntoQ3Regex); } else if (Platform.isRISCV64()) { - output.shouldContain(tryReserveForUnscaled); // unconditionally + output.shouldContain(tryReserveBelow4G); // unconditionally // bits 32..44 output.shouldContain("reserve_between (range [0x0000000100000000-0x0000100000000000)"); // bits 44..64 output.shouldContain("reserve_between (range [0x0000100000000000-0xffffffffffffffff)"); } else if (Platform.isS390x()) { - output.shouldContain(tryReserveForUnscaled); // unconditionally + output.shouldContain(tryReserveBelow4G); // unconditionally if (CDS) { output.shouldNotContain(tryReserveForZeroBased); } else { From 97d3a6a2628f8e1589b962b7cc02c7fc1f9d1b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Mon, 5 Oct 2026 07:07:37 +0000 Subject: [PATCH 39/98] 8392949: compiler/valhalla/valuetypes/TestValueArgEscapeState.java fails in valhalla-comp-stress Co-authored-by: Quan Anh Mai Reviewed-by: thartmann, qamai --- .../valhalla/valuetypes/TestValueArgEscapeState.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueArgEscapeState.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueArgEscapeState.java index 09dedf5c1a6..3df4cd930a7 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueArgEscapeState.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueArgEscapeState.java @@ -68,6 +68,11 @@ static value class ValueHolderHolder { } } + // Just to force initialization of ValueHolder and ValueHolderHolder before + // the tests are compiled, in case they are run with warm-up disabled. + static final ValueHolderHolder LOAD_VALUE_HOLDERS = + new ValueHolderHolder(new ValueHolder(null)); + static IdentityObject globalObj = null; static ValueHolder globalHolder = null; From 51a9c010c95ba4f36bf04f496e8ab49d8773d2a8 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Mon, 5 Oct 2026 07:20:24 +0000 Subject: [PATCH 40/98] 8390511: [Valhalla] Problems with array access using VarHandle Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/library_call.cpp | 19 +++++++ .../TestArrayVarHandleDifferentLayouts.java | 57 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayVarHandleDifferentLayouts.java diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 5bcd29695e9..72043317b37 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2507,6 +2507,25 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c // Save state and restore on bailout SavedState old_state(this); + if (const TypeAryPtr* base_ary_type = _gvn.type(base)->isa_aryptr(); type == T_OBJECT && base_ary_type != nullptr) { + // Read an object from a primitive array, or a Phi some of whose inputs are primitive arrays + if (base_ary_type->elem()->make_ptr() == nullptr) { + return false; + } + + // Must not be a flat array + if (base_ary_type->is_flat()) { + return false; + } + + // Refine the base so alias analysis can work properly + Node* refined_base = must_be_not_null(base, false); + base_ary_type = base_ary_type->cast_to_not_flat(); + refined_base = _gvn.transform(new CheckCastPPNode(control(), base, base_ary_type, ConstraintCastNode::DependencyType::NonFloatingNarrowing)); + replace_in_map(base, refined_base); + base = refined_base; + } + Node* adr = make_unsafe_address(base, offset, type, kind == Relaxed); assert(!stopped(), "Inlining of unsafe access failed: address construction stopped unexpectedly"); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayVarHandleDifferentLayouts.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayVarHandleDifferentLayouts.java new file mode 100644 index 00000000000..0eea7257244 --- /dev/null +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayVarHandleDifferentLayouts.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.valhalla.valuetypes; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import jdk.internal.value.ValueClass; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8390511 + * @summary Test VarHandle access on an array that is a merge of different layouts. + * @library /test/lib / + * @enablePreview + * @modules java.base/jdk.internal.value + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=1 + * -XX:CompileOnly=${test.main.class}::* ${test.main.class} + */ +public class TestArrayVarHandleDifferentLayouts { + private static final int INDEX = 8; + private static final VarHandle HANDLE = MethodHandles.arrayElementVarHandle(Integer[].class); + private static final Integer[] FLAT = new Integer[64]; + private static final Integer[] REFERENCE = (Integer[]) ValueClass.newReferenceArray(Integer.class, 64); + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + testGet((i & 1) == 0); + } + } + + static Integer testGet(boolean useFlat) { + Integer[] array = useFlat ? FLAT : REFERENCE; + return (Integer)HANDLE.getVolatile(array, INDEX); + } +} From 505226bde4b8a30a5e6282164441c9afcd978bb6 Mon Sep 17 00:00:00 2001 From: Vladimir Yaroslavskiy Date: Mon, 5 Oct 2026 07:43:41 +0000 Subject: [PATCH 41/98] 8393047: Arrays.parallelSort sorting of objects is not stable Reviewed-by: dl --- .../java/util/ArraysParallelSortHelpers.java | 4 +- test/jdk/java/util/Arrays/Sorting.java | 76 +++++++++++++------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java b/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java index 5fca0cd41d6..c040bca7656 100644 --- a/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java +++ b/src/java.base/share/classes/java/util/ArraysParallelSortHelpers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,7 @@ public final void compute() { T split = a[(rh = rn >>> 1) + rb]; for (int lo = 0; lo < lh; ) { int lm = (lo + lh) >>> 1; - if (c.compare(split, a[lm + lb]) <= 0) + if (c.compare(split, a[lm + lb]) < 0) lh = lm; else lo = lm + 1; diff --git a/test/jdk/java/util/Arrays/Sorting.java b/test/jdk/java/util/Arrays/Sorting.java index 3e2ab741e31..3c1e8ed50ea 100644 --- a/test/jdk/java/util/Arrays/Sorting.java +++ b/test/jdk/java/util/Arrays/Sorting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,11 +50,11 @@ public class Sorting { // Array lengths used in a long run (default) private static final int[] LONG_RUN_LENGTHS = { - 1, 3, 8, 21, 55, 100, 1_000, 10_000, 100_000 }; + 1, 3, 8, 21, 55, 100, 1_000, 17_000, 100_000 }; // Array lengths used in a short run private static final int[] SHORT_RUN_LENGTHS = { - 1, 8, 55, 100, 10_000 }; + 1, 8, 55, 100, 17_000 }; // Random initial values used in a long run (default) private static final TestRandom[] LONG_RUN_RANDOMS = { @@ -185,14 +185,32 @@ private void testEmptyArray() { } private void testStability(int length, TestRandom random) { - printTestName("Test stability", random, length); + for (int m = 1; m <= 2 * length; m <<= 1) { + testStability(length, m, random); + } + } + + private void testStability(int length, int m, TestRandom random) { + printTestName("Test stability [" + m + "]", random, length); + + Pair[] a = buildRandom(length, m, random); + sortingHelper.sort(a); + checkSorted(a); + checkStable(a); + checkStableRandom(a); + + a = buildRandom(length, m, random); + sortingHelper.sort(a, pairComparator); + checkSorted(a); + checkStable(a); + checkStableRandom(a); - Pair[] a = build(length, random); + a = buildEqual(length, m, random); sortingHelper.sort(a); checkSorted(a); checkStable(a); - a = build(length, random); + a = buildEqual(length, m, random); sortingHelper.sort(a, pairComparator); checkSorted(a); checkStable(a); @@ -391,38 +409,52 @@ private void checkSorted(Point[] a, Comparator c) { } } - private void checkStable(Pair[] a) { - for (int i = 0; i < a.length / 4; ) { + private void checkStableRandom(Pair[] a) { + for (int i = 0; i < a.length / 3; ) { int key1 = a[i].getKey(); int value1 = a[i++].getValue(); int key2 = a[i].getKey(); int value2 = a[i++].getValue(); int key3 = a[i].getKey(); int value3 = a[i++].getValue(); - int key4 = a[i].getKey(); - int value4 = a[i++].getValue(); - if (!(key1 == key2 && key2 == key3 && key3 == key4)) { + if (!(key1 == key2 && key2 == key3)) { fail("Keys are different " + key1 + ", " + key2 + ", " + - key3 + ", " + key4 + " at position " + i); + key3 + " at position " + i); } - if (!(value1 < value2 && value2 < value3 && value3 < value4)) { + if (!(value1 < value2 && value2 < value3)) { fail("Sorting is not stable at position " + i + ". Second values have been changed: " + value1 + ", " + - value2 + ", " + value3 + ", " + value4); + value2 + ", " + value3); + } + } + } + + private void checkStable(Pair[] a) { + for (int i = 1; i < a.length; ++i) { + if (a[i - 1].getKey() == a[i].getKey() && a[i - 1].getValue() > a[i].getValue()) { + fail("Sorting is not stable at position " + i + ": " + a[i - 1] + " and " + a[i]); } } } - private Pair[] build(int length, Random random) { - Pair[] a = new Pair[length * 4]; + private Pair[] buildRandom(int length, int m, Random random) { + Pair[] a = new Pair[3 * length]; + + for (int i = 0, k = 0; i < a.length; ) { + int key = random.nextInt(m); + a[i++] = new Pair(key, ++k); + a[i++] = new Pair(key, ++k); + a[i++] = new Pair(key, ++k); + } + return a; + } + + private Pair[] buildEqual(int length, int m, Random random) { + Pair[] a = new Pair[length]; - for (int i = 0; i < a.length; ) { - int key = random.nextInt(); - a[i++] = new Pair(key, 1); - a[i++] = new Pair(key, 2); - a[i++] = new Pair(key, 3); - a[i++] = new Pair(key, 4); + for (int i = 0; i < a.length; ++i) { + a[i] = new Pair(random.nextInt(m), i); } return a; } From fa3fee257b1e0ad5bcb8d21fcaa6a27758c62589 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Mon, 5 Oct 2026 07:51:56 +0000 Subject: [PATCH 42/98] 8393508: Disable StressCallingConvention in TestC2CCalls.java until JDK-8393507 is fixed Reviewed-by: chagedorn --- .../jtreg/compiler/valhalla/valuetypes/TestC2CCalls.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC2CCalls.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC2CCalls.java index 3cefa700e11..9ce6f5050db 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC2CCalls.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC2CCalls.java @@ -26,6 +26,7 @@ * @key randomness * @summary Test value class calling convention with compiled to compiled calls. * @library /test/lib /compiler/whitebox / + * @requires (vm.opt.StressCallingConvention == null | !vm.opt.StressCallingConvention) * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -38,6 +39,7 @@ * @key randomness * @summary Test value class calling convention with compiled to compiled calls. * @library /test/lib /compiler/whitebox / + * @requires (vm.opt.StressCallingConvention == null | !vm.opt.StressCallingConvention) * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -53,6 +55,7 @@ * @key randomness * @summary Test value class calling convention with compiled to compiled calls. * @library /test/lib /compiler/whitebox / + * @requires (vm.opt.StressCallingConvention == null | !vm.opt.StressCallingConvention) * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -68,6 +71,7 @@ * @key randomness * @summary Test value class calling convention with compiled to compiled calls. * @library /test/lib /compiler/whitebox / + * @requires (vm.opt.StressCallingConvention == null | !vm.opt.StressCallingConvention) * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -83,6 +87,7 @@ * @key randomness * @summary Test value class calling convention with compiled to compiled calls. * @library /test/lib /compiler/whitebox / + * @requires (vm.opt.StressCallingConvention == null | !vm.opt.StressCallingConvention) * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox From 8b60fd1c9c9cfffe45a5497675b62457cc92f5b9 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Mon, 5 Oct 2026 08:05:49 +0000 Subject: [PATCH 43/98] 8393182: Improve compiler stress testing Reviewed-by: rcastanedalo, qamai, mchevalier --- .../arguments/TestMethodArguments.java | 19 ++-- .../compiler/c1/TestConcurrentPatching.java | 5 +- .../c2/irTests/TestPrunedExHandler.java | 6 +- .../TestOverloadCompileQueues.java | 5 +- .../hotcode/StressHotCodeCollector.java | 4 +- .../compiler/intrinsics/sha/TestDigest.java | 96 ++++++++----------- .../sha/sanity/DigestSanityTestBase.java | 6 +- .../lib/ir_framework/test/TestVM.java | 2 - .../superword/TestEquivalentInvariants.java | 6 +- .../tiered/Level2RecompilationTest.java | 5 +- .../valuetypes/CorrectlyRestoreRfp.java | 2 +- ...PutFlatValueWithoutUseArrayFlattening.java | 11 ++- .../valuetypes/TestArrayCopyWithOops.java | 21 ++-- .../valuetypes/TestArrayNullMarkers.java | 35 ++++--- .../compiler/valhalla/valuetypes/TestC1.java | 89 ++++++++++++++--- .../valuetypes/TestCallingConvention.java | 3 - .../valuetypes/TestCallingConventionC1.java | 77 +++++++++++++-- .../valuetypes/TestGetfieldChains.java | 6 +- .../valuetypes/TestMergeMemCleanup.java | 12 ++- .../valuetypes/TestMismatchHandling.java | 20 ++-- .../valhalla/valuetypes/TestNewAcmp.java | 3 +- .../valuetypes/TestNullableValueTypes.java | 50 +++++++--- .../TestReturnBufferClassInitialization.java | 13 ++- .../valuetypes/TestSpeculateArrayAccess.java | 20 ++-- .../valhalla/valuetypes/TestTearing.java | 46 ++++----- .../valuetypes/TestTrivialMethods.java | 5 +- .../valhalla/valuetypes/TestValueClasses.java | 3 - .../valuetypes/TestVirtualThreads.java | 81 ++++++++-------- .../TestFloat16VectorOperations.java | 20 ++-- .../VectorUnsignedMinMaxOperationsTest.java | 19 +--- .../valhalla/valuetypes/AnnotationsTests.java | 13 +-- .../valuetypes/FlatArrayCopyingTest.java | 14 ++- .../FlatArrayLargeOverlapCopyTest.java | 5 +- .../valuetypes/FlatFieldAccessorMethods.java | 4 +- .../valuetypes/FlattenableSemanticTest.java | 5 +- .../valhalla/valuetypes/ObjectMethods.java | 17 +--- .../valuetypes/TestFieldNullability.java | 3 +- .../valuetypes/TestFlatteningBudget.java | 7 +- .../valhalla/valuetypes/VolatileTest.java | 3 +- .../ConcurrentClassLoadingTest.java | 5 +- ...oadFailuresDoNotImpactApplicationTest.java | 13 +-- .../AdaptiveBlocking001.java | 7 +- 42 files changed, 467 insertions(+), 319 deletions(-) diff --git a/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java b/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java index 306d0176aad..b29f393c30c 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java +++ b/test/hotspot/jtreg/compiler/arguments/TestMethodArguments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ * number of parameters (according to the JVM spec). * @library /test/lib / * @requires vm.compMode != "Xcomp" - * @run driver/timeout=1000 compiler.arguments.TestMethodArguments + * @run driver/timeout=1000 ${test.main.class} */ package compiler.arguments; @@ -48,6 +48,7 @@ public class TestMethodArguments { static final int MIN = 0; static final int MAX = 255; static final int INPUT_SIZE = 100; + static final int WARMUP_ITERATIONS = 100; public static Template.ZeroArgs generateTest(PrimitiveType type, int numberOfArguments) { String arguments = IntStream.range(0, numberOfArguments) @@ -109,14 +110,17 @@ public static String generate(CompileFramework comp) { for (int i = MIN; i <= MAX; ++i) { tests.add(generateTest(CodeGenerationDataNameType.ints(), i).asToken()); tests.add(generateTest(CodeGenerationDataNameType.floats(), i).asToken()); - // Longs and doubles take up double as much space in the parameter list as other - // primitive types (e.g., int). We therefore have to divide by two to fill up - // the same amount of space as for ints and floats. - tests.add(generateTest(CodeGenerationDataNameType.longs(), i / 2).asToken()); - tests.add(generateTest(CodeGenerationDataNameType.doubles(), i / 2).asToken()); + } + // Longs and doubles take up twice as much space in the parameter list as other + // primitive types (e.g., int). We therefore have to divide by two to fill up + // the same amount of space as for ints and floats. + for (int i = MIN; i <= MAX / 2; ++i) { + tests.add(generateTest(CodeGenerationDataNameType.longs(), i).asToken()); + tests.add(generateTest(CodeGenerationDataNameType.doubles(), i).asToken()); } return Template.make(() -> Template.scope( Template.let("classpath", comp.getEscapedClassPathOfCompiledClasses()), + Template.let("warmupIterations", WARMUP_ITERATIONS), """ import java.util.Arrays; import java.util.stream.*; @@ -133,6 +137,7 @@ public class InnerTest { public static void main() { TestFramework framework = new TestFramework(InnerTest.class); + framework.setDefaultWarmup(#warmupIterations); framework.addFlags("-classpath", "#classpath"); framework.start(); } diff --git a/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java b/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java index dfd719f3a3b..147d2e2487a 100644 --- a/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java +++ b/test/hotspot/jtreg/compiler/c1/TestConcurrentPatching.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,8 +29,9 @@ /** * @test * @bug 8340313 + * @key stress * @summary Test that concurrent patching of oop immediates is thread-safe in C1. - * @run main/othervm/timeout=1920 -Xcomp -XX:CompileCommand=compileonly,TestConcurrentPatching::* -XX:TieredStopAtLevel=1 TestConcurrentPatching + * @run main/othervm/timeout=1920 -Xcomp -XX:CompileCommand=compileonly,${test.main.class}::* -XX:TieredStopAtLevel=1 ${test.main.class} */ class MyClass { } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java index b5a27f3f714..1c0c44aba41 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * 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,13 +32,13 @@ * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / * @requires vm.opt.DeoptimizeALot != true - * @run driver compiler.c2.irTests.TestPrunedExHandler + * @requires vm.opt.final.TieredCompilation + * @run driver ${test.main.class} */ public class TestPrunedExHandler { public static void main(String[] args) { TestFramework.runWithFlags( - "-XX:+TieredCompilation", // we only profile in tier 3 "-XX:CompileCommand=inline,compiler.c2.irTests.TestPrunedExHandler::inlinee", "-XX:CompileCommand=dontinline,compiler.c2.irTests.TestPrunedExHandler::outOfLine"); } diff --git a/test/hotspot/jtreg/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java b/test/hotspot/jtreg/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java index 417a6bbc00f..d3b704d2f66 100644 --- a/test/hotspot/jtreg/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java +++ b/test/hotspot/jtreg/compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java @@ -24,11 +24,12 @@ /* * @test TestOverloadCompileQueues * @bug 8163511 8230402 + * @key stress * @summary Test overloading the C1 and C2 compile queues with tasks. * @run main/othervm/timeout=1200 -XX:-TieredCompilation -XX:CompileThreshold=2 -XX:CICompilerCount=1 - * compiler.classUnloading.methodUnloading.TestOverloadCompileQueues + * ${test.main.class} * @run main/othervm/timeout=1200 -XX:TieredCompileTaskTimeout=1000 -XX:CompileThresholdScaling=0.001 -XX:CICompilerCount=2 - * compiler.classUnloading.methodUnloading.TestOverloadCompileQueues + * ${test.main.class} */ package compiler.classUnloading.methodUnloading; diff --git a/test/hotspot/jtreg/compiler/hotcode/StressHotCodeCollector.java b/test/hotspot/jtreg/compiler/hotcode/StressHotCodeCollector.java index 551b9ea2fca..b6fa714d3e0 100644 --- a/test/hotspot/jtreg/compiler/hotcode/StressHotCodeCollector.java +++ b/test/hotspot/jtreg/compiler/hotcode/StressHotCodeCollector.java @@ -25,7 +25,7 @@ /* * @test - * @key randomness + * @key stress randomness * @requires vm.compiler2.enabled & vm.opt.SegmentedCodeCache != false * @library /test/lib / * @build jdk.test.whitebox.WhiteBox @@ -33,7 +33,7 @@ * @run main/othervm -Xbootclasspath/a:. -Xcomp -XX:-TieredCompilation -XX:+SegmentedCodeCache -XX:+UnlockExperimentalVMOptions -XX:+HotCodeHeap * -XX:+NMethodRelocation -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:HotCodeIntervalSeconds=0 -XX:HotCodeSampleSeconds=10 * -XX:HotCodeStablePercent=-1 -XX:HotCodeSamplePercent=100 -XX:HotCodeStartupDelaySeconds=0 - * compiler.hotcode.StressHotCodeCollector + * ${test.main.class} */ package compiler.hotcode; diff --git a/test/hotspot/jtreg/compiler/intrinsics/sha/TestDigest.java b/test/hotspot/jtreg/compiler/intrinsics/sha/TestDigest.java index 286f076175e..f543fc2b5c1 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/sha/TestDigest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/sha/TestDigest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,107 +28,107 @@ * * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=MD5 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-224 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-256 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-384 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-512 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-224 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-256 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-384 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-512 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=MD5 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-224 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-256 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-384 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-512 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-224 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-256 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-384 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-512 -Doffset=1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA-256 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA-512 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-256 -Dalgorithm2=SHA-512 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=MD5 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=MD5 -Dalgorithm2=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA3-224 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA3-256 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA3-384 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA-1 -Dalgorithm2=SHA3-512 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-224 -Dalgorithm2=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-256 -Dalgorithm2=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-384 -Dalgorithm2=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} * @run main/othervm/timeout=600 -Xbatch * -Dalgorithm=SHA3-512 -Dalgorithm2=SHA-1 - * compiler.intrinsics.sha.TestDigest + * ${test.main.class} */ package compiler.intrinsics.sha; @@ -146,23 +146,21 @@ public static void main(String[] args) throws Exception { String algorithm2 = System.getProperty("algorithm2", ""); int msgSize = Integer.getInteger("msgSize", 1024); int offset = Integer.getInteger("offset", 0) % ALIGN; - int iters = (args.length > 0 ? Integer.valueOf(args[0]) : 100000); - int warmupIters = (args.length > 1 ? Integer.valueOf(args[1]) : 20000); + int iters = 20000; - testDigest(provider, algorithm, msgSize, offset, iters, warmupIters); + testDigest(provider, algorithm, msgSize, offset, iters); if (algorithm2.equals("") == false) { - testDigest(provider, algorithm2, msgSize, offset, iters, warmupIters); + testDigest(provider, algorithm2, msgSize, offset, iters); } } public static void testDigest(String provider, String algorithm, int msgSize, - int offset, int iters, int warmupIters) throws Exception { + int offset, int iters) throws Exception { System.out.println("provider = " + provider); System.out.println("algorithm = " + algorithm); System.out.println("msgSize = " + msgSize + " bytes"); System.out.println("offset = " + offset); - System.out.println("iters = " + iters); byte[] expectedHash = new byte[HASH_LEN]; byte[] hash = new byte[HASH_LEN]; @@ -179,8 +177,8 @@ public static void testDigest(String provider, String algorithm, int msgSize, digest.update(data, offset, msgSize); expectedHash = digest.digest(); - /* warm up */ - for (int i = 0; i < warmupIters; i++) { + /* trigger compilation to use intrinsics */ + for (int i = 0; i < iters; i++) { digest.reset(); digest.update(data, offset, msgSize); hash = digest.digest(); @@ -196,20 +194,6 @@ public static void testDigest(String provider, String algorithm, int msgSize, } else { showArray(hash, "hash"); } - - /* measure performance */ - long start = System.nanoTime(); - for (int i = 0; i < iters; i++) { - digest.reset(); - digest.update(data, offset, msgSize); - hash = digest.digest(); - } - long end = System.nanoTime(); - double total = (double)(end - start)/1e9; /* in seconds */ - double thruput = (double)msgSize*iters/1e6/total; /* in MB/s */ - System.out.println("TestDigest runtime = " + total + " seconds"); - System.out.println("TestDigest throughput = " + thruput + " MB/s"); - System.out.println(); } catch (Exception e) { System.out.println("Exception: " + e); //System.exit(1); diff --git a/test/hotspot/jtreg/compiler/intrinsics/sha/sanity/DigestSanityTestBase.java b/test/hotspot/jtreg/compiler/intrinsics/sha/sanity/DigestSanityTestBase.java index c5826479024..ba77a9b0916 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/sha/sanity/DigestSanityTestBase.java +++ b/test/hotspot/jtreg/compiler/intrinsics/sha/sanity/DigestSanityTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,8 +53,7 @@ public class DigestSanityTestBase { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final int MSG_SIZE = 1024; private static final int OFFSET = 0; - private static final int ITERATIONS = 10000; - private static final int WARMUP_ITERATIONS = WHITE_BOX.getIntxVMFlag("Tier4InvocationThreshold").intValue() + 50; + private static final int WARMUP_ITERATIONS = WHITE_BOX.getIntxVMFlag("Tier4InvocationThreshold").intValue() + 10_000; private static final String PROVIDER = "SUN"; private final BooleanSupplier predicate; @@ -88,7 +87,6 @@ public final void test() throws Exception { TestDigest.testDigest(DigestSanityTestBase.PROVIDER, algorithm, DigestSanityTestBase.MSG_SIZE, DigestSanityTestBase.OFFSET, - DigestSanityTestBase.ITERATIONS, DigestSanityTestBase.WARMUP_ITERATIONS); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index a8d6fbcb4e9..be189cd6f24 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -492,7 +492,6 @@ private void applyForceCompileCommand(Executable ex) { "Cannot define compilation level SKIP or WAIT_FOR_COMPILATION in @ForceCompile at " + ex); compLevel = restrictCompLevel(forceCompileAnno.value()); if (FLIP_C1_C2) { - compLevel = compLevel.flipCompLevel(); compLevel = restrictCompLevel(compLevel.flipCompLevel()); } if (EXCLUDE_RANDOM) { @@ -585,7 +584,6 @@ private void addDeclaredTest(Method m) { } CompLevel compLevel = restrictCompLevel(testAnno.compLevel()); if (FLIP_C1_C2) { - compLevel = compLevel.flipCompLevel(); compLevel = restrictCompLevel(compLevel.flipCompLevel()); } if (EXCLUDE_RANDOM) { diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestEquivalentInvariants.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestEquivalentInvariants.java index 8677ae851cf..268ba59521b 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestEquivalentInvariants.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestEquivalentInvariants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * 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,7 +40,7 @@ * i.e. where the invariants have the same summands, but in a different order. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run driver/timeout=4800 compiler.loopopts.superword.TestEquivalentInvariants + * @run driver/timeout=4800 ${test.main.class} */ public class TestEquivalentInvariants { @@ -226,7 +226,7 @@ public TestEquivalentInvariants() { } } - @Warmup(100) + @Warmup(10) @Run(test = {"testArrayBB", "testArrayBBInvar3", "testMemorySegmentB", diff --git a/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java b/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java index 5bf031b7c4a..360120c4706 100644 --- a/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java +++ b/test/hotspot/jtreg/compiler/tiered/Level2RecompilationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /** * @test Level2RecompilationTest + * @key stress * @summary Test downgrading mechanism from level 3 to level 2 for those profiled methods. * @library /test/lib / * @modules java.base/jdk.internal.misc @@ -37,7 +38,7 @@ * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCaseHelper::* * -XX:CompileCommand=print,compiler.whitebox.SimpleTestCaseHelper::* - * compiler.tiered.Level2RecompilationTest + * ${test.main.class} */ package compiler.tiered; diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java index e3c4957c6be..3e775ae7b26 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/CorrectlyRestoreRfp.java @@ -23,7 +23,7 @@ /** * @test - * @key randomness + * @key stress randomness * @summary When needing some stack extension for unpacking arguments from the non-scalarized entry point, * on Aarch64, LR (x30) and RFP (x29) are duplicated on the stack. But x29 can be used as an ordinary * register, and hold an oop whose value can be updated by the GC, which is aware of only one copy diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/PutFlatValueWithoutUseArrayFlattening.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/PutFlatValueWithoutUseArrayFlattening.java index 3ef0c0f60c1..0ee5a25f6c0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/PutFlatValueWithoutUseArrayFlattening.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/PutFlatValueWithoutUseArrayFlattening.java @@ -26,15 +26,16 @@ * @bug 8365978 * @summary Unsafe::compareAndSetFlatValue crashes with -XX:-UseArrayFlattening * @enablePreview + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @library /test/lib * @modules java.base/jdk.internal.misc - * @run main/othervm -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.PutFlatValueWithoutUseArrayFlattening::test + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::test * -XX:-TieredCompilation -Xcomp * -XX:+UnlockDiagnosticVMOptions - * -XX:-UseArrayFlattening -XX:+UseFieldFlattening -XX:+IgnoreUnrecognizedVMOptions -XX:+PreloadClasses - * compiler.valhalla.valuetypes.PutFlatValueWithoutUseArrayFlattening - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening -XX:+IgnoreUnrecognizedVMOptions -XX:+PreloadClasses - * compiler.valhalla.valuetypes.PutFlatValueWithoutUseArrayFlattening + * -XX:-UseArrayFlattening -XX:+UseFieldFlattening -XX:+IgnoreUnrecognizedVMOptions + * ${test.main.class} + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening -XX:+IgnoreUnrecognizedVMOptions + * ${test.main.class} */ package compiler.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayCopyWithOops.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayCopyWithOops.java index b4ffb21b72d..034cb6ae5b0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayCopyWithOops.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayCopyWithOops.java @@ -29,35 +29,37 @@ * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/timeout=240 compiler.valhalla.valuetypes.TestArrayCopyWithOops + * @run main/timeout=240 ${test.main.class} */ /* * @test id=do + * @key stress * @bug 8252506 * @summary Verify that arraycopy intrinsics properly handle flat value class arrays with oop fields. * @library /test/lib * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/othervm/timeout=240 -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestArrayCopyWithOops::test* - * -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestArrayCopyWithOops::create* + * @run main/othervm/timeout=240 -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=dontinline,${test.main.class}::create* * -Xbatch - * compiler.valhalla.valuetypes.TestArrayCopyWithOops + * ${test.main.class} */ /* * @test id=do-no-flattening + * @key stress * @bug 8252506 * @summary Verify that arraycopy intrinsics properly handle flat value class arrays with oop fields. * @library /test/lib * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run main/othervm/timeout=240 -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestArrayCopyWithOops::test* - * -XX:CompileCommand=dontinline,compiler.valhalla.valuetypes.TestArrayCopyWithOops::create* + * @run main/othervm/timeout=240 -XX:CompileCommand=dontinline,${test.main.class}::test* + * -XX:CompileCommand=dontinline,${test.main.class}::create* * -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:-UseArrayFlattening - * compiler.valhalla.valuetypes.TestArrayCopyWithOops + * ${test.main.class} */ package compiler.valhalla.valuetypes; @@ -71,6 +73,7 @@ public class TestArrayCopyWithOops { static final int LEN = 200; + static final int WARMUP_ITERATIONS = 20_000; static class MyObject { long val = Integer.MAX_VALUE; @@ -188,7 +191,7 @@ public static void main(String[] args) { ManyOops[] dst13 = createValueClassArray(); // Warmup runs to trigger compilation - for (int i = 0; i < 50_000; ++i) { + for (int i = 0; i < WARMUP_ITERATIONS; ++i) { test1(dst1); test2(dst2); test3(dst3); @@ -234,9 +237,9 @@ public static void main(String[] args) { Asserts.assertEquals(dst3[i].hash(), expected); Asserts.assertEquals(dst4[i].hash(), expected); Asserts.assertEquals(((ManyOops)dst5[i]).hash(), expected); + Asserts.assertEquals(((ManyOops)dst6[i]).hash(), expected); Asserts.assertEquals(((ManyOops)dst7[i]).hash(), expected); Asserts.assertEquals(((ManyOops)dst8[i]).hash(), expected); - Asserts.assertEquals(((ManyOops)dst8[i]).hash(), expected); Asserts.assertEquals(((ManyOops)dst9[i]).hash(), expected); Asserts.assertEquals(((ManyOops)dst10[i]).hash(), expected); Asserts.assertEquals(((ManyOops)dst11[i]).hash(), expected); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java index 4d0251b2b24..d065b59a459 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestArrayNullMarkers.java @@ -40,11 +40,12 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=no-flattening + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -55,11 +56,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:-UseNullableAtomicValueFlattening -XX:-UseNullFreeAtomicValueFlattening -XX:-UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=nAVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -70,11 +72,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:-UseNullableAtomicValueFlattening -XX:-UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=AVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -85,11 +88,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:-UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:-UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=AVF-nAVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -100,11 +104,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:-UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=NVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -115,11 +120,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:-UseNullFreeAtomicValueFlattening -XX:-UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=NVF-nAVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -130,11 +136,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:-UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=NVF-AVF + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -145,11 +152,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:-UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=all-flattening + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -160,11 +168,12 @@ * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=all-flattening-di + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -176,11 +185,12 @@ * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening * -XX:CompileCommand=dontinline,*::test* -XX:CompileCommand=dontinline,*::check* - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ /* * @test id=all-flattening-restrict-profiling + * @key stress * @summary Test support for null markers in (flat) arrays. * @library /test/lib / * @enablePreview @@ -192,7 +202,7 @@ * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -Xbatch * -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseNullFreeNonAtomicValueFlattening * -XX:+IgnoreUnrecognizedVMOptions -XX:-MonomorphicArrayCheck -XX:-UseArrayLoadStoreProfile - * compiler.valhalla.valuetypes.TestArrayNullMarkers + * ${test.main.class} */ public class TestArrayNullMarkers { @@ -209,7 +219,7 @@ public class TestArrayNullMarkers { // The main is huge by-design (it has been made that way to make sure that C2 sees all the type info from the array factories) // but its size causes compilation failures with tier 3 C1 because of it runs out of virtual registers in linear scan, which // triggers a test failure when AbortVMOnCompilationFailure is set. - // Because this method will bail out from C1 compilation anyway, let's exclude it to prevent noise when running valhalla-comp-stress. + // Because this method will bail out from C1 compilation anyway, let's exclude it to prevent noise during stress testing. static { if (AbortVMOnCompilationFailure) { Class c = TestArrayNullMarkers.class; @@ -1431,4 +1441,3 @@ public static void main(String[] args) { testScalarReplacement8(CANARY1, true); } } - diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java index 98fa9ac5b4f..0adeacf46e0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestC1.java @@ -27,6 +27,7 @@ import compiler.lib.ir_framework.Run; import compiler.lib.ir_framework.Scenario; import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; import jdk.test.lib.Asserts; import jdk.internal.value.ValueClass; @@ -37,14 +38,74 @@ import static compiler.valhalla.valuetypes.ValueTypes.rL; /* - * @test + * @test id=vanilla * @key randomness * @summary Various tests that are specific to C1. * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run driver/timeout=300 ${test.main.class} + * @run main/timeout=300 ${test.main.class} vanilla + */ + +/* + * @test id=c1 + * @key stress randomness + * @summary Various tests that are specific to C1. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 0 + */ + +/* + * @test id=c2 + * @key stress randomness + * @summary Various tests that are specific to C1. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 1 + */ + +/* + * @test id=interpreter + * @key stress randomness + * @summary Various tests that are specific to C1. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 2 + */ + +/* + * @test id=xcomp-c1 + * @key stress randomness + * @summary Various tests that are specific to C1. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 3 + */ + +/* + * @test id=xcomp-c2 + * @key stress randomness + * @summary Various tests that are specific to C1. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 4 */ public class TestC1 { @@ -62,17 +123,19 @@ public static void main(String[] args) { new Scenario(4, "-XX:TieredStopAtLevel=4", "-XX:-TieredCompilation", "-Xcomp") }; - ValueTypes.getFramework() - .addScenarios(scenarios) - .addFlags("--enable-preview", - "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", - "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED") - .addHelperClasses(MyValue1.class, - MyValue2.class, - MyValue2Inline.class, - MyValue3.class, - MyValue3Inline.class) - .start(); + TestFramework framework = ValueTypes.getFramework() + .addFlags("--enable-preview", + "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED") + .addHelperClasses(MyValue1.class, + MyValue2.class, + MyValue2Inline.class, + MyValue3.class, + MyValue3Inline.class); + if (!args[0].equals("vanilla")) { + framework.addScenarios(scenarios[Integer.parseInt(args[0])]); + } + framework.start(); } // JDK-8229799 diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java index 655793c1ba6..880a089a950 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConvention.java @@ -171,9 +171,6 @@ public TestCallingConvention() { public static void main(String[] args) { Scenario[] scenarios = ValueTypes.DEFAULT_SCENARIOS; - // Don't generate bytecodes but call through runtime for reflective calls - scenarios[0].addFlags("-Dsun.reflect.inflationThreshold=10000"); - scenarios[1].addFlags("-Dsun.reflect.inflationThreshold=10000"); scenarios[3].addFlags("-XX:-UseArrayFlattening"); scenarios[4].addFlags("-XX:-UseTLAB"); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java index f9e3b05d771..a411ceea1f2 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestCallingConventionC1.java @@ -34,14 +34,74 @@ import static compiler.valhalla.valuetypes.ValueTypes.rL; /* - * @test + * @test id=vanilla * @key randomness * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. * @library /test/lib / * @enablePreview * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation - * @run driver/timeout=300 ${test.main.class} + * @run main/timeout=300 ${test.main.class} vanilla + */ + +/* + * @test id=default + * @key stress randomness + * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 0 + */ + +/* + * @test id=stress-calling-convention + * @key stress randomness + * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 1 + */ + +/* + * @test id=flip-c1-c2 + * @key stress randomness + * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 2 + */ + +/* + * @test id=c1 + * @key stress randomness + * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 3 + */ + +/* + * @test id=c2 + * @key stress randomness + * @summary Test calls from {C1} to {C2, Interpreter}, and vice versa. + * @library /test/lib / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @run driver/timeout=300 ${test.main.class} 4 */ @ForceCompileClassInitializer @@ -51,13 +111,11 @@ public static void main(String[] args) { final Scenario[] scenarios = { // Default: both C1 and C2 are enabled, tiered compilation enabled new Scenario(0, - "--enable-preview", "-XX:CICompilerCount=2", "-XX:TieredStopAtLevel=4", "-XX:+TieredCompilation"), // Default: both C1 and C2 are enabled, tiered compilation enabled new Scenario(1, - "--enable-preview", "-XX:CICompilerCount=2", "-XX:TieredStopAtLevel=4", "-XX:+TieredCompilation", @@ -66,30 +124,29 @@ public static void main(String[] args) { // Same as above, but flip all the compLevel=CompLevel.C1_SIMPLE and compLevel=CompLevel.C2, so we test // the compliment of the above scenario. new Scenario(2, - "--enable-preview", "-XX:CICompilerCount=2", "-XX:TieredStopAtLevel=4", "-XX:+TieredCompilation", "-DFlipC1C2=true"), // Only C1. Tiered compilation disabled. new Scenario(3, - "--enable-preview", "-XX:TieredStopAtLevel=1", "-XX:+TieredCompilation", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-PatchALot"), // Only C2. new Scenario(4, - "--enable-preview", "-XX:TieredStopAtLevel=4", "-XX:-TieredCompilation") }; System.gc(); // Resolve this call, to avoid C1 code patching in the test cases. - ValueTypes.getFramework() - .addScenarios(scenarios) - .start(); + TestFramework framework = ValueTypes.getFramework().addFlags("--enable-preview"); + if (!args[0].equals("vanilla")) { + framework.addScenarios(scenarios[Integer.parseInt(args[0])]); + } + framework.start(); } // Helper methods and classes diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java index 7b3b31dc87f..279d14240c0 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestGetfieldChains.java @@ -94,8 +94,7 @@ public static void main(String[] args) { new Scenario(1, // C2 only. (Make sure the tests are correctly written) "-XX:TieredStopAtLevel=4", - "-XX:-TieredCompilation", - "-XX:-OmitStackTraceInFastThrow"), + "-XX:-TieredCompilation"), new Scenario(2, // interpreter only "-Xint"), @@ -108,13 +107,12 @@ public static void main(String[] args) { // Xcomp Only C2 "-XX:TieredStopAtLevel=4", "-XX:-TieredCompilation", - "-XX:-OmitStackTraceInFastThrow", "-Xcomp") }; ValueTypes.getFramework() .addScenarios(scenarios) - .addFlags("--enable-preview", + .addFlags("--enable-preview", "-XX:-OmitStackTraceInFastThrow", "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED", "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED") .start(); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMergeMemCleanup.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMergeMemCleanup.java index e3d48c50e68..79c58287f78 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMergeMemCleanup.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMergeMemCleanup.java @@ -22,13 +22,21 @@ */ /* - * @test + * @test id=vanilla * @bug 8391658 * @summary Test cleanup of nested MergeMems after incremental inlining. - * @key stress randomness * @requires vm.compiler2.enabled * @enablePreview * @run main ${test.main.class} + */ + +/* + * @test id=stress + * @bug 8391658 + * @summary Test cleanup of nested MergeMems after incremental inlining. + * @key stress randomness + * @requires vm.compiler2.enabled + * @enablePreview * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions * -XX:+StressIGVN -XX:+StressIncrementalInlining -XX:StressSeed=3860063970 * -XX:CompileCommand=compileonly,${test.main.class}::test diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMismatchHandling.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMismatchHandling.java index 1a0a56e26e6..b64328548e1 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMismatchHandling.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestMismatchHandling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 8301007 - * @key randomness + * @key stress randomness * @summary Verify that mismatches of the preload attribute are properly handled in the calling convention. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -33,34 +33,34 @@ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestMismatchHandling::test* - * compiler.valhalla.valuetypes.TestMismatchHandling + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile * -XX:CompileCommand=compileonly,*::method - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile * -XX:-ValueTypePassFieldsAsArgs - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-Inline -XX:-InlineAccessors -XX:-UseBimorphicInlining -XX:-UseCHA -XX:-UseTypeProfile * -XX:-ValueTypeReturnedAsFields - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+DeoptimizeNMethodBarriersALot - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xbatch * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.valhalla.valuetypes.TestMismatchHandling + * ${test.main.class} */ // ##################################### WARNING ###################################### diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java index dad60af136d..6f0ee93f9f4 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNewAcmp.java @@ -23,13 +23,14 @@ /** * @test TestNewAcmp + * @key stress * @summary Verifies correctness of the acmp bytecode with value object operands. * @library /testlibrary /test/lib /compiler/whitebox / * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=300 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.valhalla.valuetypes.TestNewAcmp + * ${test.main.class} */ package compiler.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java index 7948bac145e..61e168ae1cc 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestNullableValueTypes.java @@ -50,7 +50,7 @@ import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP; /* - * @test + * @test id=vanilla * @key randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / @@ -59,12 +59,26 @@ * @modules java.base/jdk.internal.value * java.base/jdk.internal.vm.annotation * @build test.java.lang.invoke.lib.InstructionHelper + * @run main ${test.main.class} vanilla + */ + +/* + * @test + * @key stress randomness + * @summary Test correct handling of nullable value classes. + * @library /test/lib /test/jdk/java/lang/invoke/common / + * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64") + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") + * @enablePreview + * @modules java.base/jdk.internal.value + * java.base/jdk.internal.vm.annotation + * @build test.java.lang.invoke.lib.InstructionHelper * @run driver ${test.main.class} 0 */ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -77,7 +91,7 @@ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -90,7 +104,7 @@ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -103,7 +117,7 @@ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -116,7 +130,7 @@ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -129,7 +143,7 @@ /* * @test - * @key randomness + * @key stress randomness * @summary Test correct handling of nullable value classes. * @library /test/lib /test/jdk/java/lang/invoke/common / * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") @@ -155,19 +169,25 @@ public TestNullableValueTypes() { } public static void main(String[] args) { + TestFramework framework = ValueTypes.getFramework() + .addHelperClasses(MyValue1.class, + MyValue2.class, + MyValue2Inline.class, + MyValue3.class, + MyValue3Inline.class); + + if (args.length > 0 && args[0].equals("vanilla")) { + framework.addFlags("--enable-preview", + "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED").start(); + return; + } Scenario[] scenarios = ValueTypes.DEFAULT_SCENARIOS; scenarios[3].addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); scenarios[4].addFlags("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); - ValueTypes.getFramework() - .addScenarios(scenarios[Integer.parseInt(args[0])]) - .addHelperClasses(MyValue1.class, - MyValue2.class, - MyValue2Inline.class, - MyValue3.class, - MyValue3Inline.class) - .start(); + framework.addScenarios(scenarios[Integer.parseInt(args[0])]).start(); } static { diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnBufferClassInitialization.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnBufferClassInitialization.java index c05e863f16a..1cb61a6d953 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnBufferClassInitialization.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestReturnBufferClassInitialization.java @@ -22,14 +22,23 @@ */ /** - * @test + * @test id=vanilla * @bug 8389391 - * @key stress randomness * @summary Return buffer allocation for late-inlined MH calls should not initialize the class. * @requires vm.compiler2.enabled * @library /test/lib * @enablePreview * @run main ${test.main.class} + */ + +/** + * @test id=stress + * @bug 8389391 + * @key stress randomness + * @summary Return buffer allocation for late-inlined MH calls should not initialize the class. + * @requires vm.compiler2.enabled + * @library /test/lib + * @enablePreview * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions * -XX:+StressIncrementalInlining -XX:StressSeed=1 * -XX:CompileCommand=exclude,${test.main.class}::target diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestSpeculateArrayAccess.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestSpeculateArrayAccess.java index 0fb7627e04e..757e30d723f 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestSpeculateArrayAccess.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestSpeculateArrayAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,16 +22,23 @@ */ /** - * @test + * @test id=vanilla + * @bug 8333889 + * @summary Test that speculative array access checks do not cause a load to be wrongly hoisted before its range check. + * @run main ${test.main.class} + */ + +/** + * @test id=stress * @key stress randomness * @bug 8333889 * @summary Test that speculative array access checks do not cause a load to be wrongly hoisted before its range check. - * @run main/othervm -XX:CompileCommand=dontinline,*::* -XX:CompileCommand=compileonly,*TestSpeculateArrayAccess::test + * @run main/othervm -XX:CompileCommand=dontinline,*::* -XX:CompileCommand=compileonly,${test.main.class}::test * -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:StressSeed=1202682944 - * compiler.valhalla.valuetypes.TestSpeculateArrayAccess - * @run main/othervm -XX:CompileCommand=dontinline,*::* -XX:CompileCommand=compileonly,*TestSpeculateArrayAccess::test + * ${test.main.class} + * @run main/othervm -XX:CompileCommand=dontinline,*::* -XX:CompileCommand=compileonly,${test.main.class}::test * -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM - * compiler.valhalla.valuetypes.TestSpeculateArrayAccess + * ${test.main.class} */ package compiler.valhalla.valuetypes; @@ -73,4 +80,3 @@ static void test() { } } } - diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTearing.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTearing.java index da8ec541405..07bc9585997 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTearing.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTearing.java @@ -36,7 +36,7 @@ /* * @test id=no-flattening - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -49,12 +49,12 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI -XX:-UseFieldFlattening -XX:-UseArrayFlattening * -XX:+StressGCM -XX:+StressLCM - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=no-flattening-AII - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -68,12 +68,12 @@ * -XX:+WhiteBoxAPI -XX:-UseFieldFlattening -XX:-UseArrayFlattening * -XX:+StressGCM -XX:+StressLCM * -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=no-flattening-di - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -87,12 +87,12 @@ * -XX:+WhiteBoxAPI -XX:-UseFieldFlattening -XX:-UseArrayFlattening * -XX:CompileCommand=dontinline,*::incrementAndCheck* * -XX:+StressGCM -XX:+StressLCM - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=no-flattening-di-AII - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -107,12 +107,12 @@ * -XX:CompileCommand=dontinline,*::incrementAndCheck* * -XX:+StressGCM -XX:+StressLCM * -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=xcomp-no-stress - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -125,12 +125,12 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+WhiteBoxAPI -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseArrayFlattening * -Xcomp -XX:-TieredCompilation - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=flattening - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -143,12 +143,12 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+WhiteBoxAPI -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseArrayFlattening * -XX:+StressGCM -XX:+StressLCM - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=flattening-AII - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -162,12 +162,12 @@ * -XX:+WhiteBoxAPI -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseArrayFlattening * -XX:+StressGCM -XX:+StressLCM * -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=flattening-di - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -181,12 +181,12 @@ * -XX:+WhiteBoxAPI -XX:+UseNullableAtomicValueFlattening -XX:+UseNullFreeAtomicValueFlattening -XX:+UseArrayFlattening * -XX:CompileCommand=dontinline,*::incrementAndCheck* * -XX:+StressGCM -XX:+StressLCM - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ /* * @test id=flattening-di-AII - * @key randomness + * @key stress randomness * @requires vm.compMode != "Xint" & vm.flavor == "server" * @summary Detect tearing on flat accesses and buffering. * @library /testlibrary /test/lib / @@ -201,7 +201,7 @@ * -XX:CompileCommand=dontinline,*::incrementAndCheck* * -XX:+StressGCM -XX:+StressLCM * -XX:+IgnoreUnrecognizedVMOptions -XX:+AlwaysIncrementalInline - * compiler.valhalla.valuetypes.TestTearing + * ${test.main.class} */ @LooselyConsistentValue @@ -422,11 +422,13 @@ public static void main(String[] args) throws Exception { loopLimit = 50_000; } TestTearing test = new TestTearing(); - Thread runner = null; + Runner[] runners = new Runner[10]; for (int i = 0; i < 10; ++i) { - runner = new Runner(test, loopLimit); - runner.start(); + runners[i] = new Runner(test, loopLimit); + runners[i].start(); + } + for (Runner runner : runners) { + runner.join(); } - runner.join(); } } diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTrivialMethods.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTrivialMethods.java index 7b7ce657529..93995617752 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTrivialMethods.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestTrivialMethods.java @@ -27,6 +27,7 @@ * @summary Verify that trivial accessor methods operating on a value type * field are C2 compiled to enable scalarization of the arg/return value. * @requires vm.compiler2.enabled + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @library /test/lib /compiler/whitebox / * @enablePreview * @modules java.base/jdk.internal.value @@ -39,8 +40,8 @@ * -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields * -XX:+IgnoreUnrecognizedVMOptions -XX:-StressCallingConvention * -XX:CompileCommand=dontinline,*::getter* -XX:CompileCommand=dontinline,*::setter* - * -XX:CompileCommand=dontinline,*::constantGetter* -XX:+PreloadClasses - * compiler.valhalla.valuetypes.TestTrivialMethods + * -XX:CompileCommand=dontinline,*::constantGetter* + * ${test.main.class} */ package compiler.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java index 126611733c3..0c98b5a3346 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestValueClasses.java @@ -119,9 +119,6 @@ public class TestValueClasses { public static void main(String[] args) { Scenario[] scenarios = ValueTypes.DEFAULT_SCENARIOS; - // Don't generate bytecodes but call through runtime for reflective calls - scenarios[0].addFlags("-Dsun.reflect.inflationThreshold=10000"); - scenarios[1].addFlags("-Dsun.reflect.inflationThreshold=10000"); scenarios[3].addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:+UseArrayFlattening", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); scenarios[4].addFlags("-XX:-UseTLAB", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-MonomorphicArrayCheck"); diff --git a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java index a9b4dbfaac2..fadd3672c0d 100644 --- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java +++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestVirtualThreads.java @@ -23,7 +23,7 @@ /* * @test id=default - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -31,38 +31,38 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.valhalla.valuetypes.TestVirtualThreads + * ${test.main.class} */ /* * @test id=ci - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Xbatch -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::* - * compiler.valhalla.valuetypes.TestVirtualThreads + * -Xbatch -XX:CompileCommand=compileonly,${test.main.class}*::* + * ${test.main.class} */ /* * @test id=ci-test - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Xbatch -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* - * compiler.valhalla.valuetypes.TestVirtualThreads + * -Xbatch -XX:CompileCommand=compileonly,${test.main.class}*::test* + * ${test.main.class} */ /* * @test id=ci-test-di - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -70,14 +70,14 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xbatch -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=dontinline,*::test* - * compiler.valhalla.valuetypes.TestVirtualThreads + * ${test.main.class} */ /* * @test id=ci-test-di-helper - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -85,14 +85,14 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xbatch -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=dontinline,*::*Helper - * compiler.valhalla.valuetypes.TestVirtualThreads + * ${test.main.class} */ /* * @test id=ci-test-di-exclude-helper - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -100,40 +100,40 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xbatch -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=exclude,*::*Helper - * compiler.valhalla.valuetypes.TestVirtualThreads + * ${test.main.class} */ /* * @test id=xcomp-ci - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Xcomp -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::* - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * -Xcomp -XX:CompileCommand=compileonly,${test.main.class}*::* + * ${test.main.class} 50000 */ /* * @test id=xcomp-co-test - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -Xcomp -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * -Xcomp -XX:CompileCommand=compileonly,${test.main.class}*::test* + * ${test.main.class} 50000 */ /* * @test id=xcomp-co-test-di - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -141,14 +141,14 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xcomp -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=dontinline,*::test* - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * ${test.main.class} 50000 */ /* * @test id=xcomp-co-test-di-helper - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -156,14 +156,14 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xcomp -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=dontinline,*::*Helper - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * ${test.main.class} 50000 */ /* * @test id=xcomp-co-test-di-exclude-helper - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -171,14 +171,14 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xcomp -XX:CompileCommand=dontinline,*::dontinline - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::test* + * -XX:CompileCommand=compileonly,${test.main.class}*::test* * -XX:CompileCommand=exclude,*::*Helper - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * ${test.main.class} 50000 */ /* * @test id=co-di - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -186,13 +186,13 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xbatch -XX:CompileCommand=dontinline,*::* - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::* - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * -XX:CompileCommand=compileonly,${test.main.class}*::* + * ${test.main.class} 50000 */ /* * @test id=xcomp-co-di - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -200,13 +200,13 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xcomp -XX:CompileCommand=dontinline,*::* - * -XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.TestVirtualThreads*::* - * compiler.valhalla.valuetypes.TestVirtualThreads 50000 + * -XX:CompileCommand=compileonly,${test.main.class}*::* + * ${test.main.class} 50000 */ /* * @test id=verify-cont - * @key randomness + * @key stress randomness * @summary Test that Virtual Threads work well with Value Objects. * @library /test/lib /compiler/whitebox / * @enablePreview @@ -215,7 +215,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+VerifyContinuations - * compiler.valhalla.valuetypes.TestVirtualThreads + * ${test.main.class} */ package compiler.valhalla.valuetypes; @@ -828,4 +828,3 @@ public static void main(String[] args) throws Exception { } } } - diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java index f627f2fbf62..960f2603789 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestFloat16VectorOperations.java @@ -28,7 +28,7 @@ * @modules jdk.incubator.vector * @library /test/lib / * @compile TestFloat16VectorOperations.java -* @run driver/timeout=480 compiler.vectorapi.TestFloat16VectorOperations +* @run driver/timeout=480 ${test.main.class} */ package compiler.vectorapi; @@ -54,16 +54,22 @@ public class TestFloat16VectorOperations { public static void main(String args[]) { // Test with default MaxVectorSize - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + runTest(); // Test with different values of MaxVectorSize - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=8"); - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=16"); - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=32"); - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:MaxVectorSize=64"); + runTest("-XX:MaxVectorSize=8"); + runTest("-XX:MaxVectorSize=16"); + runTest("-XX:MaxVectorSize=32"); + runTest("-XX:MaxVectorSize=64"); // Test with FMA instructions disabled - TestFramework.runWithFlags("--add-modules=jdk.incubator.vector", "-XX:-UseFMA"); + runTest("-XX:-UseFMA", + "-DTest=vectorFmaFloat16,vectorFmaFloat16ScalarMixedConstants," + + "vectorFmaFloat16MixedConstants,vectorFmaFloat16AllConstants"); + } + + private static void runTest(String... flags) { + new TestFramework().setDefaultWarmup(50).addFlags("--add-modules=jdk.incubator.vector").addFlags(flags).start(); } static void assertResults(int arity, short ... values) { diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorUnsignedMinMaxOperationsTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorUnsignedMinMaxOperationsTest.java index b1b98306312..923d8fc7c0c 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorUnsignedMinMaxOperationsTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorUnsignedMinMaxOperationsTest.java @@ -27,7 +27,7 @@ * @summary Unsigned Vector Min / Max transforms * @modules jdk.incubator.vector * @library /test/lib / -* @run driver compiler.vectorapi.VectorUnsignedMinMaxOperationsTest +* @run driver ${test.main.class} */ package compiler.vectorapi; @@ -63,7 +63,7 @@ public class VectorUnsignedMinMaxOperationsTest { public static void main(String[] args) { TestFramework testFramework = new TestFramework(); - testFramework.setDefaultWarmup(5000) + testFramework.setDefaultWarmup(100) .addFlags("--add-modules=jdk.incubator.vector") .start(); } @@ -117,7 +117,6 @@ public VectorUnsignedMinMaxOperationsTest() { @Test @IR(counts = {IRNode.UMAX_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umax_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { ByteVector.fromArray(bspec, byte_in1, i) @@ -140,7 +139,6 @@ public void umax_byte_verify() { @Test @IR(counts = {IRNode.UMAX_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umax_short() { for (int i = 0; i < COUNT; i += sspec.length()) { ShortVector.fromArray(sspec, short_in1, i) @@ -163,7 +161,6 @@ public void umax_short_verify() { @Test @IR(counts = {IRNode.UMAX_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umax_int() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector.fromArray(ispec, int_in1, i) @@ -186,7 +183,6 @@ public void umax_int_verify() { @Test @IR(counts = {IRNode.UMAX_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umax_long() { for (int i = 0; i < COUNT; i += lspec.length()) { LongVector.fromArray(lspec, long_in1, i) @@ -209,7 +205,6 @@ public void umax_long_verify() { @Test @IR(counts = {IRNode.UMIN_VB, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_byte() { for (int i = 0; i < COUNT; i += bspec.length()) { ByteVector.fromArray(bspec, byte_in1, i) @@ -232,7 +227,6 @@ public void umin_byte_verify() { @Test @IR(counts = {IRNode.UMIN_VS, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_short() { for (int i = 0; i < COUNT; i += sspec.length()) { ShortVector.fromArray(sspec, short_in1, i) @@ -255,7 +249,6 @@ public void umin_short_verify() { @Test @IR(counts = {IRNode.UMIN_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_int() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector.fromArray(ispec, int_in1, i) @@ -278,7 +271,6 @@ public void umin_int_verify() { @Test @IR(counts = {IRNode.UMIN_VL, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_long() { for (int i = 0; i < COUNT; i += lspec.length()) { LongVector.fromArray(lspec, long_in1, i) @@ -301,7 +293,6 @@ public void umin_long_verify() { @Test @IR(counts = {IRNode.UMIN_VI, " 0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_ir_transform1() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector.fromArray(ispec, int_in1, i) @@ -324,7 +315,6 @@ public void umin_ir_transform1_verify() { @Test @IR(counts = {IRNode.UMAX_VI, " 0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umax_ir_transform1() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector.fromArray(ispec, int_in1, i) @@ -347,7 +337,6 @@ public void umax_ir_transform1_verify() { @Test @IR(counts = {IRNode.UMAX_VI, " 0 ", IRNode.UMIN_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_max_ir_transform1() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector vec1 = IntVector.fromArray(ispec, int_in1, i); @@ -374,7 +363,6 @@ public void umin_max_ir_transform1_verify() { @Test @IR(counts = {IRNode.UMIN_VI, " 0 ", IRNode.UMAX_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_max_ir_transform2() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector vec1 = IntVector.fromArray(ispec, int_in1, i); @@ -401,7 +389,6 @@ public void umin_max_ir_transform2_verify() { @Test @IR(counts = {IRNode.UMAX_VI, " 0 ", IRNode.UMIN_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_max_ir_transform3() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector vec1 = IntVector.fromArray(ispec, int_in1, i); @@ -428,7 +415,6 @@ public void umin_max_ir_transform3_verify() { @Test @IR(counts = {IRNode.UMIN_VI, " 0 ", IRNode.UMAX_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_max_ir_transform4() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector vec1 = IntVector.fromArray(ispec, int_in1, i); @@ -455,7 +441,6 @@ public void umin_max_ir_transform4_verify() { @Test @IR(counts = {IRNode.UMIN_VI, " 0 ", IRNode.UMAX_VI, " >0 "}, applyIfCPUFeatureOr = {"avx", "true", "rvv", "true"}) - @Warmup(value = 10000) public void umin_max_ir_transform5() { for (int i = 0; i < COUNT; i += ispec.length()) { IntVector vec1 = IntVector.fromArray(ispec, int_in1, i); diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/AnnotationsTests.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/AnnotationsTests.java index ad5933d4911..cff1f13a1de 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/AnnotationsTests.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/AnnotationsTests.java @@ -41,13 +41,15 @@ * java.base/jdk.internal.vm.annotation * @library /test/lib * @enablePreview + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI runtime.valhalla.valuetypes.AnnotationsTests + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ${test.main.class} */ public class AnnotationsTests { private static final WhiteBox WHITEBOX = WhiteBox.getWhiteBox(); + private static final boolean UseFieldFlattening = WHITEBOX.getBooleanVMFlag("UseFieldFlattening"); private static final boolean UseNullableAtomicValueFlattening = WHITEBOX.getBooleanVMFlag("UseNullableAtomicValueFlattening"); private static final Unsafe UNSAFE = Unsafe.getUnsafe(); @@ -110,13 +112,9 @@ void test_5() { try { GoodClass5 vc = new GoodClass5(); Field f0 = vc.getClass().getDeclaredField("f0"); - if (UseNullableAtomicValueFlattening) { - Asserts.assertTrue(UNSAFE.isFlatField(f0), "Flat field expected, but field is not flat"); - } else { - Asserts.assertFalse(UNSAFE.isFlatField(f0), "Unexpected flat field"); - } + Asserts.assertEquals(UNSAFE.isFlatField(f0), UseFieldFlattening && UseNullableAtomicValueFlattening, "Unexpected flatness"); Field f1 = vc.getClass().getDeclaredField("f1"); - Asserts.assertTrue(UNSAFE.isFlatField(f1), "Flat field expected, but field is not flat"); + Asserts.assertEquals(UNSAFE.isFlatField(f1), UseFieldFlattening, "Unexpected flatness"); } catch (IncompatibleClassChangeError e) { exception = e; System.out.println("Received " + e); @@ -289,4 +287,3 @@ void test_13() { } } - diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayCopyingTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayCopyingTest.java index 17d1bf6404c..3ba7597b6d6 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayCopyingTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayCopyingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * 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,9 +36,8 @@ * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run junit/othervm/timeout=480 -Xint -XX:+UseSerialGC -XX:+UseCompressedOops -Xlog:gc*=info - -XX:+UseCompressedClassPointers -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - runtime.valhalla.valuetypes.FlatArrayCopyingTest + ${test.main.class} */ /* @@ -57,7 +56,7 @@ * @run junit/othervm/timeout=480 -Xint -XX:+UseParallelGC -XX:+UseCompressedOops -Xlog:gc*=info -XX:ParallelGCThreads=1 -XX:-UseDynamicNumberOfGCThreads -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - runtime.valhalla.valuetypes.FlatArrayCopyingTest + ${test.main.class} */ /* @@ -76,9 +75,9 @@ * @run junit/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -Xint -XX:+UseG1GC -XX:+UseCompressedOops -Xlog:gc*=info -XX:ParallelGCThreads=1 -XX:ConcGCThreads=1 -XX:-UseDynamicNumberOfGCThreads - -XX:-G1UseConcRefinement -XX:+UseCompressedClassPointers + -XX:-G1UseConcRefinement -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - runtime.valhalla.valuetypes.FlatArrayCopyingTest + ${test.main.class} */ /* @@ -97,9 +96,8 @@ * @run junit/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -Xint -XX:+UseShenandoahGC -XX:+UseCompressedOops -Xlog:gc*=info -XX:ParallelGCThreads=1 -XX:ConcGCThreads=1 -XX:-UseDynamicNumberOfGCThreads - -XX:+UseCompressedClassPointers -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - runtime.valhalla.valuetypes.FlatArrayCopyingTest + ${test.main.class} */ package runtime.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayLargeOverlapCopyTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayLargeOverlapCopyTest.java index 9fe9c2c0b1d..fb14b3eea8b 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayLargeOverlapCopyTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatArrayLargeOverlapCopyTest.java @@ -24,12 +24,15 @@ /* * @test + * @key stress * @summary Ensures large overlapping flat array copies do not overflow payload iterator offsets. * @enablePreview * @requires os.maxMemory >= 7G + * @requires (vm.opt.UseArrayFlattening == null | vm.opt.UseArrayFlattening == "true") + * @requires (vm.opt.UseNullableAtomicValueFlattening == null | vm.opt.UseNullableAtomicValueFlattening == "true") * @modules java.base/jdk.internal.value * @run main/othervm/timeout=240 -Xint -Xmx6G - * runtime.valhalla.valuetypes.FlatArrayLargeOverlapCopyTest + * ${test.main.class} */ package runtime.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatFieldAccessorMethods.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatFieldAccessorMethods.java index d5b3c33c3f7..7a2860ed3b4 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatFieldAccessorMethods.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlatFieldAccessorMethods.java @@ -29,8 +29,10 @@ * @library /test/lib * @modules java.base/jdk.internal.misc * @enablePreview + * @requires (vm.opt.UseFieldFlattening == null | vm.opt.UseFieldFlattening == "true") + * @requires (vm.opt.UseNullableAtomicValueFlattening == null | vm.opt.UseNullableAtomicValueFlattening == "true") * @compile FlatFieldAccessorMethods.java - * @run main runtime.valhalla.valuetypes.FlatFieldAccessorMethods + * @run main ${test.main.class} */ package runtime.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlattenableSemanticTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlattenableSemanticTest.java index 17065797627..9b3d540c8fb 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlattenableSemanticTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/FlattenableSemanticTest.java @@ -34,9 +34,10 @@ * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @compile FlattenableSemanticTest.java - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening runtime.valhalla.valuetypes.FlattenableSemanticTest - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* runtime.valhalla.valuetypes.FlattenableSemanticTest + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening ${test.main.class} + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:ForceNonTearable=* ${test.main.class} */ public class FlattenableSemanticTest { diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java index a09eff48a13..d6d38bd3299 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,21 +27,12 @@ import test.java.lang.invoke.lib.InstructionHelper; /* - * @test id=compressed-class-pointers + * @test id=default * @summary Check object methods implemented by the VM behave with value types * @library /test/lib /test/jdk/java/lang/invoke/common * @enablePreview * @compile ObjectMethods.java - * @run main/othervm -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods - */ - -/* - * @test id=no-compressed-class-pointers - * @summary Check object methods implemented by the VM behave with value types - * @library /test/lib /test/jdk/java/lang/invoke/common - * @enablePreview - * @compile ObjectMethods.java - * @run main/othervm -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm ${test.main.class} */ /* @@ -50,7 +41,7 @@ * @library /test/lib /test/jdk/java/lang/invoke/common * @enablePreview * @compile ObjectMethods.java - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote runtime.valhalla.valuetypes.ObjectMethods noverify + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote ${test.main.class} noverify */ public class ObjectMethods { diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFieldNullability.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFieldNullability.java index 0c3b0ac9798..ab09239cb5c 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFieldNullability.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFieldNullability.java @@ -26,9 +26,10 @@ * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @compile TestFieldNullability.java * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening - * runtime.valhalla.valuetypes.TestFieldNullability + * ${test.main.class} */ package runtime.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFlatteningBudget.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFlatteningBudget.java index 3f14170d3e7..15228592fb7 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFlatteningBudget.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/TestFlatteningBudget.java @@ -28,10 +28,11 @@ * java.base/jdk.internal.misc * @library /test/lib * @enablePreview - * @requires vm.opt.UseFieldFlattening != "false" - * @requires vm.opt.UseNullableAtomicValueFlattening != "false" + * @requires (vm.opt.UseFieldFlattening == null | vm.opt.UseFieldFlattening == "true") + * @requires (vm.opt.UseNullableAtomicValueFlattening == null | vm.opt.UseNullableAtomicValueFlattening == "true") + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @requires vm.compMode != "Xcomp" - * @run main runtime.valhalla.valuetypes.TestFlatteningBudget + * @run main ${test.main.class} */ package runtime.valhalla.valuetypes; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/VolatileTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/VolatileTest.java index 6fb6b1ceb6f..e8e2ae0ce4f 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/VolatileTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/VolatileTest.java @@ -30,8 +30,9 @@ * java.base/jdk.internal.vm.annotation * @library /test/lib * @enablePreview + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") * @compile VolatileTest.java - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening runtime.valhalla.valuetypes.VolatileTest + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UseFieldFlattening ${test.main.class} */ import jdk.internal.misc.Unsafe; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/ConcurrentClassLoadingTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/ConcurrentClassLoadingTest.java index b40c5167849..802bb050e16 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/ConcurrentClassLoadingTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/ConcurrentClassLoadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,14 @@ /* * @test + * @key stress * @summary Invokes eight threads that concurrently have to resolve the same set of classes, thereby putting stress on the classloader and deadlocks will be noticed. This execution is iterated many times. * @library /test/lib * @enablePreview * @compile BigClassTreeClassLoader.java - * @run junit/othervm/timeout=480 -XX:ReservedCodeCacheSize=1G runtime.valhalla.valuetypes.classloading.ConcurrentClassLoadingTest + * @run junit/othervm/timeout=480 -XX:ReservedCodeCacheSize=1G ${test.main.class} */ package runtime.valhalla.valuetypes.classloading; diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/PreLoadFailuresDoNotImpactApplicationTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/PreLoadFailuresDoNotImpactApplicationTest.java index f57be0a44a7..a04e63dbe94 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/PreLoadFailuresDoNotImpactApplicationTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/classloading/PreLoadFailuresDoNotImpactApplicationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,13 @@ /* * @test * @summary Even if LoadableDescriptors fail, we observe correct operation. - This uses a custom classloader to ensure that classloading/linking will - initially fail due to LoadableDescriptors. - @compile OuterValue.java - @compile InnerValue.java + * This uses a custom classloader to ensure that classloading/linking will + * initially fail due to LoadableDescriptors. * @enablePreview - * @run junit runtime.valhalla.valuetypes.classloading.PreLoadFailuresDoNotImpactApplicationTest + * @requires (vm.opt.PreloadClasses == null | vm.opt.PreloadClasses == "true") + * @compile OuterValue.java + * @compile InnerValue.java + * @run junit ${test.main.class} */ package runtime.valhalla.valuetypes.classloading; diff --git a/test/hotspot/jtreg/vmTestbase/jit/escape/AdaptiveBlocking/AdaptiveBlocking001/AdaptiveBlocking001.java b/test/hotspot/jtreg/vmTestbase/jit/escape/AdaptiveBlocking/AdaptiveBlocking001/AdaptiveBlocking001.java index 17e88aeebb0..bce5945120e 100644 --- a/test/hotspot/jtreg/vmTestbase/jit/escape/AdaptiveBlocking/AdaptiveBlocking001/AdaptiveBlocking001.java +++ b/test/hotspot/jtreg/vmTestbase/jit/escape/AdaptiveBlocking/AdaptiveBlocking001/AdaptiveBlocking001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @key stress * * @summary converted from VM Testbase jit/escape/AdaptiveBlocking/AdaptiveBlocking001. * VM Testbase keywords: [jit, quick] @@ -36,9 +37,9 @@ * * @library /vmTestbase * /test/lib - * @build jit.escape.AdaptiveBlocking.AdaptiveBlocking001.AdaptiveBlocking001 + * @build ${test.main.class} * @run driver/timeout=1200 ExecDriver --java -server -Xcomp -XX:+DoEscapeAnalysis - * jit.escape.AdaptiveBlocking.AdaptiveBlocking001.AdaptiveBlocking001 -numRounds 10 + * ${test.main.class} -numRounds 10 */ package jit.escape.AdaptiveBlocking.AdaptiveBlocking001; From f9a13f8dc3bec24b27ddaf9b586a6f9b682241db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Mon, 5 Oct 2026 08:22:45 +0000 Subject: [PATCH 44/98] 8386622: JFR: Contextual information is applied to neither jdk.ExecutionSample nor jdk.NativeMethodSample events Reviewed-by: egahlin --- src/hotspot/share/jfr/metadata/metadata.xml | 10 +- .../sampling/jfrCPUTimeThreadSampler.cpp | 12 +-- .../sampling/jfrCPUTimeThreadSampler.hpp | 6 +- .../periodic/sampling/jfrThreadSampling.cpp | 32 +++++-- .../share/jfr/support/jfrThreadLocal.cpp | 1 - ...ckTrace.java => StackTraceTestMatrix.java} | 94 ++++++++++++++----- .../TestCPUTimeSampleFullStackTrace.java | 8 +- .../event/profiling/TestFullStackTrace.java | 9 +- ...estFullStackTracePreserveFramePointer.java | 42 +++++++++ .../jdk/jfr/event/profiling/TestNative.java | 32 +++++-- .../jdk/jdk/jfr/tool/TestPrintContextual.java | 66 ++++++++++++- 11 files changed, 247 insertions(+), 65 deletions(-) rename test/jdk/jdk/jfr/event/profiling/{BaseTestFullStackTrace.java => StackTraceTestMatrix.java} (60%) create mode 100644 test/jdk/jdk/jfr/event/profiling/TestFullStackTracePreserveFramePointer.java diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 22bd21c8ff4..71e2d9c747b 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -976,7 +976,7 @@ + thread="true" period="everyChunk"> @@ -984,7 +984,7 @@ + thread="true" period="everyChunk"> @@ -992,18 +992,16 @@ + throttle="true" thread="true" experimental="true" startTime="false"> - + thread="true" stackTrace="false" startTime="false" experimental="true"> - diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp index 7507b9c994e..235ff51bd34 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2025 SAP SE. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -332,7 +333,7 @@ void JfrCPUSamplerThread::on_javathread_terminate(JavaThread* thread) { tl->deallocate_cpu_time_jfr_queue(); s4 lost_samples = tl->cpu_time_jfr_queue().lost_samples(); if (lost_samples > 0) { - JfrCPUTimeThreadSampling::send_lost_event(JfrTicks::now(), JfrThreadLocal::thread_id(thread), lost_samples); + JfrCPUTimeThreadSampling::send_lost_event(JfrTicks::now(), lost_samples); } tl->release_cpu_time_jfr_queue_lock(); } @@ -422,11 +423,10 @@ void JfrCPUSamplerThread::stackwalk_threads_in_native() { static volatile size_t count = 0; -void JfrCPUTimeThreadSampling::send_empty_event(const JfrTicks &start_time, traceid tid, Tickspan cpu_time_period) { +void JfrCPUTimeThreadSampling::send_empty_event(const JfrTicks &start_time, Tickspan cpu_time_period) { EventCPUTimeSample event(UNTIMED); event.set_failed(true); event.set_starttime(start_time); - event.set_eventThread(tid); event.set_stackTrace(0); event.set_samplingPeriod(cpu_time_period); event.set_biased(false); @@ -436,11 +436,10 @@ void JfrCPUTimeThreadSampling::send_empty_event(const JfrTicks &start_time, trac static volatile size_t biased_count = 0; -void JfrCPUTimeThreadSampling::send_event(const JfrTicks &start_time, traceid sid, traceid tid, Tickspan cpu_time_period, bool biased) { +void JfrCPUTimeThreadSampling::send_event(const JfrTicks &start_time, traceid sid, Tickspan cpu_time_period, bool biased) { EventCPUTimeSample event(UNTIMED); event.set_failed(false); event.set_starttime(start_time); - event.set_eventThread(tid); event.set_stackTrace(sid); event.set_samplingPeriod(cpu_time_period); event.set_biased(biased); @@ -454,14 +453,13 @@ void JfrCPUTimeThreadSampling::send_event(const JfrTicks &start_time, traceid si } } -void JfrCPUTimeThreadSampling::send_lost_event(const JfrTicks &time, traceid tid, s4 lost_samples) { +void JfrCPUTimeThreadSampling::send_lost_event(const JfrTicks &time, s4 lost_samples) { if (!EventCPUTimeSamplesLost::is_enabled()) { return; } EventCPUTimeSamplesLost event(UNTIMED); event.set_starttime(time); event.set_lostSamples(lost_samples); - event.set_eventThread(tid); event.commit(); } diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp index e7c915fc8be..2585446b2ba 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp @@ -133,9 +133,9 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj { static void on_javathread_terminate(JavaThread* thread); void handle_timer_signal(siginfo_t* info, void* context); - static void send_empty_event(const JfrTicks& start_time, traceid tid, Tickspan cpu_time_period); - static void send_event(const JfrTicks& start_time, traceid sid, traceid tid, Tickspan cpu_time_period, bool biased); - static void send_lost_event(const JfrTicks& time, traceid tid, s4 lost_samples); + static void send_empty_event(const JfrTicks& start_time, Tickspan cpu_time_period); + static void send_event(const JfrTicks& start_time, traceid sid, Tickspan cpu_time_period, bool biased); + static void send_lost_event(const JfrTicks& time, s4 lost_samples); static void trigger_async_processing_of_cpu_time_jfr_requests(); diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp index 24868d7f896..362950af20a 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp @@ -316,6 +316,8 @@ static void record_thread_in_java(const JfrSampleRequest& request, const JfrTick } assert(sid != 0, "invariant"); const traceid tid = in_continuation ? tl->vthread_id_with_epoch_update(jt) : JfrThreadLocal::jvm_thread_id(jt); + JfrThreadLocal::impersonate(current, tid); + assert(JfrThreadLocal::thread_id(current) == tid, "invariant"); send_sample_event(request._sample_ticks, now, sid, tid); if (current == jt) { send_safepoint_latency_event(request, now, sid, jt); @@ -331,10 +333,13 @@ static void record_cpu_time_thread(const JfrCPUTimeSampleRequest& request, const bool biased = false; bool in_continuation = false; bool could_compute_top_frame = compute_top_frame(request._request, top_frame, in_continuation, jt, biased); + const traceid tid = in_continuation ? tl->vthread_id_with_epoch_update(jt) : JfrThreadLocal::jvm_thread_id(jt); + JfrThreadLocal::impersonate(current, tid); + assert(JfrThreadLocal::thread_id(current) == tid, "invariant"); if (!could_compute_top_frame) { - JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, tid, request._cpu_time_period); + JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, request._cpu_time_period); return; } traceid sid; @@ -343,15 +348,13 @@ static void record_cpu_time_thread(const JfrCPUTimeSampleRequest& request, const JfrStackTrace stacktrace; if (!stacktrace.record(jt, top_frame, in_continuation, request._request)) { // Unable to record stacktrace. Fail. - JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, tid, request._cpu_time_period); + JfrCPUTimeThreadSampling::send_empty_event(request._request._sample_ticks, request._cpu_time_period); return; } sid = JfrStackTraceRepository::add(stacktrace); } assert(sid != 0, "invariant"); - - - JfrCPUTimeThreadSampling::send_event(request._request._sample_ticks, sid, tid, request._cpu_time_period, biased); + JfrCPUTimeThreadSampling::send_event(request._request._sample_ticks, sid, request._cpu_time_period, biased); if (current == jt) { send_safepoint_latency_event(request._request, now, sid, jt); } @@ -369,6 +372,7 @@ static void drain_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, Jav record_thread_in_java(request, now, tl, jt, current); } tl->clear_enqueued_requests(); + JfrThreadLocal::stop_impersonating(current); } assert(!tl->has_enqueued_requests(), "invariant"); } @@ -390,7 +394,11 @@ static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal assert(queue.is_empty(), "invariant"); tl->set_has_cpu_time_jfr_requests(false); if (queue.lost_samples() > 0) { - JfrCPUTimeThreadSampling::send_lost_event( now, JfrThreadLocal::thread_id(jt), queue.get_and_reset_lost_samples()); + // Lost samples belong to the platform thread that owns the queue. + const traceid tid = JfrThreadLocal::jvm_thread_id(jt); + JfrThreadLocal::impersonate(current, tid); + assert(JfrThreadLocal::thread_id(current) == tid, "invariant"); + JfrCPUTimeThreadSampling::send_lost_event(now, queue.get_and_reset_lost_samples()); queue.resize_if_needed(); } if (lock) { @@ -402,9 +410,9 @@ static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal // Entry point for a thread that has been sampled in native code and has a pending JFR CPU time request. void JfrThreadSampling::process_cpu_time_request(JavaThread* jt, JfrThreadLocal* tl, Thread* current, bool lock) { assert(jt != nullptr, "invariant"); - const JfrTicks now = JfrTicks::now(); drain_enqueued_cpu_time_requests(now, tl, jt, current, lock); + JfrThreadLocal::stop_impersonating(current); } static void drain_all_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, JavaThread* jt, Thread* current) { @@ -414,6 +422,7 @@ static void drain_all_enqueued_requests(const JfrTicks& now, JfrThreadLocal* tl, drain_enqueued_requests(now, tl, jt, current); if (tl->has_cpu_time_jfr_requests()) { drain_enqueued_cpu_time_requests(now, tl, jt, current, true); + JfrThreadLocal::stop_impersonating(current); } } @@ -453,12 +462,16 @@ bool JfrThreadSampling::process_native_sample_request(JfrThreadLocal* tl, JavaTh } sid = JfrStackTraceRepository::add(stacktrace); } - // Read the tid under the monitor to ensure that if its a virtual thread, + + // Read the tid under the monitor to ensure that if it is a virtual thread, // it is not unmounted until we are done with it. tid = JfrThreadLocal::thread_id(jt); + // Since the sampler thread cannot commit any other event except its next sample, + // no explicit cleanup is required; the next processed sample overwrites the alias. + JfrThreadLocal::impersonate(sampler_thread, tid); } - assert(tl->sample_state() == NO_SAMPLE, "invariant"); + assert(JfrThreadLocal::thread_id(sampler_thread) == tid, "invariant"); send_sample_event(start_time, start_time, sid, tid); return true; } @@ -493,4 +506,3 @@ void JfrThreadSampling::process_sample_request(JavaThread* jt) { } drain_all_enqueued_requests(now, tl, jt, jt); } - diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 3b170c0ea53..b1a5d5e203c 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -379,7 +379,6 @@ bool JfrThreadLocal::is_impersonating(const Thread* t) { void JfrThreadLocal::impersonate(const Thread* t, traceid other_thread_id) { assert(t != nullptr, "invariant"); - assert(other_thread_id != 0, "invariant"); JfrThreadLocal* const tl = t->jfr_thread_local(); tl->_thread_id_alias = other_thread_id; } diff --git a/test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/StackTraceTestMatrix.java similarity index 60% rename from test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java rename to test/jdk/jdk/jfr/event/profiling/StackTraceTestMatrix.java index aadd9dded7e..9ae1ca21d2b 100644 --- a/test/jdk/jdk/jfr/event/profiling/BaseTestFullStackTrace.java +++ b/test/jdk/jdk/jfr/event/profiling/StackTraceTestMatrix.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * 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,24 +36,53 @@ import jdk.test.lib.jfr.Events; import jdk.test.lib.jfr.RecurseThread; -public class BaseTestFullStackTrace { +/* + * Exercises sampling events across the following matrix: + * + * ExecutionSample x platform thread + * ExecutionSample x virtual thread + * CPUTimeSample x platform thread + * CPUTimeSample x virtual thread + * + * Each cell verifies that the event identifies the sampled thread and that + * its stack trace contains the expected recursive Java frames. ExecutionSample + * additionally verifies that eventThread and sampledThread identify the same + * thread. + */ +public class StackTraceTestMatrix { private final static int MAX_DEPTH = 64; // currently hardcoded in jvm private final String eventName; private final String threadFieldName; + private final boolean virtual; - public BaseTestFullStackTrace(String eventName, String threadFieldName) { + private StackTraceTestMatrix(String eventName, String threadFieldName, boolean virtual) { this.eventName = eventName; this.threadFieldName = threadFieldName; + this.virtual = virtual; + } + + public static void runAllThreadKinds(String eventName, String threadFieldName) throws Throwable { + runPlatformThreads(eventName, threadFieldName); + new StackTraceTestMatrix(eventName, threadFieldName, true).run(); + } + + public static void runPlatformThreads(String eventName, String threadFieldName) throws Throwable { + new StackTraceTestMatrix(eventName, threadFieldName, false).run(); } public void run() throws Throwable { RecurseThread[] threads = new RecurseThread[3]; + // Virtual threads execute the RecurseThread workloads on distinct Thread objects. + Thread[] javaThreads = new Thread[threads.length]; for (int i = 0; i < threads.length; ++i) { int depth = MAX_DEPTH - 1 + i; threads[i] = new RecurseThread(depth); - threads[i].setName("recursethread-" + depth); - threads[i].start(); + String name = "recursethread-" + depth; + Thread thread = virtual ? Thread.ofVirtual().unstarted(threads[i]) : threads[i]; + thread.setName(name); + javaThreads[i] = thread; + thread.start(); } for (RecurseThread thread : threads) { @@ -62,15 +91,15 @@ public void run() throws Throwable { } } - assertStackTraces(threads); + assertStackTraces(threads, javaThreads); - for (RecurseThread thread : threads) { - thread.quit(); - thread.join(); + for (int i = 0; i < threads.length; ++i) { + threads[i].quit(); + javaThreads[i].join(); } } - private void assertStackTraces(RecurseThread[] threads) throws Throwable { + private void assertStackTraces(RecurseThread[] threads, Thread[] javaThreads) throws Throwable { while (true) { try (Recording recording = new Recording()) { if (eventName.equals(EventNames.CPUTimeSample)) { @@ -81,26 +110,35 @@ private void assertStackTraces(RecurseThread[] threads) throws Throwable { recording.start(); Thread.sleep(500); recording.stop(); - if (hasValidStackTraces(recording, threads)) { + if (hasValidStackTraces(recording, threads, javaThreads)) { break; } } }; } - private boolean hasValidStackTraces(Recording recording, RecurseThread[] threads) throws Throwable { + private boolean hasValidStackTraces(Recording recording, RecurseThread[] threads, Thread[] javaThreads) throws Throwable { boolean[] isEventFound = new boolean[threads.length]; for (RecordedEvent event : Events.fromRecording(recording)) { System.out.println("Event: " + event); + if (EventNames.ExecutionSample.equals(event.getEventType().getName())) { + long sampledThreadId = + Events.assertField(event, "sampledThread.javaThreadId").getValue(); + long eventThreadId = + Events.assertField(event, "eventThread.javaThreadId").getValue(); + Asserts.assertEquals(eventThreadId, sampledThreadId, + "eventThread and sampledThread must identify the same thread"); + } String threadName = Events.assertField(event, threadFieldName + ".javaName").getValue(); long threadId = Events.assertField(event, threadFieldName + ".javaThreadId").getValue(); for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { RecurseThread currThread = threads[threadIndex]; - if (threadId == currThread.getId()) { - System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); - Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); + Thread javaThread = javaThreads[threadIndex]; + if (threadId == javaThread.threadId()) { + System.out.println("ThreadName=" + javaThread.getName() + ", depth=" + currThread.totalDepth); + Asserts.assertEquals(threadName, javaThread.getName(), "Wrong thread name"); if ("recurseEnd".equals(getTopMethodName(event))) { isEventFound[threadIndex] = true; checkEvent(event, currThread.totalDepth); @@ -136,7 +174,8 @@ private void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable try { stacktrace = event.getStackTrace(); List frames = stacktrace.getFrames(); - Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); + int expectedStackDepth = Math.min(MAX_DEPTH, expectedDepth + (virtual ? 1 : 0)); + Asserts.assertEquals(expectedStackDepth, frames.size(), "Wrong stacktrace depth. Expected:" + expectedStackDepth); List expectedMethods = getExpectedMethods(expectedDepth); Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); @@ -148,13 +187,21 @@ private void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable } boolean isTruncated = stacktrace.isTruncated(); - boolean isTruncateExpected = expectedDepth > MAX_DEPTH; + // Thread.runWith(Object, Runnable) invokes a virtual-thread task below + // RecurseThread.run(). With MAX_DEPTH - 1 recursive frames, it fills the + // final stack-depth slot, but continuation frames still remain below it. + boolean isTruncateExpected = expectedDepth > MAX_DEPTH || + (virtual && expectedDepth >= MAX_DEPTH - 1); Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); String firstMethod = frames.getLast().getMethod().getName(); - boolean isFullTrace = "run".equals(firstMethod); - String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); - Asserts.assertTrue(isTruncated != isFullTrace, msg); + if (!virtual) { + boolean isFullTrace = "run".equals(firstMethod); + String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); + Asserts.assertTrue(isTruncated != isFullTrace, msg); + } else if (!isTruncated) { + Asserts.assertEquals(firstMethod, "run", "A full virtual-thread trace should end at RecurseThread.run"); + } } catch (Throwable t) { System.out.println(String.format("stacktrace:%n%s", stacktrace)); throw t; @@ -167,8 +214,13 @@ private List getExpectedMethods(int depth) { for (int i = 0; i < depth - 2; ++i) { methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); } + // A platform thread enters the recursive workload through RecurseThread.run(). methods.add("run"); - if (depth > MAX_DEPTH) { + if (virtual) { + // A virtual thread invokes that Runnable through Thread.runWith(Object, Runnable). + methods.add("runWith"); + } + if (methods.size() > MAX_DEPTH) { methods = methods.subList(0, MAX_DEPTH); } return methods; diff --git a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java index 0d1108346ab..9a5a4610b5a 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java +++ b/test/jdk/jdk/jfr/event/profiling/TestCPUTimeSampleFullStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,15 +27,15 @@ /** * @test - * @requires vm.hasJFR & os.family == "linux" + * @requires vm.hasJFR & vm.continuations & os.family == "linux" * @library /test/lib - * @build jdk.jfr.event.profiling.BaseTestFullStackTrace + * @build jdk.jfr.event.profiling.StackTraceTestMatrix * @run main/othervm jdk.jfr.event.profiling.TestCPUTimeSampleFullStackTrace */ public class TestCPUTimeSampleFullStackTrace { public static void main(String[] args) throws Throwable { - new BaseTestFullStackTrace(EventNames.CPUTimeSample, "eventThread").run(); + StackTraceTestMatrix.runAllThreadKinds(EventNames.CPUTimeSample, "eventThread"); } } diff --git a/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java b/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java index bfffb5e6f1d..855c318c55d 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java +++ b/test/jdk/jdk/jfr/event/profiling/TestFullStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,16 @@ /** * @test - * @requires vm.hasJFR + * @requires vm.hasJFR & vm.continuations * @requires vm.opt.DeoptimizeALot != true * @library /test/lib - * @build jdk.jfr.event.profiling.BaseTestFullStackTrace + * @build jdk.jfr.event.profiling.StackTraceTestMatrix * @run main/othervm jdk.jfr.event.profiling.TestFullStackTrace - * @run main/othervm -XX:CompileCommand=compileonly,jdk.test.lib.jfr.RecurseThread::recurse* -XX:+PreserveFramePointer jdk.jfr.event.profiling.TestFullStackTrace */ public class TestFullStackTrace { public static void main(String[] args) throws Throwable { - new BaseTestFullStackTrace(EventNames.ExecutionSample, "sampledThread").run(); + StackTraceTestMatrix.runAllThreadKinds(EventNames.ExecutionSample, "sampledThread"); } } diff --git a/test/jdk/jdk/jfr/event/profiling/TestFullStackTracePreserveFramePointer.java b/test/jdk/jdk/jfr/event/profiling/TestFullStackTracePreserveFramePointer.java new file mode 100644 index 00000000000..95fda43a0d3 --- /dev/null +++ b/test/jdk/jdk/jfr/event/profiling/TestFullStackTracePreserveFramePointer.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.event.profiling; + +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @requires vm.hasJFR + * @requires vm.opt.DeoptimizeALot != true + * @library /test/lib + * @build jdk.jfr.event.profiling.StackTraceTestMatrix + * @run main/othervm -XX:CompileCommand=compileonly,jdk.test.lib.jfr.RecurseThread::recurse* -XX:+PreserveFramePointer ${test.main.class} + */ +public class TestFullStackTracePreserveFramePointer { + + public static void main(String[] args) throws Throwable { + StackTraceTestMatrix.runPlatformThreads(EventNames.ExecutionSample, "sampledThread"); + } + +} diff --git a/test/jdk/jdk/jfr/event/profiling/TestNative.java b/test/jdk/jdk/jfr/event/profiling/TestNative.java index e7e076051f4..5a2c31e5d62 100644 --- a/test/jdk/jdk/jfr/event/profiling/TestNative.java +++ b/test/jdk/jdk/jfr/event/profiling/TestNative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,11 +24,13 @@ package jdk.jfr.event.profiling; import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; -import jdk.jfr.Recording; import jdk.jfr.consumer.RecordingStream; import jdk.jfr.internal.JVM; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; /* * @test @@ -45,21 +47,39 @@ public class TestNative { static volatile boolean alive = true; public static void main(String[] args) throws Exception { + Thread target = new Thread(TestNative::nativeMethod, "Native Sample Target"); + target.setDaemon(true); + AtomicReference failure = new AtomicReference<>(); try (RecordingStream rs = new RecordingStream()) { rs.enable(NATIVE_EVENT).withPeriod(Duration.ofMillis(1)); + rs.onError(t -> { + failure.compareAndSet(null, t); + alive = false; + rs.close(); + }); rs.onEvent(NATIVE_EVENT, e -> { + long sampledThreadId = Events.assertField(e, "sampledThread.javaThreadId").getValue(); + if (sampledThreadId != target.threadId()) { + return; + } + long eventThreadId = Events.assertField(e, "eventThread.javaThreadId").getValue(); + Asserts.assertEquals(eventThreadId, sampledThreadId, + "eventThread and sampledThread must identify the same thread"); alive = false; rs.close(); }); - Thread t = new Thread(TestNative::nativeMethod); - t.setDaemon(true); - t.start(); + target.start(); rs.start(); + } finally { + alive = false; + } + if (failure.get() != null) { + throw new RuntimeException("Native sample validation failed", failure.get()); } } - public static void nativeMethod() { + static void nativeMethod() { while (alive) { JVM.getPid(); } diff --git a/test/jdk/jdk/jfr/tool/TestPrintContextual.java b/test/jdk/jdk/jfr/tool/TestPrintContextual.java index e6df9c7c729..1d31bf9aeeb 100644 --- a/test/jdk/jdk/jfr/tool/TestPrintContextual.java +++ b/test/jdk/jdk/jfr/tool/TestPrintContextual.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; import jdk.jfr.Contextual; import jdk.jfr.Event; import jdk.jfr.Name; import jdk.jfr.Recording; import jdk.jfr.StackTrace; +import jdk.jfr.consumer.RecordingStream; import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; @@ -43,7 +46,7 @@ /** * @test * @requires vm.flagless - * @requires vm.hasJFR + * @requires vm.hasJFR & vm.continuations * @library /test/lib * @run main/othervm jdk.jfr.tool.TestPrintContextual */ @@ -109,6 +112,7 @@ public static void main(String[] args) throws Exception { testDeepContext(); testThreadedContext(); testFiltered(); + testSampledThreadContext(); } // Tests that context values are injected into non-contextual events @@ -356,6 +360,61 @@ private static void testFiltered() throws Exception { } } + // Tests that context values are also injected into sampled threads. + private static void testSampledThreadContext() throws Exception { + testSampledThreadContext(false); + testSampledThreadContext(true); + } + + private static void testSampledThreadContext(boolean virtual) throws Exception { + String threadKind = virtual ? "virtual" : "platform"; + String contextName = threadKind + " sample context"; + CountDownLatch contextStarted = new CountDownLatch(1); + CountDownLatch sampleReceived = new CountDownLatch(1); + AtomicBoolean running = new AtomicBoolean(true); + Thread target = (virtual ? Thread.ofVirtual() : Thread.ofPlatform()) + .name(threadKind + " sample target") + .unstarted(() -> { + TraceEvent trace = new TraceEvent(); + trace.name = contextName; + trace.begin(); + contextStarted.countDown(); + while (running.get()) { + Thread.onSpinWait(); + } + trace.commit(); + }); + + try (RecordingStream rs = new RecordingStream()) { + rs.enable("Trace").withoutStackTrace(); + rs.enable("jdk.ExecutionSample").withoutStackTrace().withPeriod(java.time.Duration.ofMillis(1)); + rs.onEvent("jdk.ExecutionSample", event -> { + if (event.getThread().getJavaThreadId() == target.threadId()) { + sampleReceived.countDown(); + } + }); + rs.startAsync(); + target.start(); + contextStarted.await(); + sampleReceived.await(); + running.set(false); + target.join(); + + Path file = Path.of(threadKind + "-sample-context.jfr"); + rs.dump(file); + List events = parseEvents(readPrintedLines(file, "--stack-depth", "0")); + for (PrintedEvent event : events) { + if (event.name.equals("jdk.ExecutionSample") && contextName.equals(event.getContextValue("Trace.name"))) { + return; + } + } + throw new Exception("No ExecutionSample found with context " + contextName); + } finally { + running.set(false); + target.join(); + } + } + private static void assertName(PrintedEvent event, String name) throws Exception { if (!event.name.equals(name)) { throw new Exception("Expected event name " + name + ", but was " + event.name); @@ -401,6 +460,9 @@ private static List parseEvents(List lines) { pe = null; } else if (pe != null) { int index = line.indexOf("="); + if (index < 0) { + continue; + } String field = line.substring(0, index).trim(); String value = line.substring(index + 1).trim(); if (value.startsWith("\"") && value.endsWith("\"")) { From 93bfd596427c2c40f0c1da09d4397e2aed18b4fe Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 5 Oct 2026 08:58:21 +0000 Subject: [PATCH 45/98] 8391985: View.getNextVisualPositionFrom can throw NullPointerException Reviewed-by: prr, jdv --- .../share/classes/javax/swing/text/View.java | 9 ++- .../text/View/TestViewVisualPosition.java | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 test/jdk/javax/swing/text/View/TestViewVisualPosition.java diff --git a/src/java.desktop/share/classes/javax/swing/text/View.java b/src/java.desktop/share/classes/javax/swing/text/View.java index 7fcc00caaac..29187efe280 100644 --- a/src/java.desktop/share/classes/javax/swing/text/View.java +++ b/src/java.desktop/share/classes/javax/swing/text/View.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -518,8 +518,11 @@ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, getStartOffset(); break; } - JTextComponent target = (JTextComponent) getContainer(); - Caret c = (target != null) ? target.getCaret() : null; + Container container = getContainer(); + if (!(container instanceof JTextComponent target)) { + return -1; + } + Caret c = target.getCaret(); // YECK! Ideally, the x location from the magic caret position // would be passed in. Point mcp; diff --git a/test/jdk/javax/swing/text/View/TestViewVisualPosition.java b/test/jdk/javax/swing/text/View/TestViewVisualPosition.java new file mode 100644 index 00000000000..9e2d28e5d51 --- /dev/null +++ b/test/jdk/javax/swing/text/View/TestViewVisualPosition.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8391985 + * @summary View.getNextVisualPositionFrom can throw NullPointerException + * @run main TestViewVisualPosition + */ + +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; +import javax.swing.SwingConstants; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; +import javax.swing.text.Position; +import javax.swing.text.View; + +public class TestViewVisualPosition { + public static void main(String[] args) throws Exception { + PlainDocument document = new PlainDocument(); + document.insertString(0, "x", null); + View view = new View(document.getDefaultRootElement()) { + @Override + public float getPreferredSpan(int axis) { + return 0; + } + + @Override + public void paint(Graphics g, Shape allocation) { + + } + + @Override + public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { + return null; + } + + @Override + public int viewToModel(float x, float y, Shape a, Position.Bias[] biasReturn) { + return 0; + } + }; + view.getNextVisualPositionFrom( + 0, + Position.Bias.Forward, + new Rectangle(0, 0, 10, 10), + SwingConstants.NORTH, + new Position.Bias[1]); + } +} From f29984825f40dcab2e603400a2c6c2940d3a37c4 Mon Sep 17 00:00:00 2001 From: Kieran Farrell Date: Mon, 5 Oct 2026 11:09:21 +0000 Subject: [PATCH 46/98] 8361344: Address sun/security/ssl/SSLLogger.java code coverage gaps Reviewed-by: mdonovan --- .../security/ssl/SSLLogger/SSLLoggerTest.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 test/jdk/sun/security/ssl/SSLLogger/SSLLoggerTest.java diff --git a/test/jdk/sun/security/ssl/SSLLogger/SSLLoggerTest.java b/test/jdk/sun/security/ssl/SSLLogger/SSLLoggerTest.java new file mode 100644 index 00000000000..e8d4b30d3fb --- /dev/null +++ b/test/jdk/sun/security/ssl/SSLLogger/SSLLoggerTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8361344 + * @summary Verify SSLLogger map entry formatting and throwable logging + * @modules java.base/sun.security.ssl:+open + * @run testng/othervm -Djavax.net.debug=all SSLLoggerTest + */ + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.System.Logger.Level; +import java.util.ListResourceBundle; +import java.util.Map; +import java.util.ResourceBundle; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import sun.security.ssl.SSLLogger; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class SSLLoggerTest { + private static final String NL = System.lineSeparator(); + private System.Logger logger; + + private PrintStream originalErr; + private PrintStream capturedErr; + private ByteArrayOutputStream capturedBytes; + + + @BeforeClass + public void initializeLogger() throws Exception { + // access SSLLogger's private console logger + var field = SSLLogger.class.getDeclaredField("logger"); + field.setAccessible(true); + logger = (System.Logger) field.get(null); + assertTrue(logger instanceof SSLLogger, "SSL logger not retrieved"); + } + + @BeforeMethod + public void setUp() { + // capture SSLLogger output in a fresh buffer for each test run + originalErr = System.err; + capturedBytes = new ByteArrayOutputStream(); + capturedErr = new PrintStream(capturedBytes, true, UTF_8); + System.setErr(capturedErr); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() { + if (originalErr != null) { + System.setErr(originalErr); + } + if (capturedErr != null) { + capturedErr.close(); + } + } + + @DataProvider(name = "mapEntries") + public Object[][] mapEntries() { + // description, entry value, expected formatted entry + return new Object[][] { + { "string", "value", " \"key\": \"value\"" }, + { "empty string", "", " \"key\": \"\"" }, + { "empty string array", new String[0], + " \"key\": [" + NL + " ]" }, + { "multiple strings", new String[] { "one", "two" }, + " \"key\": [" + NL + + " \"one\"," + NL + + " \"two\"" + NL + " ]" }, + { "byte array", new byte[] { 0, 0x0a, (byte) 0xff }, + " \"key\": \"000AFF\"" }, + { "byte", (byte) 0x0a, " \"key\": \"0a\"" }, + { "other object", Integer.valueOf(42), " \"key\": \"42\"" } + }; + } + + @DataProvider(name = "logEntries") + public Object[][] logEntries() { + ResourceBundle bundle = new ListResourceBundle() { + @Override + protected Object[][] getContents() { + return new Object[][] { { "bundle", "translated message" } }; + } + }; + + // level, bundle, log message, throwable, expected + return new Object[][] { + { Level.INFO, null, "info", + new RuntimeException("info exception"), + "java.lang.RuntimeException: info exception" }, + { Level.WARNING, null, "warning", + new IllegalStateException("warning exception"), + "java.lang.IllegalStateException: warning exception" }, + { Level.ERROR, null, "inner cause", + new RuntimeException("outer exception", + new IllegalArgumentException("inner exception")), + "Caused by: java.lang.IllegalArgumentException: inner exception" }, + { Level.INFO, bundle, "bundle", + new RuntimeException("bundle exception"), + "java.lang.RuntimeException: bundle exception" } + }; + } + + @Test(dataProvider = "mapEntries") + public void testFormatMapEntry(String name, Object value, String expected) { + // standard usage via SSLLogger.info calls formatMapEntry + SSLLogger.info(name, Map.entry("key", value)); + capturedErr.flush(); + String log = capturedBytes.toString(UTF_8); + + // check the message and formatted map entry at the end of the log + String suffix = "|" + name + " (\n" + expected + "\n)\n"; + assertTrue(log.endsWith(suffix), + name + ": expected suffix [" + suffix + "]\nActual: [" + log + "]"); + } + + @Test(dataProvider = "logEntries") + public void testLogThrowable(Level level, ResourceBundle rb, + String message, Throwable thrwbl, + String expected) { + logger.log(level, rb, message, thrwbl); + capturedErr.flush(); + String log = capturedBytes.toString(UTF_8); + + assertTrue(log.startsWith("javax.net.ssl|" + level + "|"), log); + assertTrue(log.contains("|" + message + " (\n"), log); + assertTrue(log.contains("\"throwable\" : {"), log); + assertTrue(log.contains(expected), + "Expected [" + expected + "] but got [" + log + "]"); + } + + @Test + public void testThrowableLoggingOff() { + assertFalse(logger.isLoggable(Level.OFF)); + logger.log(Level.OFF, null, "off", + new RuntimeException("exception")); + capturedErr.flush(); + assertEquals(capturedBytes.toString(UTF_8), "", + "The log call with throwable should not log at Level.OFF"); + } +} From 6310a3c779b33bc92175a019c02b7cb257ab9ecf Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 5 Oct 2026 11:52:46 +0000 Subject: [PATCH 47/98] 8393318: VarHandle CAE case should be handled as in JDK-8391651 Reviewed-by: jsjolen, dholmes --- .../classes/jdk/internal/misc/Unsafe.java | 3 +- .../inlinetypes/VarHandleCAEOfEmptyClass.java | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarHandleCAEOfEmptyClass.java diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index f7a82e9d72d..3bdcb87d6e7 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -1808,7 +1808,8 @@ public final Object compareAndExchangeFlatValue(Object o, long offset, V expected, V x) { Object[] array = newSpecialArray(valueType, 2, layout); - compareAndSetFlatValueAsBytes(array, o, offset, layout, valueType, expected, x); + int containerLayout = arrayLayout(array); + compareAndSetFlatValueAsBytes(array, o, offset, containerLayout, valueType, expected, x); return array[0]; } diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarHandleCAEOfEmptyClass.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarHandleCAEOfEmptyClass.java new file mode 100644 index 00000000000..ec3975b0344 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/VarHandleCAEOfEmptyClass.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package runtime.valhalla.inlinetypes; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + +/* + * @test VarHandleCAEOfEmptyClass + * @summary VarHandle compareAndExchange can handle empty value classes + * @bug 8391651 + * @enablePreview + * @compile VarHandleCAEOfEmptyClass.java + * @run main runtime.valhalla.inlinetypes.VarHandleCAEOfEmptyClass + */ + +public class VarHandleCAEOfEmptyClass { + static value class Empty { } + + static class Holder { + Empty value; + } + + public static void main(String[] args) throws ReflectiveOperationException { + VarHandle handle = MethodHandles.lookup().findVarHandle(Holder.class, "value", Empty.class); + if (handle.compareAndExchange(new Holder(), null, new Empty()) != null) { + throw new RuntimeException("compareAndExchange failed"); + } + } +} From 4e8bec6f862b58732537dea15cb3dd50a01fede0 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Mon, 5 Oct 2026 12:52:22 +0000 Subject: [PATCH 48/98] 8393451: ValuePayload copy and null marker simplifications and cleanups Reviewed-by: stefank, cnorrbin --- src/hotspot/share/gc/z/zBarrierSet.inline.hpp | 5 +- .../share/oops/accessBackend.inline.hpp | 14 +- src/hotspot/share/oops/flatArrayKlass.cpp | 5 +- src/hotspot/share/oops/valuePayload.hpp | 17 ++- .../share/oops/valuePayload.inline.hpp | 129 ++++++++---------- 5 files changed, 72 insertions(+), 98 deletions(-) diff --git a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp index 566e503ccf3..beb0ff8dfad 100644 --- a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp @@ -497,8 +497,7 @@ inline void ZBarrierSet::AccessBarrier::value_copy_in_h ValueKlass* const klass = src.klass(); - const LayoutKind layout_kind = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind()); - const size_t payload_size = klass->layout_size_in_bytes(layout_kind); + const size_t payload_size = ValuePayload::copy_size_in_bytes(src, dst); // The addr() points at the payload start, not the object start. const address src_payload = src.addr(); @@ -537,7 +536,7 @@ inline void ZBarrierSet::AccessBarrier::value_store_nul return; } - const size_t payload_size = klass->layout_size_in_bytes(layout_kind); + const size_t payload_size = dst.size_in_bytes(); // The addr() points at the payload start, not the object start. const address dst_payload = dst.addr(); diff --git a/src/hotspot/share/oops/accessBackend.inline.hpp b/src/hotspot/share/oops/accessBackend.inline.hpp index ab99247223d..1c8585185b7 100644 --- a/src/hotspot/share/oops/accessBackend.inline.hpp +++ b/src/hotspot/share/oops/accessBackend.inline.hpp @@ -336,23 +336,13 @@ template inline void RawAccessBarrier::value_copy(const ValuePayload& src, const ValuePayload& dst) { precond(src.klass() == dst.klass()); - const ValueKlass* klass = src.klass(); - const LayoutKind copy_layout = LayoutKindHelper::get_copy_layout( - src.layout_kind(), dst.layout_kind()); - const int size = klass->layout_size_in_bytes(copy_layout); - AccessInternal::value_copy_internal(src.addr(), dst.addr(), - static_cast(size)); + ValuePayload::copy_size_in_bytes(src, dst)); } template inline void RawAccessBarrier::value_store_null(const ValuePayload& dst) { - address dst_addr = dst.addr(); - const LayoutKind lk = dst.layout_kind(); - const ValueKlass* klass = dst.klass(); - const int size = klass->layout_size_in_bytes(lk); - - AccessInternal::value_store_null(dst_addr, static_cast(size)); + AccessInternal::value_store_null(dst.addr(), dst.size_in_bytes()); } #endif // SHARE_OOPS_ACCESSBACKEND_INLINE_HPP diff --git a/src/hotspot/share/oops/flatArrayKlass.cpp b/src/hotspot/share/oops/flatArrayKlass.cpp index f5ddcd1b68d..73c3b233182 100644 --- a/src/hotspot/share/oops/flatArrayKlass.cpp +++ b/src/hotspot/share/oops/flatArrayKlass.cpp @@ -256,8 +256,7 @@ void FlatArrayKlass::copy_array(arrayOop s, int src_pos, if (fsk->layout_kind() == fdk->layout_kind()) { // Because source and destination have the same layout, we do not have - // to worry about null checks and atomicity problems and can call the - // Access API directly. + // to worry about null checks and atomicity problems. int index_delta; if (needs_backwards_copy(sa, src_pos, da, dst_pos, length)) { index_delta = -1; @@ -268,7 +267,7 @@ void FlatArrayKlass::copy_array(arrayOop s, int src_pos, } for (int i = 0; i < length; i++) { - HeapAccess<>::value_copy(src_payload, dst_payload); + src_payload.copy_to(dst_payload); src_payload.advance_index(index_delta); dst_payload.advance_index(index_delta); } diff --git a/src/hotspot/share/oops/valuePayload.hpp b/src/hotspot/share/oops/valuePayload.hpp index 1e742729bbe..63673f12efe 100644 --- a/src/hotspot/share/oops/valuePayload.hpp +++ b/src/hotspot/share/oops/valuePayload.hpp @@ -110,12 +110,14 @@ class ValuePayload { inline void set_offset(ptrdiff_t offset); static inline void copy(const ValuePayload& src, - const ValuePayload& dst, - LayoutKind copy_layout_kind); + const ValuePayload& dst); inline void mark_as_non_null(); inline void mark_as_null(); + inline bool has_null_marker() const; + inline bool is_payload_null() const; + inline bool uses_absolute_addr() const; inline oop& container(); @@ -127,8 +129,7 @@ class ValuePayload { inline void assert_is_flat_field(const InstanceKlass* klass, int offset) const NOT_DEBUG_RETURN; inline void assert_post_construction_invariants() const NOT_DEBUG_RETURN; static inline void assert_pre_copy_invariants(const ValuePayload& src, - const ValuePayload& dst, - LayoutKind copy_layout_kind) NOT_DEBUG_RETURN; + const ValuePayload& dst) NOT_DEBUG_RETURN; public: inline ValueKlass* klass() const; @@ -137,12 +138,13 @@ class ValuePayload { inline address addr() const; - inline bool has_null_marker() const; - inline bool is_payload_null() const; + inline size_t size_in_bytes() const; class Handle; class OopHandle; + static inline size_t copy_size_in_bytes(const ValuePayload& src, const ValuePayload& dst); + [[nodiscard]] static inline ValuePayload construct_from_parts(address absolute_addr, ValueKlass* klass, LayoutKind layout_kind); @@ -200,6 +202,9 @@ class FlatValuePayload : public ValuePayload { inline void write_without_nullability_check(valueOop obj); inline void write(valueOop obj, TRAPS); + inline bool has_null_marker() const; + inline bool is_payload_null() const; + [[nodiscard]] static inline FlatValuePayload construct_from_parts(oop container, ptrdiff_t offset, ValueKlass* klass, diff --git a/src/hotspot/share/oops/valuePayload.inline.hpp b/src/hotspot/share/oops/valuePayload.inline.hpp index 4a1f567c945..07fa1ba4238 100644 --- a/src/hotspot/share/oops/valuePayload.inline.hpp +++ b/src/hotspot/share/oops/valuePayload.inline.hpp @@ -39,6 +39,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "utilities/debug.hpp" +#include "utilities/integerCast.hpp" #include "utilities/ostream.hpp" #include "utilities/vmError.hpp" @@ -183,31 +184,9 @@ inline void ValuePayload::set_offset(ptrdiff_t offset) { } inline void ValuePayload::copy(const ValuePayload& src, - const ValuePayload& dst, - LayoutKind copy_layout_kind) { - assert_pre_copy_invariants(src, dst, copy_layout_kind); - - ValueKlass* const klass = src.klass(); - - switch (copy_layout_kind) { - case LayoutKind::NULLABLE_ATOMIC_FLAT: - case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: { - if (src.is_payload_null()) { - HeapAccess<>::value_store_null(dst); - } else { - HeapAccess<>::value_copy(src, dst); - } - } break; - case LayoutKind::BUFFERED: - case LayoutKind::NULL_FREE_ATOMIC_FLAT: - case LayoutKind::NULL_FREE_NON_ATOMIC_FLAT: { - if (!klass->is_empty_value_type()) { - HeapAccess<>::value_copy(src, dst); - } - } break; - default: - ShouldNotReachHere(); - } + const ValuePayload& dst) { + assert_pre_copy_invariants(src, dst); + HeapAccess<>::value_copy(src, dst); } inline void ValuePayload::mark_as_non_null() { @@ -220,6 +199,14 @@ inline void ValuePayload::mark_as_null() { klass()->mark_payload_as_null(addr()); } +inline bool ValuePayload::has_null_marker() const { + return klass()->layout_has_null_marker(layout_kind()); +} + +inline bool ValuePayload::is_payload_null() const { + return has_null_marker() && klass()->is_payload_marked_as_null(addr()); +} + inline bool ValuePayload::uses_absolute_addr() const { return _storage.uses_absolute_addr(); } @@ -360,8 +347,7 @@ inline void ValuePayload::assert_post_construction_invariants() const { } inline void ValuePayload::assert_pre_copy_invariants(const ValuePayload& src, - const ValuePayload& dst, - LayoutKind copy_layout_kind) { + const ValuePayload& dst) { OnVMError on_assertion_failuire([&](outputStream* st) { st->print_cr("=== assert_post_construction_invariants failure ==="); StreamIndentor si(st); @@ -377,12 +363,6 @@ inline void ValuePayload::assert_pre_copy_invariants(const ValuePayload& src, dst.print_on(st); st->cr(); } - { - st->print_cr("--- copy layout kind ---"); - StreamIndentor si(st); - LayoutKindHelper::print_on(copy_layout_kind, st); - st->cr(); - } }); const ValueKlass* const src_klass = src.klass(); @@ -390,6 +370,8 @@ inline void ValuePayload::assert_pre_copy_invariants(const ValuePayload& src, precond(src_klass == dst_klass); + const LayoutKind copy_layout_kind = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind()); + const bool src_is_buffered = src.layout_kind() == LayoutKind::BUFFERED; const bool dst_is_buffered = dst.layout_kind() == LayoutKind::BUFFERED; const bool src_and_dst_same_layout_kind = src.layout_kind() == dst.layout_kind(); @@ -407,17 +389,12 @@ inline void ValuePayload::assert_pre_copy_invariants(const ValuePayload& src, precond(!src_klass->supports_nullable_layouts() || container != src_klass->null_reset_value()); } - const int src_layout_size_in_bytes = src_klass->layout_size_in_bytes(src.layout_kind()); - const int dst_layout_size_in_bytes = dst_klass->layout_size_in_bytes(dst.layout_kind()); - const int copy_layout_size_in_bytes = - src_has_copy_layout - ? src_layout_size_in_bytes - : dst_layout_size_in_bytes; + const size_t src_layout_size_in_bytes = src.size_in_bytes(); + const size_t dst_layout_size_in_bytes = dst.size_in_bytes(); + const size_t copy_layout_size_in_bytes = copy_size_in_bytes(src, dst); precond(copy_layout_size_in_bytes <= src_layout_size_in_bytes); precond(copy_layout_size_in_bytes <= dst_layout_size_in_bytes); - precond(LayoutKindHelper::get_copy_layout(src.layout_kind(), - dst.layout_kind()) == copy_layout_kind); } #endif // ASSERT @@ -441,12 +418,15 @@ inline address ValuePayload::addr() const { : (cast_from_oop
        (container()) + offset()); } -inline bool ValuePayload::has_null_marker() const { - return klass()->layout_has_null_marker(layout_kind()); +inline size_t ValuePayload::size_in_bytes() const { + return integer_cast(klass()->layout_size_in_bytes(layout_kind())); } -inline bool ValuePayload::is_payload_null() const { - return has_null_marker() && klass()->is_payload_marked_as_null(addr()); +inline size_t ValuePayload::copy_size_in_bytes(const ValuePayload& src, const ValuePayload& dst) { + precond(src.klass() == dst.klass()); + + const LayoutKind copy_layout_kind = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind()); + return integer_cast(src.klass()->layout_size_in_bytes(copy_layout_kind)); } inline ValuePayload ValuePayload::construct_from_parts(address absolute_addr, @@ -473,14 +453,16 @@ inline valueOop BufferedValuePayload::container() const { } inline void BufferedValuePayload::copy_to(const BufferedValuePayload& dst) { - copy(*this, dst, LayoutKind::BUFFERED); + copy(*this, dst); } inline FlatValuePayload::FlatValuePayload(oop container, ptrdiff_t offset, ValueKlass* klass, LayoutKind layout_kind) - : ValuePayload(container, offset, klass, layout_kind) {} + : ValuePayload(container, offset, klass, layout_kind) { + postcond(LayoutKindHelper::is_flat(layout_kind)); +} inline valueOop FlatValuePayload::allocate_instance(TRAPS) { // Preserve the container oop across the instance allocation. @@ -495,7 +477,7 @@ inline bool FlatValuePayload::copy_to(BufferedValuePayload& dst) { // Copy from FLAT to BUFFERED, null marker fix may be required. // Copy the payload to the buffered object. - copy(*this, dst, layout_kind()); + copy(*this, dst); if (!has_null_marker() && dst.has_null_marker()) { // We must fix the null marker if the src does not have a null marker but @@ -523,39 +505,30 @@ inline void FlatValuePayload::copy_from(BufferedValuePayload& src) { // valid non null value. src.mark_as_non_null(); } - copy(src, *this, layout_kind()); + copy(src, *this); } inline void FlatValuePayload::copy_to(const FlatValuePayload& dst) { - copy(*this, dst, layout_kind()); + copy(*this, dst); } inline valueOop FlatValuePayload::read(TRAPS) { - switch (layout_kind()) { - case LayoutKind::NULLABLE_ATOMIC_FLAT: - case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: { - if (is_payload_null()) { - return nullptr; - } - } // Fallthrough - case LayoutKind::NULL_FREE_ATOMIC_FLAT: - case LayoutKind::NULL_FREE_NON_ATOMIC_FLAT: { - valueOop res = allocate_instance(CHECK_NULL); - BufferedValuePayload dst(res, klass()); - if (!copy_to(dst)) { - // copy_to may fail if the payload has been updated with a null value - // between our is_payload_null() check above and the copy. - // In this case we have copied a null value into the buffer the payload. - return nullptr; - } - // Must ensure the content of the buffered value is visible - // before publishing the buffered value oop - OrderAccess::storestore(); - return res; - } break; - default: - ShouldNotReachHere(); + if (is_payload_null()) { + return nullptr; + } + + valueOop res = allocate_instance(CHECK_NULL); + BufferedValuePayload dst(res, klass()); + if (!copy_to(dst)) { + // copy_to may fail if the payload has been updated with a null value + // between our is_payload_null() check above and the copy. + // In this case we have copied a null value into the buffer the payload. + return nullptr; } + // Must ensure the content of the buffered value is visible + // before publishing the buffered value oop + OrderAccess::storestore(); + return res; } inline void FlatValuePayload::write_without_nullability_check(valueOop obj) { @@ -578,6 +551,14 @@ inline void FlatValuePayload::write(valueOop obj, TRAPS) { write_without_nullability_check(obj); } +inline bool FlatValuePayload::has_null_marker() const { + return ValuePayload::has_null_marker(); +} + +inline bool FlatValuePayload::is_payload_null() const { + return ValuePayload::is_payload_null(); +} + inline FlatValuePayload FlatValuePayload::construct_from_parts(oop container, ptrdiff_t offset, ValueKlass* klass, From 76bbfb3ee769a77f56e154e1a538cfa9f13c49f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Mon, 5 Oct 2026 14:00:40 +0000 Subject: [PATCH 49/98] 8382615: Expand MinMaxVector benchmark to floating point values Reviewed-by: roland, missa --- .../openjdk/bench/java/lang/MinMaxVector.java | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/test/micro/org/openjdk/bench/java/lang/MinMaxVector.java b/test/micro/org/openjdk/bench/java/lang/MinMaxVector.java index 3aa89604273..6bc161d933e 100644 --- a/test/micro/org/openjdk/bench/java/lang/MinMaxVector.java +++ b/test/micro/org/openjdk/bench/java/lang/MinMaxVector.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025, Red Hat, Inc. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. * 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,6 +37,7 @@ import org.openjdk.jmh.annotations.Warmup; import java.util.Arrays; +import java.util.DoubleSummaryStatistics; import java.util.IntSummaryStatistics; import java.util.LongSummaryStatistics; import java.util.Random; @@ -69,12 +71,22 @@ public static class LoopState { int[] minIntB; long[] minLongA; long[] minLongB; + float[] minFloatA; + float[] minFloatB; + double[] minDoubleA; + double[] minDoubleB; int[] maxIntA; int[] maxIntB; long[] maxLongA; long[] maxLongB; + float[] maxFloatA; + float[] maxFloatB; + double[] maxDoubleA; + double[] maxDoubleB; int[] resultIntArray; long[] resultLongArray; + float[] resultFloatArray; + double[] resultDoubleArray; @Setup public void setup() { @@ -83,12 +95,22 @@ public void setup() { maxLongB = longs[1]; maxIntA = toInts(maxLongA); maxIntB = toInts(maxLongB); + maxFloatA = toFloats(maxLongA); + maxFloatB = toFloats(maxLongB); + maxDoubleA = toDoubles(maxLongA); + maxDoubleB = toDoubles(maxLongB); minLongA = negate(maxLongA); minLongB = negate(maxLongB); minIntA = toInts(minLongA); minIntB = toInts(minLongB); + minFloatA = toFloats(minLongA); + minFloatB = toFloats(minLongB); + minDoubleA = toDoubles(minLongA); + minDoubleB = toDoubles(minLongB); resultIntArray = new int[size]; resultLongArray = new long[size]; + resultFloatArray = new float[size]; + resultDoubleArray = new double[size]; } static long[] negate(long[] nums) { @@ -99,6 +121,18 @@ static int[] toInts(long[] nums) { return Arrays.stream(nums).mapToInt(i -> (int) i).toArray(); } + static float[] toFloats(long[] nums) { + final float[] floats = new float[nums.length]; + for (int i = 0; i < nums.length; i++) { + floats[i] = (float) nums[i]; + } + return floats; + } + + static double[] toDoubles(long[] nums) { + return Arrays.stream(nums).mapToDouble(i -> (double) i).toArray(); + } + static long[][] distributeLongRandomIncrement(int size, int probability) { long[][] result; int aboveCount, abovePercent; @@ -166,10 +200,18 @@ public static class RangeState int[] resultInts; long[] longs; long[] resultLongs; + float[] floats; + float[] resultFloats; + double[] doubles; + double[] resultDoubles; int highestInt; int lowestInt; long highestLong; long lowestLong; + float highestFloat; + float lowestFloat; + double highestDouble; + double lowestDouble; Random r = new Random(seed); @Setup @@ -178,10 +220,16 @@ public void setup() { resultInts = new int[size]; longs = new long[size]; resultLongs = new long[size]; + floats = new float[size]; + resultFloats = new float[size]; + doubles = new double[size]; + resultDoubles = new double[size]; for (int i = 0; i < size; i++) { ints[i] = r.nextInt(); longs[i] = r.nextLong(); + floats[i] = r.nextFloat(); + doubles[i] = r.nextDouble(); } final IntSummaryStatistics intStats = Arrays.stream(ints).summaryStatistics(); @@ -191,6 +239,19 @@ public void setup() { final LongSummaryStatistics longStats = Arrays.stream(longs).summaryStatistics(); highestLong = (long) (longStats.getMax() * (range / 100f)); lowestLong = longStats.getMin() + (longStats.getMax() - highestLong); + + float maxFloat = Float.MIN_VALUE; + float minFloat = Float.MAX_VALUE; + for (float f : floats) { + maxFloat = Math.max(maxFloat, f); + minFloat = Math.min(minFloat, f); + } + highestFloat = maxFloat * (range / 100f); + lowestFloat = minFloat + (maxFloat - highestFloat); + + final DoubleSummaryStatistics doubleStats = Arrays.stream(doubles).summaryStatistics(); + highestDouble = doubleStats.getMax() * (range / 100f); + lowestDouble = doubleStats.getMin() + (doubleStats.getMax() - highestDouble); } } @@ -321,4 +382,132 @@ public long longReductionSimpleMax(LoopState state) { } return result; } + + @Benchmark + public float[] floatClippingRange(RangeState state) { + for (int i = 0; i < state.size; i++) { + state.resultFloats[i] = Math.min(Math.max(state.floats[i], state.lowestFloat), state.highestFloat); + } + return state.resultFloats; + } + + @Benchmark + public float[] floatLoopMin(LoopState state) { + for (int i = 0; i < state.size; i++) { + state.resultFloatArray[i] = Math.min(state.minFloatA[i], state.minFloatB[i]); + } + return state.resultFloatArray; + } + + @Benchmark + public float[] floatLoopMax(LoopState state) { + for (int i = 0; i < state.size; i++) { + state.resultFloatArray[i] = Math.max(state.maxFloatA[i], state.maxFloatB[i]); + } + return state.resultFloatArray; + } + + @Benchmark + public float floatReductionMultiplyMin(LoopState state) { + float result = 0; + for (int i = 0; i < state.size; i++) { + final float v = 11 * state.minFloatA[i]; + result = Math.min(v, result); + } + return result; + } + + @Benchmark + public float floatReductionSimpleMin(LoopState state) { + float result = 0; + for (int i = 0; i < state.size; i++) { + final float v = state.minFloatA[i]; + result = Math.min(v, result); + } + return result; + } + + @Benchmark + public float floatReductionMultiplyMax(LoopState state) { + float result = 0; + for (int i = 0; i < state.size; i++) { + final float v = 11 * state.maxFloatA[i]; + result = Math.max(v, result); + } + return result; + } + + @Benchmark + public float floatReductionSimpleMax(LoopState state) { + float result = 0; + for (int i = 0; i < state.size; i++) { + final float v = state.maxFloatA[i]; + result = Math.max(v, result); + } + return result; + } + + @Benchmark + public double[] doubleClippingRange(RangeState state) { + for (int i = 0; i < state.size; i++) { + state.resultDoubles[i] = Math.min(Math.max(state.doubles[i], state.lowestDouble), state.highestDouble); + } + return state.resultDoubles; + } + + @Benchmark + public double[] doubleLoopMin(LoopState state) { + for (int i = 0; i < state.size; i++) { + state.resultDoubleArray[i] = Math.min(state.minDoubleA[i], state.minDoubleB[i]); + } + return state.resultDoubleArray; + } + + @Benchmark + public double[] doubleLoopMax(LoopState state) { + for (int i = 0; i < state.size; i++) { + state.resultDoubleArray[i] = Math.max(state.maxDoubleA[i], state.maxDoubleB[i]); + } + return state.resultDoubleArray; + } + + @Benchmark + public double doubleReductionMultiplyMin(LoopState state) { + double result = 0; + for (int i = 0; i < state.size; i++) { + final double v = 11 * state.minDoubleA[i]; + result = Math.min(v, result); + } + return result; + } + + @Benchmark + public double doubleReductionSimpleMin(LoopState state) { + double result = 0; + for (int i = 0; i < state.size; i++) { + final double v = state.minDoubleA[i]; + result = Math.min(v, result); + } + return result; + } + + @Benchmark + public double doubleReductionMultiplyMax(LoopState state) { + double result = 0; + for (int i = 0; i < state.size; i++) { + final double v = 11 * state.maxDoubleA[i]; + result = Math.max(v, result); + } + return result; + } + + @Benchmark + public double doubleReductionSimpleMax(LoopState state) { + double result = 0; + for (int i = 0; i < state.size; i++) { + final double v = state.maxDoubleA[i]; + result = Math.max(v, result); + } + return result; + } } From 3bb884db76d7bad186746c3cbd707a1bfc40d812 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Mon, 5 Oct 2026 15:11:17 +0000 Subject: [PATCH 50/98] 8393528: DumpingWithNoCoops.java fails with 'Setting InitialHeapSize to 4G for CDS dumping' missing Reviewed-by: jpai --- .../runtime/cds/appcds/DumpingWithNoCoops.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/DumpingWithNoCoops.java b/test/hotspot/jtreg/runtime/cds/appcds/DumpingWithNoCoops.java index b5fc35ad149..a179fe84243 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/DumpingWithNoCoops.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/DumpingWithNoCoops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,19 +79,6 @@ String heapArgsString(HeapArgs ha) { new HeapArgs( 2, 1, 33), }; - static void checkExpectedMessages(HeapArgs ha, OutputAnalyzer output) throws Exception { - final int DUMPTIME_MAX_HEAP = 4; // 4 GB - if (ha.minSize > DUMPTIME_MAX_HEAP) { - output.shouldContain("Setting MinHeapSize to 4G for CDS dumping"); - } - if (ha.initialSize > DUMPTIME_MAX_HEAP) { - output.shouldContain("Setting InitialHeapSize to 4G for CDS dumping"); - } - if (ha.maxSize > DUMPTIME_MAX_HEAP) { - output.shouldContain("Setting MaxHeapSize to 4G for CDS dumping"); - } - } - public static void main(String[] args) throws Exception { final String noCoops = "-XX:-UseCompressedOops"; final String logArg = "-Xlog:gc+heap=trace,cds=debug,aot=debug"; @@ -118,7 +105,6 @@ public static void main(String[] args) throws Exception { dumptimeArgs.add(heapSize); } output = TestCommon.dump(appJar, appClasses, dumptimeArgs.toArray(new String[0])); - checkExpectedMessages(ha, output); } TestCommon.checkDump(output); From ddf6ad610d7fd0577e7827d30a7998ec32bb919d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 5 Oct 2026 16:24:12 +0000 Subject: [PATCH 51/98] 8388560: C2 VerifyIterativeGVN: fix many missing optimization/notification cases for arithmetic nodes Reviewed-by: qamai, chagedorn --- src/hotspot/share/opto/addnode.cpp | 4 + src/hotspot/share/opto/loopopts.cpp | 2 + src/hotspot/share/opto/node.cpp | 4 + src/hotspot/share/opto/phaseX.cpp | 532 ++++++++---------- src/hotspot/share/opto/superword.cpp | 6 + .../loopopts/superword/TestReductions.java | 13 +- 6 files changed, 241 insertions(+), 320 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index aa523fe9be8..4a6659db879 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1610,6 +1610,8 @@ Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { } if (can_reshape) { return fold_subI_no_underflow_pattern(this, phase); + } else { + phase->record_for_igvn(this); } return nullptr; } @@ -1642,6 +1644,8 @@ Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { } if (can_reshape) { return fold_subI_no_underflow_pattern(this, phase); + } else { + phase->record_for_igvn(this); } return nullptr; } diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 77ccf3b31bd..d5332dbef54 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -612,6 +612,8 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { n_op == Op_MulD) { if (n2_loop == n_loop) { assert(n1_loop != n_loop, ""); + // IGVN may want to swap the edges back later, to commute (canonicalize) the operation. + _igvn.rehash_node_delayed(n); n->swap_edges(1, 2); } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index d16edc38e23..163b9ee8978 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -1238,6 +1238,10 @@ bool Node::has_special_unique_user() const { } else if (op == Op_LoadUS && n->Opcode() == Op_LShiftI) { // Condition for RShiftI(LShiftI(LoadUS(...), 16), 16) => LoadS(...), see RShiftINode::Ideal return true; + } else if ((op == Op_LoadB || op == Op_LoadS) && n->Opcode() == Op_AndI) { + // AndINode::Ideal turns AndI(LoadB/S) into AndI(LoadUB/US), if the LoadB + // only has a single use. + return true; } else if (op == Op_AddL) { // Condition for convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) return n->Opcode() == Op_ConvL2I && n->in(1) == this; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c3f255ab08e..9587d0d046d 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -440,22 +440,34 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, assert(worklist.is_subset_of(_useful), "only useful nodes should still be in the worklist"); + // While we don't need to rehash, some optimizations are sensitive to the + // relative ordering of node IDs, for example AddNode::commute wants to keep + // in1 < in2. Therefore, we keep the relative ordering stable. + uint next_idx = 0; + for (int old_idx = 0; old_idx < _old2new_map.length(); old_idx++) { + if (_useful.member_set().test(old_idx)) { + _old2new_map.at_put(old_idx, next_idx++); + } + } + assert(next_idx == _useful.size(), "must map all useful nodes"); + // Iterate over the set of live nodes. for (uint current_idx = 0; current_idx < _useful.size(); current_idx++) { Node* n = _useful.at(current_idx); - const Type* type = gvn->type_or_null(n); - _new_type_array.map(current_idx, type); + uint old_idx = n->_idx; + int new_idx = _old2new_map.at(old_idx); + assert(new_idx >= 0, "every useful node must have new idx"); - assert(_old2new_map.at(n->_idx) == -1, "already seen"); - _old2new_map.at_put(n->_idx, current_idx); + const Type* type = gvn->type_or_null(n); + _new_type_array.map(new_idx, type); if (old_node_note_array != nullptr) { - Node_Notes* nn = C->locate_node_notes(old_node_note_array, n->_idx); - C->set_node_notes_at(current_idx, nn); + Node_Notes* nn = C->locate_node_notes(old_node_note_array, old_idx); + C->set_node_notes_at(new_idx, nn); } - n->set_idx(current_idx); // Update node ID. + n->set_idx(new_idx); // Update node ID. if (update_embedded_ids(n) < 0) { _delayed.push(n); // has embedded IDs; handle later @@ -1379,6 +1391,51 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. switch (n->Opcode()) { + // BoolNode::Ideal canonicalizes integer comparisons, but only if "!is_counted_loop_exit_test". + // We only optimize if: + // Cmp -> Bool -> If + // And not if: + // Cmp -> Bool -> CountedLoopEnd + // In loop unswitching, we turn a CountedLoopEnd into an If. + // We would now have to notify the input of the modified node, + // which we don't do so far. + // One simple hack would be to just push the Bool node on + // the worklist in that optimization, in an ad-hoc fashon. + // But more principled would probably be to do this as part + // of replace_input_of, which calls set_req_X, which already + // has some notification logic, for example using + // Node::has_special_unique_user. There are also other places + // the graph could be updated and lose the CountedLoopEnd node, + // and in those places we also use Node::has_special_unique_user, + // and other related code. + // I would suggest we refactor and extend that notification + // code in a future RFE. + // + // Found with: + // -XX:VerifyIterativeGVN=1110 + // compiler/loopopts/TestBaseCountedEndLoopUnswitchCandidate.java + case Op_Bool: + return; + + // I have encountered some issues with AddHF. + // It could be due to issues arising from JDK-8388873, we should + // check again after that is fixed. + // + // Found on aarch64 with: + // -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110 + // compiler/c2/irTests/TestFloat16ScalarOperations.java + case Op_AddHF: + return; + + // In XorI/LNode::Ideal, we optimize Xor(x, -1), but + // only if is_used_in_only_arithmetic: so we would + // need notification if the Xor only has arithmetic + // nodes. I think this also calls for a generalization + // of Node::has_special_unique_user. + case Op_XorI: + case Op_XorL: + return; + // RegionNode::Ideal does "Skip around the useless IF diamond". // 245 IfTrue === 244 // 258 If === 245 257 @@ -1398,182 +1455,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_Region: return; - // In AddNode::Ideal, we call "commute", which swaps the inputs so - // that smaller idx are first. Tracking it back, it led me to - // PhaseIdealLoop::remix_address_expressions which swapped the edges. - // - // Example: - // Before PhaseIdealLoop::remix_address_expressions - // 154 AddI === _ 12 144 - // After PhaseIdealLoop::remix_address_expressions - // 154 AddI === _ 144 12 - // After AddNode::Ideal - // 154 AddI === _ 12 144 - // - // I suspect that the node should be added to the IGVN worklist after - // PhaseIdealLoop::remix_address_expressions - // - // This is the only case I looked at, there may be others. Found like this: - // java -XX:VerifyIterativeGVN=0100 -Xbatch --version - // - // The following hit the same logic in PhaseIdealLoop::remix_address_expressions. - // - // Note: currently all of these fail also for other reasons, for example - // because of "commute" doing the reordering with the phi below. Once - // that is resolved, we can come back to this issue here. - // - // case Op_AddD: - // case Op_AddI: - // case Op_AddL: - // case Op_AddF: - // case Op_MulI: - // case Op_MulL: - // case Op_MulF: - // case Op_MulD: - // if (n->in(1)->_idx > n->in(2)->_idx) { - // // Expect "commute" to revert this case. - // return false; - // } - // break; // keep verifying - - // AddFPNode::Ideal calls "commute", which can reorder the inputs for this: - // Check for tight loop increments: Loop-phi of Add of loop-phi - // It wants to take the phi into in(1): - // 471 Phi === 435 38 390 - // 390 AddF === _ 471 391 - // - // Other Associative operators are also affected equally. - // - // Investigate why this does not happen earlier during IGVN. - // - // Found with: - // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java - // -XX:VerifyIterativeGVN=1110 - case Op_AddD: - //case Op_AddI: // Also affected for other reasons, see case further down. - //case Op_AddL: // Also affected for other reasons, see case further down. - case Op_AddF: - case Op_MulI: - case Op_MulL: - case Op_MulF: - case Op_MulD: - case Op_MinF: - case Op_MinD: - case Op_MaxF: - case Op_MaxD: - // XorINode::Ideal - // Found with: - // compiler/intrinsics/chacha/TestChaCha20.java - // -XX:VerifyIterativeGVN=1110 - case Op_XorI: - case Op_XorL: - // It seems we may have similar issues with the HF cases. - // Found with aarch64: - // compiler/vectorization/TestFloat16VectorOperations.java - // -XX:VerifyIterativeGVN=1110 - case Op_AddHF: - case Op_MulHF: - case Op_MaxHF: - case Op_MinHF: - return; - - // In MulNode::Ideal the edges can be swapped to help value numbering: - // - // // We are OK if right is a constant, or right is a load and - // // left is a non-constant. - // if( !(t2->singleton() || - // (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) { - // if( t1->singleton() || // Left input is a constant? - // // Otherwise, sort inputs (commutativity) to help value numbering. - // (in(1)->_idx > in(2)->_idx) ) { - // swap_edges(1, 2); - // - // Why was this not done earlier during IGVN? - // - // Found with: - // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java - // -XX:VerifyIterativeGVN=1110 - case Op_AndI: - // Same for AndL. - // Found with: - // compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java - // -XX:VerifyIterativeGVN=1110 - case Op_AndL: - return; - - // SubLNode::Ideal does transform like: - // Convert "c1 - (y+c0)" into "(c1-c0) - y" - // - // In IGVN before verification: - // 8423 ConvI2L === _ 3519 [[ 8424 ]] #long:-2 - // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www - // 8424 AddL === _ 8422 8423 [[ 8383 ]] !orig=[8382] - // 8016 ConL === 0 [[ 8383 ]] #long:0 - // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154] - // - // And then in verification: - // 8338 ConL === 0 [[ 8339 8424 ]] #long:-2 <----- Was constant folded. - // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www - // 8424 AddL === _ 8422 8338 [[ 8383 ]] !orig=[8382] - // 8016 ConL === 0 [[ 8383 ]] #long:0 - // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154] - // - // So the form changed from: - // c1 - (y + [8423 ConvI2L]) - // to - // c1 - (y + -2) - // but the SubL was not added to the IGVN worklist. Investigate why. - // There could be other issues too. - // - // There seems to be a related AddL IGVN optimization that triggers - // the same SubL optimization, so investigate that too. - // - // Found with: - // java -XX:VerifyIterativeGVN=0100 -Xcomp --version - case Op_SubL: - return; - - // SubINode::Ideal does - // Convert "x - (y+c0)" into "(x-y) - c0" AND - // Convert "c1 - (y+c0)" into "(c1-c0) - y" - // - // Investigate why this does not yet happen during IGVN. - // - // Found with: - // test/hotspot/jtreg/compiler/c2/IVTest.java - // -XX:VerifyIterativeGVN=1110 - case Op_SubI: - return; - - // AddNode::IdealIL does transform like: - // Convert x + (con - y) into "(x - y) + con" - // - // In IGVN before verification: - // 8382 ConvI2L - // 8381 ConvI2L === _ 791 [[ 8383 ]] #long:0 - // 8383 SubL === _ 8381 8382 - // 8168 ConvI2L - // 8156 AddL === _ 8168 8383 [[ 8158 ]] - // - // And then in verification: - // 8424 AddL - // 8016 ConL === 0 [[ 8383 ]] #long:0 <--- Was constant folded. - // 8383 SubL === _ 8016 8424 - // 8168 ConvI2L - // 8156 AddL === _ 8168 8383 [[ 8158 ]] - // - // So the form changed from: - // x + (ConvI2L(0) - [8382 ConvI2L]) - // to - // x + (0 - [8424 AddL]) - // but the AddL was not added to the IGVN worklist. Investigate why. - // There could be other issues, too. For example with "commute", see above. - // - // Found with: - // java -XX:VerifyIterativeGVN=0100 -Xcomp --version - case Op_AddL: - return; - // SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does // Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass))); // record_for_cleanup(cmp, phase); @@ -1671,20 +1552,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_ConvI2L: return; - // AddNode::IdealIL can do this transform (and similar other ones): - // Convert "a*b+a*c into a*(b+c) - // The example had AddI(MulI(a, b), MulI(a, c)). Why did this not happen - // during IGVN? There was a mutation for one of the MulI, and only - // after that the pattern was as needed for the optimization. The MulI - // was added to the IGVN worklist, but not the AddI. This probably - // can be fixed by adding the correct pattern in add_users_of_use_to_worklist. - // - // Found with: - // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java - // -XX:VerifyIterativeGVN=1110 - case Op_AddI: - return; - // ArrayCopyNode::Ideal // calls ArrayCopyNode::prepare_array_copy // calls Compile::conv_I2X_index -> is called with sizetype = intcon(0), I think that @@ -1731,67 +1598,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_CastII: return; - // MaxLNode::Ideal - // calls AddNode::Ideal - // calls commute -> decides to swap edges - // - // Another notification issue, because we check inputs of inputs? - // MaxL -> Phi -> Loop - // MaxL -> Phi -> MaxL - // - // Found with: - // compiler/c2/irTests/TestIfMinMax.java - // -XX:VerifyIterativeGVN=1110 - case Op_MaxL: - case Op_MinL: - return; - - // OrINode::Ideal - // calls AddNode::Ideal - // calls commute -> left is Load, right not -> commute. - // - // Not sure why notification does not work here, seems like - // the depth is only 1, so it should work. Needs investigation. - // - // Found with: - // compiler/codegen/TestCharVect2.java#id0 - // -XX:VerifyIterativeGVN=1110 - case Op_OrI: - case Op_OrL: - return; - - // Bool -> constant folded to 1. - // Issue with notification? - // - // Found with: - // compiler/c2/irTests/TestVectorizationMismatchedAccess.java - // -XX:VerifyIterativeGVN=1110 - case Op_Bool: - return; - - // LShiftLNode::Ideal - // Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)" - // Probably a notification issue. - // - // Found with: - // compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java - // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - case Op_LShiftL: - return; - - // LShiftINode::Ideal - // pattern: ((x + con1) << con2) -> x << con2 + con1 << con2 - // Could be issue with notification of inputs of inputs - // - // Side-note: should cases like these not be shared between - // LShiftI and LShiftL? - // - // Found with: - // compiler/escapeAnalysis/Test6689060.java - // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - case Op_LShiftI: - return; - // AddPNode::Ideal seems to do set_req without removing lock first. // Found with various vector tests tier1-tier3. case Op_AddP: @@ -1841,13 +1647,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit // java -XX:VerifyIterativeGVN=1110 -Xcomp --version case Op_CmpP: return; - - // MinINode::Ideal - // Did not investigate, but there are some patterns that might - // need more notification. - case Op_MinI: - case Op_MaxI: // preemptively removed it as well. - return; } if (n->is_Store()) { @@ -2053,24 +1852,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { case Op_CheckCastPP: return; - // In SubNode::Identity, we do: - // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y - // In the example, the AddI had an input replaced, the AddI is - // added to the IGVN worklist, but the SubI is one link further - // down and is not added. I checked add_users_of_use_to_worklist - // where I would expect the SubI would be added, and I cannot - // find the pattern, only this one: - // If changed AddI/SubI inputs, check CmpU for range check optimization. - // - // Fix this "notification" issue and check if there are any other - // issues. - // - // Found with: - // java -XX:VerifyIterativeGVN=1000 -Xcomp --version - case Op_SubI: - case Op_SubL: - return; - // PhiNode::Identity checks for patterns like: // r = (x != con) ? x : con; // that can be constant folded to "x". @@ -2084,22 +1865,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { // -XX:VerifyIterativeGVN=1110 case Op_Phi: return; - - // ConvI2LNode::Identity does - // convert I2L(L2I(x)) => x - // - // Investigate why this did not already happen during IGVN. - // - // Found with: - // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A - // -XX:VerifyIterativeGVN=1110 - case Op_ConvI2L: - return; - - // AbsINode::Identity - // Not investigated yet. - case Op_AbsI: - return; } if (n->is_Load()) { @@ -2533,8 +2298,18 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } - int use_op = use->Opcode(); - if(use->is_Cmp()) { // Enable CMP/BOOL optimization + // AndLNode::Ideal folds GraphKit::mark_word_test patterns. Give it a chance to run. + if (n->is_Load() && use->is_Phi()) { + for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { + Node* u = use->fast_out(i); + if (u->Opcode() == Op_AndL) { + worklist.push(u); + } + } + } + + const int use_op = use->Opcode(); + if (use->is_Cmp()) { // Enable CMP/BOOL optimization add_users_to_worklist0(use, worklist); // Put Bool on worklist for (DUIterator_Fast jmax, j = use->fast_outs(jmax); j < jmax; j++) { Node* bol = use->fast_out(j); @@ -2680,6 +2455,18 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_AndI || u->Opcode() == Op_AndL; }); } + // OrI/LNode::Ideal converts shift/or patterns into rotates, which + // needs to trigger when x and y common. + // e.g. (x << 14) | (y >>> 18) + if (use_op == Op_LShiftI || use_op == Op_URShiftI) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_OrI; + }); + } else if (use_op == Op_LShiftL || use_op == Op_URShiftL) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_OrL; + }); + } // If changed LShift inputs, check CompressBits and ExpandBits users for // compress(x, 1 << n), compress(x, -1 << n), // expand(x, 1 << n), expand(x, -1 << n) optimizations. @@ -2709,6 +2496,33 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_CmpU; }); } + // LShiftNode::IdealIL: + // (x + c1) << c2 -> (x << c2) + c3 + // (c1 - x) << c2 -> c3 - (x << c2) + if (use_op == Op_AddI || use_op == Op_AddL || + use_op == Op_SubI || use_op == Op_SubL) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL; + }); + } + // AddNode::Ideal reassociates constants to the outside, e.g.: + // (x + C) + y -> (x + y) + C + // (x | C) | y -> (x | y) | C + if (use->is_Add()) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == use_op; + }); + } + // If changed AddI/AddL inputs, check for add users: + // e.g. x - (y + n) -> x - (y + C) -> (x - y) + (-C) + // before mutation after mutation optimized by + // AddNode::Ideal + if (use_op == Op_AddI || use_op == Op_AddL) { + int sub_op = (use_op == Op_AddI) ? Op_SubI : Op_SubL; + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == sub_op; + }); + } // If changed AddI/AddL inputs, check URShift users for // "((X << z) + Y) >>> z" optimization in URShift{I,L}Node::Ideal. if (use_op == Op_AddI || use_op == Op_AddL) { @@ -2737,10 +2551,20 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity + // LShiftNode::IdealIL optimizes patterns like: LShift(And(RShift(x, c), Y), c) if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || - u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL || + u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL; + }); + } + // MulNode::AndIL_sum_and_mask can optimize: + // AndX(AddX(x, y), mask) -> AndX(x, mask) + if (use_op == Op_AddI || use_op == Op_AddL) { + const int and_op = (use_op == Op_AddI) ? Op_AndI : Op_AndL; + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == and_op; }); } // Check for redundant conversion patterns: @@ -2750,15 +2574,20 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // ConvI2F->ConvF2I->ConvI2F // Note: there may be other 3-nodes conversion chains that would require to be added here, but these // are the only ones that are known to trigger missed optimizations otherwise + // + // ConvI2LNode::Identity optimizes the following 2-hop optimization, once x is proven to be in int range: + // ConvI2L(ConvL2I(x)) -> x if (use_op == Op_ConvL2D || use_op == Op_ConvI2F || use_op == Op_ConvL2F || - use_op == Op_ConvF2I) { - add_users_to_worklist_if(worklist, use, [=](Node* u) { + use_op == Op_ConvF2I || + use_op == Op_ConvL2I) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { return (use_op == Op_ConvL2D && u->Opcode() == Op_ConvD2L) || (use_op == Op_ConvI2F && u->Opcode() == Op_ConvF2I) || (use_op == Op_ConvL2F && u->Opcode() == Op_ConvF2L) || - (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F); + (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F) || + (use_op == Op_ConvL2I && u->Opcode() == Op_ConvI2L); }); } // ConvD2F::Ideal matches ConvD2F(SqrtD(ConvF2D(x))) => SqrtF(x). @@ -2777,7 +2606,7 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // address expression flattening. if (use_op == Op_AddP) { bool offset_changed = n == use->in(AddPNode::Offset); - add_users_to_worklist_if(worklist, use, [=](Node* u) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { return u->is_Mem() || (offset_changed && u->is_AddP() && u->in(AddPNode::Offset)->is_Con()); }); @@ -2790,8 +2619,12 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }); } // Check for Max/Min(A, Max/Min(B, C)) where A == B or A == C - if (use->is_MinMax()) { - add_users_to_worklist_if(worklist, use, [](Node* u) { return u->is_MinMax(); }); + // MinMaxNode::IdealI also optimizes these cases (if no overflow): + // MinI(AddI(x, -1), x) -> AddI(x, -1) + if (use->is_MinMax() || use->Opcode() == Op_AddI) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->is_MinMax(); + }); } // Check for A | (B | C) and (B | C) | A where A == B or A == C. if (use_op == Op_OrI || use_op == Op_OrL) { @@ -2957,9 +2790,90 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // If changed Sub inputs, check Add for identity. // e.g., (x - y) + y -> x; x + (y - x) -> y. + // SubI/LNode::Ideal also optimize this: + // (x - y) - x -> -y if (use_op == Op_SubI || use_op == Op_SubL) { const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; - add_users_to_worklist_if(worklist, use, [=](Node* u) { return u->Opcode() == add_op; }); + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == add_op || u->Opcode() == use_op; + }); + } + + // If changed Mul inputs, check Add/Sub for common-factor reassociation. + // e.g., (a * b) + (a * c) -> a * (b + c). + if (use_op == Op_MulI || use_op == Op_MulL) { + const int add_op = (use_op == Op_MulI) ? Op_AddI : Op_AddL; + const int sub_op = (use_op == Op_MulI) ? Op_SubI : Op_SubL; + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == add_op || u->Opcode() == sub_op; + }); + } + + // MulNode::Ideal distributes constant multiplication: + // e.g., (x + c1) * c2 -> x * c2 + (c1 * c2) + if (use_op == Op_AddI || use_op == Op_AddL) { + const int mul_op = (use_op == Op_AddI) ? Op_MulI : Op_MulL; + add_users_to_worklist_if(worklist, use, [&](Node* u) { return u->Opcode() == mul_op; }); + } + + // We may have a loop-phi that is about to close the AddI recurrence, + // and we would have to ensure AddNode::commute is called for the AddI. + // e.g. use = Phi(x, n) -> use = Phi(x, u) -> use = Phi(x, u) + // u = AddI(y, use) u = AddI(y, use) -> u = AddI(use, y) + // current state, After mutation, After commute, + // before mutation before commute canonical loop incr + // + // Alternatively, we may have a region-phi, that is about to turn + // into a loop-phi, and where the AddI recurrence is already closed, + // and the AddI has its inputs sorted by idx. In this case, we also + // need AddNode::commute to canonicalize the AddI, so that the phi + // input is on the left now. + // e.g. use = Phi(region, u) -> use = Phi(loop, u) -> use = Phi(loop, u) + // u = AddI(y, use) u = AddI(y, use) -> u = AddI(use, y) + // current state, loop-phi, After commute, + // region-phi before commute canonical loop incr + if (use->is_Phi() && + use->req() == 3 && + (n == use->in(0) || // possibly: region-phi -> loop-phi + n == use->in(LoopNode::LoopBackControl))) { // possibly: closing AddI recurrence + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->is_Add(); + }); + } + + // AndLNode::Ideal optimizes GraphKit::mark_word_test patterns + // Pattern: AndL(LoadL(x=mark word), y) + // Pattern: AndL(Phi(LoadL(x=mark word), z), y) + if (use->is_Load()) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == Op_AndL) { + // n -> Load -> AndL + worklist.push(u); + } else if (u->is_Phi()) { + // n -> Load -> Phi -> AndL + add_users_to_worklist_if(worklist, u, [](Node* u2) { + return u2->Opcode() == Op_AndL; + }); + } + } + } + + // PhiNode::Ideal can optimize: + // Pattern: Phi(condition, x + y, x) -> x + Phi(condition, y, 0) + if (use_op == Op_AddI) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->is_Phi(); + }); + } + + // AndVNode::Ideal and OrVNode::Ideal have 2-hop operations for + // repeated and/or with the same value: + // Pattern: AndV(AndV(a, b), a) -> AndV(a, b) + if (use_op == Op_AndV || use_op == Op_OrV) { + add_users_to_worklist_if(worklist, use, [&](Node* u) { + return u->Opcode() == use_op; + }); } // (Phi (ValueType ValueType)) is transformed into (ValueType (Phi ) (Phi )...) // and is applied if there are EncodeP/DecodeN or casts between a Phi and InlineType nodes diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 8acdf2b8000..62fa32c7045 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1081,10 +1081,12 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod if (is_marked_reduction(use1) && is_marked_reduction(use2)) { Node* use1_in2 = use1->in(2); if (use1_in2->is_Phi() || is_marked_reduction(use1_in2)) { + igvn().rehash_node_delayed(use1); use1->swap_edges(1, 2); } Node* use2_in2 = use2->in(2); if (use2_in2->is_Phi() || is_marked_reduction(use2_in2)) { + igvn().rehash_node_delayed(use2); use2->swap_edges(1, 2); } return PairOrderStatus::Ordered; @@ -1100,14 +1102,17 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod if (i1 != i2) { if ((i1 == (3-i2)) && (use2->is_Add() || use2->is_Mul())) { // 2. Commutative: swap edges, and hope the other position matches too. + igvn().rehash_node_delayed(use2); use2->swap_edges(i1, i2); } else if (VectorNode::is_muladds2i(use2) && use1 != use2) { // 3.a/b: MulAddS2I. if (i1 == 5 - i2) { // ((i1 == 3 && i2 == 2) || (i1 == 2 && i2 == 3) || (i1 == 1 && i2 == 4) || (i1 == 4 && i2 == 1)) + igvn().rehash_node_delayed(use2); use2->swap_edges(1, 2); use2->swap_edges(3, 4); } if (i1 == 3 - i2 || i1 == 7 - i2) { // ((i1 == 1 && i2 == 2) || (i1 == 2 && i2 == 1) || (i1 == 3 && i2 == 4) || (i1 == 4 && i2 == 3)) + igvn().rehash_node_delayed(use2); use2->swap_edges(2, 3); use2->swap_edges(1, 4); } @@ -1118,6 +1123,7 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod } } else if (i1 == i2 && VectorNode::is_muladds2i(use2) && use1 != use2) { // 3.c: MulAddS2I. + igvn().rehash_node_delayed(use2); use2->swap_edges(1, 3); use2->swap_edges(2, 4); return PairOrderStatus::Unknown; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java index 599e95bd6ab..d7b4278ede9 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java @@ -118,8 +118,7 @@ public static void main(String[] args) { switch (args[0]) { case "P0" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=0"); } case "P1" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=1"); } - // Note: increasing the node count limit also helps in some cases. - case "P2" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=2", "-XX:LoopUnrollLimit=1000"); } + case "P2" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=2"); } default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } }; framework.start(); @@ -2116,15 +2115,7 @@ private static long longAddBig() { IRNode.MUL_REDUCTION_VL, "> 0", IRNode.MUL_VL, "> 0"}, applyIfCPUFeatureOr = {"avx512dq", "true", "sve", "true"}, - applyIfAnd = {"AutoVectorizationOverrideProfitability", "> 0", - "LoopUnrollLimit", ">= 1000"}) - @IR(failOn = IRNode.LOAD_VECTOR_L, - applyIfCPUFeature = {"avx512dq", "true"}, - applyIfAnd = {"AutoVectorizationOverrideProfitability", "> 0", - "LoopUnrollLimit", "< 1000"}) - // Increasing the body limit seems to help. Filed for investigation: JDK-8370685 - // If you can eliminate this exception for LoopUnrollLimit, please remove - // the flag completely from the test, also the "addFlags" at the top. + applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) @IR(failOn = IRNode.LOAD_VECTOR_L, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) // MulVL is not implemented on NEON, so we also not have the reduction. From 6602ddf2f2d6b9d5693519bc82b00a91c3838976 Mon Sep 17 00:00:00 2001 From: Liyuan Wang Date: Mon, 5 Oct 2026 17:03:03 +0000 Subject: [PATCH 52/98] 8393048: HijrahDate.withVariant fails when the target Hijrah variant has a shorter month Reviewed-by: jlu --- .../java/time/chrono/HijrahChronology.java | 1 + .../classes/java/time/chrono/HijrahDate.java | 4 +- .../java/time/chrono/HijrahConfigCheck.java | 15 +++++++ .../java/time/chrono/HijrahConfigTest.java | 5 ++- ...-Hijrah-variant_islamic-variant.properties | 42 +++++++++++++++++++ 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-variant_islamic-variant.properties diff --git a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java index ccc3dd3d858..3551e6c80f6 100644 --- a/src/java.base/share/classes/java/time/chrono/HijrahChronology.java +++ b/src/java.base/share/classes/java/time/chrono/HijrahChronology.java @@ -637,6 +637,7 @@ int getDayOfYear(int prolepticYear, int month) { * @return the length of the month */ int getMonthLength(int prolepticYear, int monthOfYear) { + checkCalendarInit(); int epochMonth = yearToEpochMonth(prolepticYear) + (monthOfYear - 1); if (epochMonth < 0 || epochMonth >= hijrahEpochMonthStartDays.length) { throw new DateTimeException("Invalid Hijrah date, year: " + diff --git a/src/java.base/share/classes/java/time/chrono/HijrahDate.java b/src/java.base/share/classes/java/time/chrono/HijrahDate.java index bdd3c0a25c1..d40de870d88 100644 --- a/src/java.base/share/classes/java/time/chrono/HijrahDate.java +++ b/src/java.base/share/classes/java/time/chrono/HijrahDate.java @@ -452,7 +452,7 @@ public HijrahDate with(TemporalAdjuster adjuster) { * HijrahChronology. If the chronology has a shorter month length * for the month, the day is reduced to be the last day of the month. * - * @param chronology the new HijrahChonology, non-null + * @param chronology the new HijrahChronology, non-null * @return a HijrahDate with the requested HijrahChronology, non-null */ public HijrahDate withVariant(HijrahChronology chronology) { @@ -460,7 +460,7 @@ public HijrahDate withVariant(HijrahChronology chronology) { return this; } // Like resolvePreviousValid the day is constrained to stay in the same month - int monthDays = chronology.getDayOfYear(prolepticYear, monthOfYear); + int monthDays = chronology.getMonthLength(prolepticYear, monthOfYear); return HijrahDate.of(chronology, prolepticYear, monthOfYear,(dayOfMonth > monthDays) ? monthDays : dayOfMonth ); } diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java index 317ba15205b..a40686a068b 100644 --- a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java +++ b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigCheck.java @@ -28,11 +28,14 @@ import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.Chronology; +import java.time.chrono.HijrahChronology; +import java.time.chrono.HijrahDate; import java.util.Locale; public class HijrahConfigCheck { private static final String VALID_CALTYPE = "islamic-valid"; private static final String INVALID_CALTYPE = "islamic-invalid"; + private static final String VARIANT_CALTYPE = "islamic-variant"; public static void main(String... args) { // Availability test @@ -71,5 +74,17 @@ public static void main(String... args) { throw new RuntimeException("Unexpected exception for invalid Hijrah configuration", ex); } } + + // Variant configuration test + // Year 1300 month 1 in the variant config has 29 days, + // so day 30 is clamped to 29. + HijrahChronology variantChronology = (HijrahChronology) Chronology.of(VARIANT_CALTYPE); + HijrahDate hijrahDateWithVariant = HijrahDate.of(1300, 1, 30).withVariant(variantChronology); + HijrahDate expected = variantChronology.date(1300,1,29); + if (!hijrahDateWithVariant.equals(expected)) { + throw new RuntimeException("Variant Hijrah configuration did not produce expected date. " + + "Expected: " + expected + ", but got: " + hijrahDateWithVariant); + } + } } diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java index f809a7ab855..23d1a0eec47 100644 --- a/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java +++ b/test/jdk/java/time/nonjunit/java/time/chrono/HijrahConfigTest.java @@ -32,7 +32,7 @@ /* * @test * @summary Tests whether a custom Hijrah configuration properties file works correctly - * @bug 8187987 8392848 + * @bug 8187987 8392848 8393048 * @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g) * @library /tools/lib /test/lib * @modules java.base/jdk.internal.jimage @@ -45,6 +45,7 @@ public class HijrahConfigTest { private static final String VALID_TEST_CONFIG = "hijrah-config-Hijrah-valid_islamic-valid.properties"; private static final String INVALID_TEST_CONFIG = "hijrah-config-Hijrah-invalid_islamic-invalid.properties"; + private static final String VARIANT_TEST_CONFIG = "hijrah-config-Hijrah-variant_islamic-variant.properties"; public static void main(String[] args) throws Exception { Helper helper = Helper.newHelper(); @@ -67,6 +68,8 @@ public static void main(String[] args) throws Exception { confPath.resolve(VALID_TEST_CONFIG)); Files.copy(Path.of(System.getProperty("test.src"), INVALID_TEST_CONFIG), confPath.resolve(INVALID_TEST_CONFIG)); + Files.copy(Path.of(System.getProperty("test.src"), VARIANT_TEST_CONFIG), + confPath.resolve(VARIANT_TEST_CONFIG)); // Run tests Path launcher = outputPath.resolve("bin").resolve("java"); diff --git a/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-variant_islamic-variant.properties b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-variant_islamic-variant.properties new file mode 100644 index 00000000000..a3b70342382 --- /dev/null +++ b/test/jdk/java/time/nonjunit/java/time/chrono/hijrah-config-Hijrah-variant_islamic-variant.properties @@ -0,0 +1,42 @@ +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# A test configuration properties file for a custom Hijrah chronology. +# The data here are solely hypothetical and do not represent any of +# the actual Hijrah variants. + +# Test-only custom chronology for JDK-8393048 +# Year 1300 month 1 is deliberately set as a shorter month of 29 days. + +# Version of this definition +version=0.1 + +# Java chronology ID +id=Hijrah-variant + +# Standard calendar type specification +type=islamic-variant + +# defines the corresponding ISO date to the earliest Hijrah date +iso-start=1882-11-12 + +# 1 2 3 4 5 6 7 8 9 10 11 12 +1300=29 30 30 29 30 30 29 30 29 30 29 29 From a3f7f9c8d961d9df796381ba8b4e943f96f20a49 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Mon, 5 Oct 2026 17:24:22 +0000 Subject: [PATCH 53/98] 8392996: Timezone name formatting repeatedly looks up missing CLDR dstOffset data Reviewed-by: jlu --- .../tools/cldrconverter/CLDRConverter.java | 21 +++++++++++---- .../ResourceBundleGenerator.java | 2 +- .../classes/java/text/SimpleDateFormat.java | 7 +++-- .../time/format/DateTimeFormatterBuilder.java | 4 +-- .../classes/sun/util/calendar/ZoneInfo.java | 13 +++++++++- .../sun/util/calendar/ZoneInfoFile.java | 4 +-- .../util/locale/provider/LocaleResources.java | 26 +++++++++++++++---- .../locale/provider/TimeZoneNameUtility.java | 21 ++++++++++----- 8 files changed, 72 insertions(+), 26 deletions(-) diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index b7754326c97..5de53add192 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -87,7 +87,7 @@ public class CLDRConverter { static final String EXEMPLAR_CITY_PREFIX = "timezone.excity."; static final String ZONE_NAME_PREFIX = "timezone.displayname."; static final String METAZONE_ID_PREFIX = "metazone.id."; - static final String METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset."; + static final String METAZONE_DSTOFFSETS = "metazone.dstoffsets"; static final String PARENT_LOCALE_PREFIX = "parentLocale."; static final String LIKELY_SCRIPT_PREFIX = "likelyScript."; static final String META_EMPTY_ZONE_NAME = "EMPTY_ZONE"; @@ -863,10 +863,21 @@ private static Map extractZoneNames(Map map, Str .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); names.putAll(exCities); - // Explicit metazone offsets - if (id.equals("root")) { - explicitDstOffsets.forEach((k, v) -> - names.put(METAZONE_DSTOFFSET_PREFIX + k, v)); + // Explicit metazone offsets. For example, + // "metazone.dstoffsets" -> "America/Vancouver=-07;Europe/Dublin=+01" + if (id.equals("root") && !explicitDstOffsets.isEmpty()) { + names.put(METAZONE_DSTOFFSETS, + explicitDstOffsets.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .map(e -> { + String tzid = e.getKey(); + if (tzid.isEmpty() || tzid.indexOf('=') >= 0 || tzid.indexOf(';') >= 0) { + throw new IllegalArgumentException("Invalid timezone ID: " + tzid); + } + ZoneOffset.of(e.getValue()); // checks if the offset is valid + return tzid + "=" + e.getValue(); + }) + .collect(Collectors.joining(";"))); } // If there's no UTC entry at this point, add an empty one diff --git a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java index 8e9635ab519..dd06587b0e3 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java @@ -197,7 +197,7 @@ public void generateBundle(String packageName, String baseName, String localeID, var escapedVal = CLDRConverter.escape(valStr); if (type == BundleType.TIMEZONE && !(key.startsWith(CLDRConverter.EXEMPLAR_CITY_PREFIX) || - key.startsWith(CLDRConverter.METAZONE_DSTOFFSET_PREFIX)) || + key.startsWith(CLDRConverter.METAZONE_DSTOFFSETS)) || valStr.startsWith(META_VALUE_PREFIX)) { out.printf(" { \"%s\", %s },\n", keyStr, escapedVal); } else { diff --git a/src/java.base/share/classes/java/text/SimpleDateFormat.java b/src/java.base/share/classes/java/text/SimpleDateFormat.java index 6957ee60bd4..66bbd55192a 100644 --- a/src/java.base/share/classes/java/text/SimpleDateFormat.java +++ b/src/java.base/share/classes/java/text/SimpleDateFormat.java @@ -1303,7 +1303,7 @@ private void subFormat(int patternCharIndex, int count, int zoneOffset = calendar.get(Calendar.ZONE_OFFSET); int dstOffset = calendar.get(Calendar.DST_OFFSET) + zoneOffset; - String explicitDstOffset = null; + ZoneOffset explicitDstOffset = null; // Check if an explicit metazone DST offset exists. // Only check against instances of ZoneInfo, since the standard JDK timezones // are guaranteed to extend this internal type. @@ -1312,14 +1312,13 @@ private void subFormat(int patternCharIndex, int count, if (explicitDstOffset != null) { // The time zone ID has an explicit dst offset. Ensure that // our current TimeZone is canonical. - var canonicalZone = ZoneInfo.getTimeZone(tzid); - if (canonicalZone == null || !canonicalZone.equals(zi)) { + if (!ZoneInfo.hasCanonicalRule(zi)) { explicitDstOffset = null; } } } boolean daylight = explicitDstOffset != null ? - dstOffset == ZoneOffset.of(explicitDstOffset).getTotalSeconds() * 1_000 : + dstOffset == explicitDstOffset.getTotalSeconds() * 1_000 : dstOffset != zoneOffset; if (formatData.locale == null || formatData.isZoneStringsSet) { int zoneIndex = formatData.getZoneIndex(tzid); diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 9bbffe48a5e..d368c3ad8e1 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -4514,9 +4514,9 @@ public boolean format(DateTimePrintContext context, StringBuilder buf, boolean o if (!isGeneric) { if (dt.isSupported(INSTANT_SECONDS)) { // Check if an explicit metazone DST offset exists - String dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); + ZoneOffset dstOffset = TimeZoneNameUtility.explicitDstOffset(zname); if (dt.isSupported(OFFSET_SECONDS) && dstOffset != null) { - type = ZoneOffset.from(dt).equals(ZoneOffset.of(dstOffset)) ? DST : STD; + type = ZoneOffset.from(dt).equals(dstOffset) ? DST : STD; } else { type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD; } diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java index 9de9400c34a..7a90bff7d94 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -596,6 +596,17 @@ public static TimeZone getTimeZone(String ID) { return ZoneInfoFile.getZoneInfo(ID); } + /** + * {@return {@code true} if the specified ZoneInfo object has the same + * rule as the canonical ZoneInfo for the ID, {@code false} otherwise} + * + * @param zi ZoneInfo object to check + */ + public static boolean hasCanonicalRule(ZoneInfo zi) { + var canonical = ZoneInfoFile.getZoneInfo0(zi.getID()); + return canonical != null && canonical.hasSameRules(zi); + } + private transient SimpleTimeZone lastRule; /** diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 874bbd59b0f..c4dfef0fbe8 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,7 +134,7 @@ public static ZoneInfo getZoneInfo(String zoneId) { return zi; } - private static ZoneInfo getZoneInfo0(String zoneId) { + static ZoneInfo getZoneInfo0(String zoneId) { try { ZoneInfo zi = zones.get(zoneId); if (zi != null) { diff --git a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java index 0629eadcb7c..08cd5feb5ca 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/src/java.base/share/classes/sun/util/locale/provider/LocaleResources.java @@ -45,6 +45,7 @@ import java.text.ListFormat; import java.text.MessageFormat; import java.text.NumberFormat; +import java.time.ZoneOffset; import java.time.format.FormatStyle; import java.util.Arrays; import java.util.Calendar; @@ -106,8 +107,8 @@ public class LocaleResources { // TimeZoneNamesBundle exemplar city prefix private static final String TZNB_EXCITY_PREFIX = "timezone.excity."; - // TimeZoneNamesBundle explicit metazone dst offset prefix - private static final String TZNB_METAZONE_DSTOFFSET_PREFIX = "metazone.dstoffset."; + // TimeZoneNamesBundle explicit metazone dst offsets + private static final String TZNB_METAZONE_DSTOFFSETS = "metazone.dstoffsets"; // null singleton cache value private static final Object NULLOBJECT = new Object(); @@ -327,7 +328,7 @@ public Object getTimeZoneNames(String key) { if (Objects.isNull(data) || Objects.isNull(val = data.get())) { TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale); if (key.startsWith(TZNB_EXCITY_PREFIX) || - key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) { + key.startsWith(TZNB_METAZONE_DSTOFFSETS)) { if (tznb.containsKey(key)) { val = tznb.getString(key); assert val instanceof String; @@ -360,6 +361,22 @@ public Object getTimeZoneNames(String key) { return val; } + // Returns explicit DST offset map solely for TimeZoneNameUtility. + // No need to be cached, as it is only invoked once. + public Map getExplicitDstOffsets() { + TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale); + if (tznb.containsKey("metazone.dstoffsets")) { + Map offsets = HashMap.newHashMap(32); + for (String entry : tznb.getString("metazone.dstoffsets").split(";", -1)) { + int separator = entry.indexOf('='); + offsets.put(entry.substring(0, separator), ZoneOffset.of(entry.substring(separator + 1))); + } + return offsets; + } else { + return Map.of(); + } + } + @SuppressWarnings("unchecked") Set getZoneIDs() { Set zoneIDs; @@ -385,7 +402,7 @@ String[][] getZoneStrings() { Set tzIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs())); for (String key : keyset) { if (!key.startsWith(TZNB_EXCITY_PREFIX) && - !key.startsWith(TZNB_METAZONE_DSTOFFSET_PREFIX)) { + !key.startsWith(TZNB_METAZONE_DSTOFFSETS)) { value.add(rb.getStringArray(key)); tzIds.remove(key); } @@ -569,7 +586,6 @@ public String[] getCNPatterns(NumberFormat.Style formatStyle) { return compactNumberPatterns; } - /** * Returns the FormatData resource bundle of this LocaleResources. * The FormatData should be used only for accessing extra diff --git a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java index 014b3af680f..ca0ef170152 100644 --- a/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java +++ b/src/java.base/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java @@ -26,6 +26,7 @@ package sun.util.locale.provider; import java.lang.ref.SoftReference; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -61,6 +62,12 @@ public final class TimeZoneNameUtility { private static final Map>> cachedDisplayNames = new ConcurrentHashMap<>(); + /** + * Explicit DST offset map + */ + private static final LazyConstant> explicitDstOffsets = + LazyConstant.of(TimeZoneNameUtility::initExplicitDstOffsets); + /** * get time zone localized strings. Enumerate all keys. */ @@ -177,12 +184,14 @@ public static Optional canonicalTZID(String id) { * {@return the explicit metazone DST offset for the specified time zone ID if it exists, or else null} * @param tzid the time zone ID */ - public static String explicitDstOffset(String tzid) { - return (String) (LocaleProviderAdapter.forType(CLDR) instanceof CLDRLocaleProviderAdapter ca ? - ca.getLocaleResources(Locale.ROOT) - .getTimeZoneNames("metazone.dstoffset." + - ca.canonicalTZID(tzid).orElse(tzid)) : - null); + public static ZoneOffset explicitDstOffset(String tzid) { + return explicitDstOffsets.get().get(canonicalTZID(tzid).orElse(tzid)); + } + + private static Map initExplicitDstOffsets() { + return LocaleProviderAdapter.forType(CLDR) + .getLocaleResources(Locale.ROOT) + .getExplicitDstOffsets(); } private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { From 8cbb6b7036f1cbc48efea2d9e4830d240ab01fec Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Mon, 5 Oct 2026 19:03:07 +0000 Subject: [PATCH 54/98] 8393201: Most SA tests fail with timeout on macOS 26.6.2 and 27 due to not properly detaching from the debuggee process Reviewed-by: dholmes, sspitsyn --- .../native/libsaproc/MacosxDebuggerLocal.m | 37 +++++++++++-------- .../macosx/native/libsaproc/libproc_impl.h | 2 +- .../libsaproc/{libproc.h => libsaproc.h} | 6 +-- 3 files changed, 26 insertions(+), 19 deletions(-) rename src/jdk.hotspot.agent/macosx/native/libsaproc/{libproc.h => libsaproc.h} (98%) diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m b/src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m index a2f59a7ed3e..5ee6dd99563 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/MacosxDebuggerLocal.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1188,6 +1188,20 @@ static void detach_cleanup(task_t gTask, JNIEnv *env, jobject this_obj, bool thr task_t gTask = getTask(env, this_obj); kern_return_t k_res = 0; + // reply to the previous exception message + k_res = mach_msg(&rep_msg.header, + MACH_SEND_MSG| MACH_SEND_INTERRUPT, + rep_msg.header.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if (k_res != MACH_MSG_SUCCESS) { + print_error("detach: mach_msg() for replying to pending exceptions failed: '%s' (%d)\n", + mach_error_string(k_res), k_res); + detach_cleanup(gTask, env, this_obj, true); + } + // Restore the pre-saved original exception ports registered with the target process for (uint32_t i = 0; i < exception_saved_state.saved_exception_types_count; ++i) { k_res = task_set_exception_ports(gTask, @@ -1210,6 +1224,13 @@ static void detach_cleanup(task_t gTask, JNIEnv *env, jobject this_obj, bool thr detach_cleanup(gTask, env, this_obj, true); } else { + // Sleep 10ms before doing the PT_DETACH. This is necessary to give the Mach + // state of the task (process) time to settle after having done the Mach + // exception reply and restoring the Mach exception ports. If this delay + // is not done, sometimes the SIGSTOP generated during the attach ends + // up triggering again, changing the bsd process status of the process + // to SSTOP, resulting in it remaining suspended after the detach. + usleep(10000); errno = 0; ptrace(PT_DETACH, pid, (caddr_t)1, 0); if (errno != 0) { @@ -1218,20 +1239,6 @@ static void detach_cleanup(task_t gTask, JNIEnv *env, jobject this_obj, bool thr } } - // reply to the previous exception message - k_res = mach_msg(&rep_msg.header, - MACH_SEND_MSG| MACH_SEND_INTERRUPT, - rep_msg.header.msgh_size, - 0, - MACH_PORT_NULL, - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - if (k_res != MACH_MSG_SUCCESS) { - print_error("detach: mach_msg() for replying to pending exceptions failed: '%s' (%d)\n", - mach_error_string(k_res), k_res); - detach_cleanup(gTask, env, this_obj, true); - } - detach_cleanup(gTask, env, this_obj, false); JNI_COCOA_EXIT(env); diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h index ec10f80713b..82d55d4c58f 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.h @@ -28,7 +28,7 @@ #include #include -#include "libproc.h" +#include "libsaproc.h" #include "symtab.h" #define UNSUPPORTED_ARCH "Unsupported architecture!" diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc.h b/src/jdk.hotspot.agent/macosx/native/libsaproc/libsaproc.h similarity index 98% rename from src/jdk.hotspot.agent/macosx/native/libsaproc/libproc.h rename to src/jdk.hotspot.agent/macosx/native/libsaproc/libsaproc.h index b5b48917011..a6ba2e8953f 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc.h +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libsaproc.h @@ -22,8 +22,8 @@ * */ -#ifndef _LIBPROC_H_ -#define _LIBPROC_H_ +#ifndef _LIBSAPROC_H_ +#define _LIBSAPROC_H_ #include #include @@ -145,4 +145,4 @@ uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, // address->nearest symbol lookup. return NULL for no symbol const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset); -#endif //__LIBPROC_H_ +#endif //_LIBSAPROC_H_ From cbeb12ffb4946cca2e895a48fc64ec15c60a96bf Mon Sep 17 00:00:00 2001 From: Shiv Shah Date: Tue, 6 Oct 2026 01:17:48 +0000 Subject: [PATCH 55/98] 8393270: Some Valhalla field layout tests still use UseCompressedClassPointers Reviewed-by: fparain, lfoltan, lmesnik --- .../field_layout/FieldAlignmentTest.java | 52 +++++------------- .../field_layout/NullMarkersTest.java | 54 ++++++------------- .../ValueFieldInheritanceTest.java | 52 +++++------------- 3 files changed, 42 insertions(+), 116 deletions(-) diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/FieldAlignmentTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/FieldAlignmentTest.java index fc2117cd0c2..2a5bb14a704 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/FieldAlignmentTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/FieldAlignmentTest.java @@ -33,58 +33,47 @@ */ /* - * @test id=64_COOP_CCP_NCOH + * @test id=64_COOP_NCOH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java - * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_COOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_COOP_NCOH */ /* - * @test id=64_NCOOP_CCP_NCOH + * @test id=64_NCOOP_NCOH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java - * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_NCOOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_NCOOP_NCOH */ /* - * @test id=64_NCOOP_NCCP_NCOH + * @test id=64_COOP_COH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java - * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_NCOOP_NCCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_COOP_COH */ /* - * @test id=64_COOP_CCP_COH + * @test id=64_NCOOP_COH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java - * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_COOP_CCP_COH - */ - -/* - * @test id=64_NCOOP_CCP_COH - * @requires vm.bits == 64 - * @requires vm.flagless - * @library /test/lib - * @modules java.base/jdk.internal.vm.annotation - * @enablePreview - * @compile FieldLayoutAnalyzer.java FieldAlignmentTest.java - * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_NCOOP_CCP_COH + * @run main runtime.valhalla.valuetypes.field_layout.FieldAlignmentTest 64_NCOOP_COH */ package runtime.valhalla.valuetypes.field_layout; @@ -187,7 +176,6 @@ void generateTestRunner() throws Exception { } static ProcessBuilder exec(String compressedOopsArg, - String compressedKlassPointersArg, String compactObjectHeader, String... args) throws Exception { List argsList = new ArrayList<>(); @@ -198,9 +186,6 @@ static ProcessBuilder exec(String compressedOopsArg, if (compressedOopsArg != null) { Collections.addAll(argsList, compressedOopsArg); } - if (compressedKlassPointersArg != null) { - Collections.addAll(argsList, compressedKlassPointersArg); - } if (compactObjectHeader != null) { Collections.addAll(argsList, compactObjectHeader); } @@ -212,38 +197,27 @@ static ProcessBuilder exec(String compressedOopsArg, public static void main(String[] args) throws Exception { String compressedOopsArg; - String compressedKlassPointersArg; String compactObjectHeaderArg; switch(args[0]) { case "32": compressedOopsArg = null; - compressedKlassPointersArg = null; compactObjectHeaderArg = null; break; - case "64_COOP_CCP_NCOH": + case "64_COOP_NCOH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; - compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; - break; - case "64_NCOOP_CCP_NCOH": - compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; break; - case "64_NCOOP_NCCP_NCOH": + case "64_NCOOP_NCOH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:-UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; break; - case "64_COOP_CCP_COH": + case "64_COOP_COH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:+UseCompactObjectHeaders"; break; - case "64_NCOOP_CCP_COH": + case "64_NCOOP_COH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:+UseCompactObjectHeaders"; break; default: throw new RuntimeException("Unrecognized configuration"); @@ -255,7 +229,7 @@ public static void main(String[] args) throws Exception { fat.generateTestRunner(); // Execute the test runner in charge of loading all test classes - ProcessBuilder pb = exec(compressedOopsArg, compressedKlassPointersArg, compactObjectHeaderArg, "TestRunner"); + ProcessBuilder pb = exec(compressedOopsArg, compactObjectHeaderArg, "TestRunner"); OutputAnalyzer out = new OutputAnalyzer(pb.start()); if (out.getExitValue() != 0) { diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/NullMarkersTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/NullMarkersTest.java index 84afee1105d..dd591d70908 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/NullMarkersTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/NullMarkersTest.java @@ -33,58 +33,47 @@ */ /* - * @test id=64_COOP_CCP_NCOH + * @test id=64_COOP_NCOH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java NullMarkersTest.java - * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_COOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_COOP_NCOH */ /* - * @test id=64_NCOOP_CCP_NCOH + * @test id=64_NCOOP_NCOH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java NullMarkersTest.java - * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_NCOOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_NCOOP_NCOH */ /* - * @test id=64_NCOOP_NCCP_NCOH + * @test id=64_COOP_COH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java NullMarkersTest.java - * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_NCOOP_NCCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_COOP_COH */ /* - * @test id=64_COOP_CCP_COH + * @test id=64_NCOOP_COH * @requires vm.bits == 64 * @requires vm.flagless * @library /test/lib * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java NullMarkersTest.java - * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_COOP_CCP_COH - */ - -/* - * @test id=64_NCOOP_CCP_COH - * @requires vm.bits == 64 - * @requires vm.flagless - * @library /test/lib - * @modules java.base/jdk.internal.vm.annotation - * @enablePreview - * @compile FieldLayoutAnalyzer.java NullMarkersTest.java - * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_NCOOP_CCP_COH + * @run main runtime.valhalla.valuetypes.field_layout.NullMarkersTest 64_NCOOP_COH */ package runtime.valhalla.valuetypes.field_layout; @@ -328,7 +317,7 @@ static void check_5(FieldLayoutAnalyzer fla) { Asserts.assertTrue(fb1.hasNullMarker()); } - static ProcessBuilder exec(String compressedOopsArg, String compressedKlassPointersArg, String... args) throws Exception { + static ProcessBuilder exec(String compressedOopsArg, String compactObjectHeaderArg, String... args) throws Exception { List argsList = new ArrayList<>(); Collections.addAll(argsList, "--enable-preview"); Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions"); @@ -337,8 +326,8 @@ static ProcessBuilder exec(String compressedOopsArg, String compressedKlassPoint if (compressedOopsArg != null) { Collections.addAll(argsList, compressedOopsArg); } - if (compressedKlassPointersArg != null) { - Collections.addAll(argsList, compressedKlassPointersArg); + if (compactObjectHeaderArg != null) { + Collections.addAll(argsList, compactObjectHeaderArg); } Collections.addAll(argsList, "-Xmx256m"); Collections.addAll(argsList, "-XX:+UseNullableAtomicValueFlattening"); @@ -349,38 +338,27 @@ static ProcessBuilder exec(String compressedOopsArg, String compressedKlassPoint public static void main(String[] args) throws Exception { String compressedOopsArg; - String compressedKlassPointersArg; String compactObjectHeaderArg; switch(args[0]) { case "32": compressedOopsArg = null; - compressedKlassPointersArg = null; compactObjectHeaderArg = null; break; - case "64_COOP_CCP_NCOH": + case "64_COOP_NCOH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; - compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; - break; - case "64_NCOOP_CCP_NCOH": - compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; break; - case "64_NCOOP_NCCP_NCOH": + case "64_NCOOP_NCOH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:-UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:-UseCompactObjectHeaders"; break; - case "64_COOP_CCP_COH": + case "64_COOP_COH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:+UseCompactObjectHeaders"; break; - case "64_NCOOP_CCP_COH": + case "64_NCOOP_COH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeaderArg = "-XX:+UseCompactObjectHeaders"; break; default: throw new RuntimeException("Unrecognized configuration"); @@ -390,7 +368,7 @@ public static void main(String[] args) throws Exception { NullMarkersTest fat = new NullMarkersTest(); // Execute the test runner in charge of loading all test classes - ProcessBuilder pb = exec(compressedOopsArg, compressedKlassPointersArg, pkg+"NullMarkersTest$TestRunner"); + ProcessBuilder pb = exec(compressedOopsArg, compactObjectHeaderArg, pkg+"NullMarkersTest$TestRunner"); OutputAnalyzer out = new OutputAnalyzer(pb.start()); if (out.getExitValue() != 0) { diff --git a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/ValueFieldInheritanceTest.java b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/ValueFieldInheritanceTest.java index 54d25755063..ac451801a98 100644 --- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/ValueFieldInheritanceTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/field_layout/ValueFieldInheritanceTest.java @@ -33,58 +33,47 @@ */ /* - * @test id=64_COOP_CCP_NCOH + * @test id=64_COOP_NCOH * @requires vm.bits == 64 * @library /test/lib * @requires vm.flagless * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java ValueFieldInheritanceTest.java - * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_COOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_COOP_NCOH */ /* - * @test id=64_NCOOP_CCP_NCOH + * @test id=64_NCOOP_NCOH * @requires vm.bits == 64 * @library /test/lib * @requires vm.flagless * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java ValueFieldInheritanceTest.java - * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_NCOOP_CCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_NCOOP_NCOH */ /* - * @test id=64_NCOOP_NCCP_NCOH + * @test id=64_COOP_COH * @requires vm.bits == 64 * @library /test/lib * @requires vm.flagless * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java ValueFieldInheritanceTest.java - * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_NCOOP_NCCP_NCOH + * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_COOP_COH */ /* - * @test id=64_COOP_CCP_COH + * @test id=64_NCOOP_COH * @requires vm.bits == 64 * @library /test/lib * @requires vm.flagless * @modules java.base/jdk.internal.vm.annotation * @enablePreview * @compile FieldLayoutAnalyzer.java ValueFieldInheritanceTest.java - * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_COOP_CCP_COH - */ - -/* - * @test id=64_NCOOP_CCP_COH - * @requires vm.bits == 64 - * @library /test/lib - * @requires vm.flagless - * @modules java.base/jdk.internal.vm.annotation - * @enablePreview - * @compile FieldLayoutAnalyzer.java ValueFieldInheritanceTest.java - * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_NCOOP_CCP_COH + * @run main runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest 64_NCOOP_COH */ package runtime.valhalla.valuetypes.field_layout; @@ -194,7 +183,6 @@ public static void main(String[] args) throws Exception { } static ProcessBuilder exec(String compressedOopsArg, - String compressedKlassPointersArg, String compactObjectHeader, String... args) throws Exception { List argsList = new ArrayList<>(); @@ -205,9 +193,6 @@ static ProcessBuilder exec(String compressedOopsArg, if (compressedOopsArg != null) { Collections.addAll(argsList, compressedOopsArg); } - if (compressedKlassPointersArg != null) { - Collections.addAll(argsList, compressedKlassPointersArg); - } if (compactObjectHeader != null) { Collections.addAll(argsList, compactObjectHeader); } @@ -219,45 +204,34 @@ static ProcessBuilder exec(String compressedOopsArg, public static void main(String[] args) throws Exception { String compressedOopsArg; - String compressedKlassPointersArg; String compactObjectHeader; switch(args[0]) { case "32": compressedOopsArg = null; - compressedKlassPointersArg = null; compactObjectHeader = null; break; - case "64_COOP_CCP_NCOH": + case "64_COOP_NCOH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; - compactObjectHeader = "-XX:-UseCompactObjectHeaders"; - break; - case "64_NCOOP_CCP_NCOH": - compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeader = "-XX:-UseCompactObjectHeaders"; break; - case "64_NCOOP_NCCP_NCOH": + case "64_NCOOP_NCOH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:-UseCompressedClassPointers"; compactObjectHeader = "-XX:-UseCompactObjectHeaders"; break; - case "64_COOP_CCP_COH": + case "64_COOP_COH": compressedOopsArg = "-XX:+UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeader = "-XX:+UseCompactObjectHeaders"; break; - case "64_NCOOP_CCP_COH": + case "64_NCOOP_COH": compressedOopsArg = "-XX:-UseCompressedOops"; - compressedKlassPointersArg = "-XX:+UseCompressedClassPointers"; compactObjectHeader = "-XX:+UseCompactObjectHeaders"; break; default: throw new RuntimeException("Unrecognized configuration"); } // Execute the test runner in charge of loading all test classes - ProcessBuilder pb = exec(compressedOopsArg, compressedKlassPointersArg, compactObjectHeader, + ProcessBuilder pb = exec(compressedOopsArg, compactObjectHeader, "runtime.valhalla.valuetypes.field_layout.ValueFieldInheritanceTest$TestRunner"); OutputAnalyzer out = new OutputAnalyzer(pb.start()); From e1845f56f67f8bf956c7839f0fa8e714ccdf512d Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 6 Oct 2026 01:29:14 +0000 Subject: [PATCH 56/98] 8392966: Improve the error message from "java -jar" for JAR files without a manifest Reviewed-by: cstein, alanb --- src/java.base/share/native/libjli/emessages.h | 10 +- src/java.base/share/native/libjli/java.c | 6 +- .../share/native/libjli/parse_manifest.c | 87 +++++------ test/jdk/tools/launcher/MiscTests.java | 146 +++++++++++------- 4 files changed, 141 insertions(+), 108 deletions(-) diff --git a/src/java.base/share/native/libjli/emessages.h b/src/java.base/share/native/libjli/emessages.h index 6fb7cf4ce90..0eef557f1cd 100644 --- a/src/java.base/share/native/libjli/emessages.h +++ b/src/java.base/share/native/libjli/emessages.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ /* * This file primarily consists of all the error and warning messages, that - * are used in JLI_ReportErrorMessage. All message must be defined here, in - * order to help with localizing the messages. + * are used in JLI_ReportErrorMessage. */ #ifndef _EMESSAGES_H @@ -62,8 +61,9 @@ #define JVM_ERROR1 "Error: Could not create the Java Virtual Machine.\n" GEN_ERROR #define JVM_ERROR2 "Error: Could not detach main thread.\n" JNI_ERROR -#define JAR_ERROR2 "Error: Unable to access jarfile %s" -#define JAR_ERROR3 "Error: Invalid or corrupt jarfile %s" +#define JAR_ERROR_CANNOT_OPEN "Error: Unable to open JAR file %s" +#define JAR_ERROR_MANIFEST_MISSING "Error: No manifest in JAR file %s" +#define JAR_ERROR_MANIFEST_PARSE "Error: Unable to parse the manifest, invalid or corrupt JAR file %s" #define CFG_WARN1 "Warning: %s VM not supported; %s VM will be used" #define CFG_WARN2 "Warning: No leading - on line %d of `%s'" diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c index 1236e7cbaf1..0df8a9a3543 100644 --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -1466,9 +1466,11 @@ SetupSplashScreenEnvVars(const char *splash_file_path, char *jar_path) { if ((res = JLI_ParseManifest(jar_path, &info)) != 0) { JLI_FreeManifest(); // cleanup any manifest structure if (res == -1) { - JLI_ReportErrorMessage(JAR_ERROR2, jar_path); + JLI_ReportErrorMessage(JAR_ERROR_CANNOT_OPEN, jar_path); + } else if (res == -2) { + JLI_ReportErrorMessage(JAR_ERROR_MANIFEST_MISSING, jar_path); } else { - JLI_ReportErrorMessage(JAR_ERROR3, jar_path); + JLI_ReportErrorMessage(JAR_ERROR_MANIFEST_PARSE, jar_path); } exit(1); } diff --git a/src/java.base/share/native/libjli/parse_manifest.c b/src/java.base/share/native/libjli/parse_manifest.c index 274825a4af5..6465f232a1c 100644 --- a/src/java.base/share/native/libjli/parse_manifest.c +++ b/src/java.base/share/native/libjli/parse_manifest.c @@ -295,39 +295,27 @@ find_positions(int fd, Byte *eb, jlong* base_offset, jlong* censtart) free(buffer); return (-1); } - +// The size of the buffer for reading the Central Directory of the ZIP/JAR file. +// It needs to be large enough to accommodate the largest possible single record +// and the signature of the next record #define BUFSIZE (3 * 65536 + CENHDR + SIGSIZ) #define MINREAD 1024 /* - * Locate the manifest file with the zip/jar file. - * - * fd: File descriptor of the jar file. - * entry: To be populated with the information necessary to perform - * the inflation (the compressed and uncompressed sizes and - * the offset in the file where the compressed data is located). - * - * Returns zero upon success. Returns a negative value upon failure. - * - * The buffer for reading the Central Directory if the zip/jar file needs - * to be large enough to accommodate the largest possible single record - * and the signature of the next record which is: - * - * 3*2**16 + CENHDR + SIGSIZ + * Locate an entry with the given file_name within the ZIP/JAR file. * - * Each of the three variable sized fields (name, comment and extension) - * has a maximum possible size of 64k. + * fd: File descriptor of the ZIP/JAR file. + * entry: The zentry that will be populated from the located + * entry. Minimally, it will be populated with information + * necessary to perform the inflation - the compressed and + * uncompressed sizes, the compression method, and the offset + * in the file where the entry's data is located. * - * Typically, only a small bit of this buffer is used with bytes shuffled - * down to the beginning of the buffer. It is one thing to allocate such - * a large buffer and another thing to actually start faulting it in. - * - * In most cases, all that needs to be read are the first two entries in - * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid - * in mind when optimizing this code. + * Returns zero upon success. Returns -2 if the entry wasn't found. + * Returns a negative value upon any other failure. */ static int -find_file(int fd, zentry *entry, const char *file_name) +find_file(const int fd, zentry *entry, const char *file_name) { int bytes; int res; @@ -355,8 +343,10 @@ find_file(int fd, zentry *entry, const char *file_name) Byte *buffer; Byte locbuf[LOCHDR]; + // Typically, only a small bit of this buffer is used with bytes shuffled + // down to the beginning of the buffer. if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) { - return(-1); + return -1; } bp = buffer; @@ -372,12 +362,13 @@ find_file(int fd, zentry *entry, const char *file_name) if ((bytes = read(fd, bp, MINREAD)) < 0) { free(buffer); - return (-1); + return -1; } p = bp; /* - * Loop through the Central Directory Headers. Note that a valid zip/jar - * must have an ENDHDR (with ENDSIG) after the Central Directory. + * The presence of ENDHDR (with ENDSIG) has been confirmed + * (through find_positions). We now loop through the Central Directory Headers + * to locate the entry we are looking for. */ while (CENSIG_AT(p)) { @@ -394,7 +385,7 @@ find_file(int fd, zentry *entry, const char *file_name) p = memmove(bp, p, bytes); if ((res = read(fd, bp + bytes, MINREAD)) <= 0) { free(buffer); - return (-1); + return -1; } bytes += res; } @@ -406,29 +397,28 @@ find_file(int fd, zentry *entry, const char *file_name) read_size = (read_size < MINREAD) ? MINREAD : read_size; if ((res = read(fd, bp + bytes, read_size)) <= 0) { free(buffer); - return (-1); + return -1; } bytes += res; } /* - * Check if the name is the droid we are looking for; the jar file - * manifest. If so, build the entry record from the data found in - * the header located and return success. + * Check if this is the entry we are looking for. If so, build the + * entry record from the data found in the header located and return success. */ if ((size_t)CENNAM(p) == JLI_StrLen(file_name) && memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) { if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) { free(buffer); - return (-1); + return -1; } if (read(fd, locbuf, LOCHDR) < 0) { free(buffer); - return (-1); + return -1; } if (!LOCSIG_AT(locbuf)) { free(buffer); - return (-1); + return -1; } entry->isize = CENLEN(p); entry->csize = CENSIZ(p); @@ -436,7 +426,7 @@ find_file(int fd, zentry *entry, const char *file_name) LOCNAM(locbuf) + LOCEXT(locbuf); entry->how = CENHOW(p); free(buffer); - return (0); + return 0; // successfully located the entry } /* @@ -447,7 +437,7 @@ find_file(int fd, zentry *entry, const char *file_name) p += entry_size; } free(buffer); - return (-1); /* Fell off the end the loop without a Manifest */ + return -2; /* entry not found in the ZIP/JAR */ } /* @@ -565,14 +555,14 @@ parse_nv_pair(char **lp, char **name, char **value) } /* - * Read the manifest from the specified jar file and fill in the manifest_info + * Read the manifest from the specified JAR file and fill in the manifest_info * structure with the information found within. * * Error returns are as follows: * 0 Success * -1 Unable to open jarfile - * -2 Error accessing the manifest from within the jarfile (most likely - * a manifest is not present, or this isn't a valid zip/jar file). + * -2 Manifest file is not present in the JAR file + * -3 Any other error when parsing the manifest */ int JLI_ParseManifest(char *jarfile, manifest_info *info) @@ -592,17 +582,20 @@ JLI_ParseManifest(char *jarfile, manifest_info *info) | O_BINARY /* use binary mode on windows */ #endif )) == -1) { - return (-1); + return -1; } info->splashscreen_image_file_name = NULL; if ((rc = find_file(fd, &entry, manifest_name)) != 0) { close(fd); - return (-2); + if (rc == -2) { + return -2; // manfiest entry not found + } + return -3; // some other error } manifest = inflate_file(fd, &entry, NULL); if (manifest == NULL) { close(fd); - return (-2); + return -3; } lp = manifest; while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { @@ -612,9 +605,9 @@ JLI_ParseManifest(char *jarfile, manifest_info *info) } close(fd); if (rc == 0) - return (0); + return 0; else - return (-2); + return -3; } /* diff --git a/test/jdk/tools/launcher/MiscTests.java b/test/jdk/tools/launcher/MiscTests.java index 034f72eb8ff..fa865007628 100644 --- a/test/jdk/tools/launcher/MiscTests.java +++ b/test/jdk/tools/launcher/MiscTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,72 +21,110 @@ * questions. */ +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.spi.ToolProvider; + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.Test; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* * @test - * @bug 8154212 8154470 - * @summary Miscellaneous tests, Exceptions - * @modules jdk.compiler - * jdk.zipfs - * @compile -XDignore.symbol.file MiscTests.java - * @run main MiscTests + * @bug 8154212 8154470 8392966 + * @summary Miscellaneous tests for java launcher + * @library /test/lib + * @build jdk.test.lib.process.ProcessTools jdk.test.lib.JDKToolFinder + * jdk.test.lib.process.OutputAnalyzer + * @run junit ${test.main.class} */ +public class MiscTests { - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class MiscTests extends TestHelper { + private static final ToolProvider JAVAC = ToolProvider.findFirst("javac") + .orElseThrow(() -> new RuntimeException("javac not found")); /** - * Test with class path set on the command line via -Djava.class.path + * Launch "java" with class path set on the command line via -Djava.class.path + * and verify that the launcher correctly launches the main application */ - static void testWithClassPathSetViaProperty() throws IOException { + @Test + void testWithClassPathSetViaProperty() throws Exception { final String mainClass = "Foo"; - - File source = new File(mainClass + ".java"); - - List scratch = new ArrayList<>(); - scratch.add("public class Foo {"); - scratch.add("public static void main(String... args) {"); - scratch.add("}"); - scratch.add("}"); - createFile(source, scratch); - - compile(mainClass + ".java"); - - String dir = new File(mainClass + ".class").getAbsoluteFile().getParent(); - TestResult tr = doExec(javaCmd, "-Djava.class.path=" + dir, mainClass); - for (String s : tr.testOutput) { - System.out.println(s); - } + // create the .java file + final Path sourceFile = Path.of(mainClass + ".java"); + final String content = """ + public class Foo { + public static void main(String... args) {} + } + """; + Files.writeString(sourceFile, content); + // compile it + final int compilationExitVal = JAVAC.run(System.out, System.err, sourceFile.toString()); + assertEquals(0, compilationExitVal, "compilation of " + sourceFile + " failed"); + final Path compiledFile = sourceFile.toAbsolutePath().getParent() + .resolve(mainClass + ".class"); + assertTrue(Files.isRegularFile(compiledFile), "missing or not a regular" + + " file " + compiledFile); + // directory into which the .class was compiled to + final Path classPathDir = compiledFile.getParent(); + // launch "java" with the classpath system property + final List javaCmdArgs = List.of( + "-Djava.class.path=" + classPathDir, + mainClass + ); + final OutputAnalyzer oa = ProcessTools.executeTestJava(javaCmdArgs); + oa.reportDiagnosticSummary(); + oa.shouldHaveExitValue(0); } - static void testJLDEnv() { - final Map envToSet = new HashMap<>(); - envToSet.put("_JAVA_LAUNCHER_DEBUG", "true"); - for (String cmd : new String[] { javaCmd, javacCmd }) { - TestResult tr = doExec(envToSet, cmd, "-version"); - tr.checkPositive(); - String javargs = cmd.equals(javacCmd) ? "on" : "off"; - String progname = cmd.equals(javacCmd) ? "javac" : "java"; - if (!tr.isOK() - || !tr.matches("\\s*debug:on$") - || !tr.matches("\\s*javargs:" + javargs + "$") - || !tr.matches("\\s*program name:" + progname + "$")) { - System.out.println(tr); - } + /** + * Verify that the "_JAVA_LAUNCHER_DEBUG" can be configured to enable + * debug logging for the "java" and "javac" launchers + */ + @Test + void testJavaLauncherDebugEnvVar() throws Exception { + for (String cmd : new String[]{"java", "javac"}) { + final String toolLocation = JDKToolFinder.getJDKTool(cmd); + System.err.println("running test against " + toolLocation); + final ProcessBuilder pb = new ProcessBuilder(List.of(toolLocation, "-version")); + pb.environment().put("_JAVA_LAUNCHER_DEBUG", "true"); + final OutputAnalyzer oa = ProcessTools.executeCommand(pb); + oa.reportDiagnosticSummary(); + oa.shouldHaveExitValue(0); + final String javargs = cmd.equals("javac") ? "on" : "off"; + final String progname = cmd.equals("javac") ? "javac" : "java"; + assertTrue(oa.matches("\\s*debug:on$"), "missing debug:on in output"); + assertTrue(oa.matches("\\s*javargs:" + javargs + "$"), "missing javargs in output"); + assertTrue(oa.matches("\\s*program name:" + progname + "$"), + "missing program name in output"); } } - public static void main(String... args) throws IOException { - testWithClassPathSetViaProperty(); - testJLDEnv(); - if (testExitValue != 0) { - throw new Error(testExitValue + " tests failed"); + /** + * Verify that when a JAR file with no META-INF/MANIFEST.MF file is launched + * using "java -jar" then the launch fails + */ + @Test + void testExecutableJARNoManifest() throws Exception { + final Path jarFile = Files.createTempFile(Path.of("."), "8392966-", ".jar"); + // create a JAR file without any manifest + try (JarOutputStream noManifest = new JarOutputStream(Files.newOutputStream(jarFile))) { + final JarEntry entry = new JarEntry("foo.txt"); + noManifest.putNextEntry(entry); + noManifest.write("bar".getBytes(US_ASCII)); + noManifest.closeEntry(); } + // run "java -jar" against that JAR file and expect the launch to fail + final OutputAnalyzer oa = ProcessTools.executeTestJava("-jar", jarFile.toString()); + oa.shouldNotHaveExitValue(0); // expected to fail with non-zero exit code + // verify it failed for the right reason + oa.shouldContain("Error: No manifest in JAR file"); } } From 95b293d3f30bdda0344bc8830e08a4ff87366a0b Mon Sep 17 00:00:00 2001 From: Daniel Skantz Date: Tue, 6 Oct 2026 05:31:37 +0000 Subject: [PATCH 57/98] 8384260: C2: VM crashes instead of throwing OOM when building a statically overflowing StringConcat Reviewed-by: kvn, dlong --- src/hotspot/share/opto/c2_globals.hpp | 4 +- src/hotspot/share/opto/c2compiler.cpp | 15 ++ src/hotspot/share/opto/c2compiler.hpp | 1 + src/hotspot/share/opto/compile.cpp | 11 +- src/hotspot/share/opto/compile.hpp | 5 + src/hotspot/share/opto/parseHelper.cpp | 2 +- src/hotspot/share/opto/stringopts.cpp | 131 ++++++++++-------- .../TestStackedConcatsManyOverflow.java | 125 +++++++++++++++++ 8 files changed, 234 insertions(+), 60 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyOverflow.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 3cc4049c232..7741d420a46 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -491,8 +491,8 @@ "instructions.") \ \ develop(bool, StressRecompilation, false, \ - "Recompile each compiled method without subsuming loads " \ - "or escape analysis.") \ + "Recompile each compiled method while progressively disabling " \ + "selected optimizations.") \ \ develop(intx, ImplicitNullCheckThreshold, 3, \ "Don't do implicit null checks if NPE's in a method exceeds " \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 9b8d072719f..580a4e72cbf 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -61,6 +61,9 @@ const char* C2Compiler::retry_no_reduce_allocation_merges() { const char* C2Compiler::retry_no_superword() { return "retry without SuperWord"; } +const char* C2Compiler::retry_no_stringopts() { + return "retry without StringOpts"; +} void compiler_stubs_init(bool in_compiler_thread); @@ -134,6 +137,7 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo bool eliminate_boxing = EliminateAutoBox; bool do_locks_coarsening = EliminateLocks; bool do_superword = UseSuperWord; + bool do_stringopts = OptimizeStringConcat; while (!env->failing()) { ResourceMark rm; @@ -145,6 +149,7 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo eliminate_boxing, do_locks_coarsening, do_superword, + do_stringopts, install_code); Compile C(env, target, entry_bci, options, directive); @@ -186,6 +191,12 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo env->report_failure(C.failure_reason()); continue; // retry } + if (C.failure_reason_is(retry_no_stringopts())) { + assert(do_stringopts, "must make progress"); + do_stringopts = false; + env->report_failure(C.failure_reason()); + continue; // retry + } if (C.has_boxed_value()) { // Recompile without boxing elimination regardless failure reason. assert(eliminate_boxing, "must make progress"); @@ -211,6 +222,10 @@ void C2Compiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, boo do_locks_coarsening = false; continue; // retry } + if (do_stringopts) { + do_stringopts = false; + continue; // retry + } } // print inlining for last compilation only C.dump_print_inlining(); diff --git a/src/hotspot/share/opto/c2compiler.hpp b/src/hotspot/share/opto/c2compiler.hpp index 868372568af..d525cae2e3a 100644 --- a/src/hotspot/share/opto/c2compiler.hpp +++ b/src/hotspot/share/opto/c2compiler.hpp @@ -58,6 +58,7 @@ class C2Compiler : public AbstractCompiler { static const char* retry_no_reduce_allocation_merges(); static const char* retry_no_locks_coarsening(); static const char* retry_no_superword(); + static const char* retry_no_stringopts(); // Print compilation timers and statistics void print_timers(); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 42285e5e176..b698172b801 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -589,6 +589,12 @@ void Compile::print_compile_messages() { tty->print_cr("** Bailout: Recompile without locks coarsening **"); tty->print_cr("*********************************************************"); } + if ((do_stringopts() != OptimizeStringConcat) && PrintOpto) { + // Recompiling without string concatenation optimizations + tty->print_cr("*********************************************************"); + tty->print_cr("** Bailout: Recompile without StringOpts **"); + tty->print_cr("*********************************************************"); + } if (env()->break_at_compile()) { // Open the debugger when compiling this method. tty->print("### Breaking when compiling: "); @@ -2751,6 +2757,9 @@ void Compile::inline_string_calls(bool parse_time) { ResourceMark rm; print_method(PHASE_BEFORE_STRINGOPTS, 3); PhaseStringOpts pso(initial_gvn()); + if (C->failing()) { + return; + } print_method(PHASE_AFTER_STRINGOPTS, 3); } @@ -4507,7 +4516,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } case Op_Proj: { - if (OptimizeStringConcat || IncrementalInline) { + if (C->do_stringopts() || IncrementalInline) { ProjNode* proj = n->as_Proj(); if (proj->_is_io_use) { assert(proj->_con == TypeFunc::I_O || proj->_con == TypeFunc::Memory, ""); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index f8542a14233..44077c9386f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -188,6 +188,7 @@ class Options { const bool _eliminate_boxing; // Do boxing elimination. const bool _do_locks_coarsening; // Do locks coarsening const bool _do_superword; // Do SuperWord + const bool _do_stringopts; // Do StringOpts const bool _install_code; // Install the code that was compiled public: Options(bool subsume_loads, @@ -197,6 +198,7 @@ class Options { bool eliminate_boxing, bool do_locks_coarsening, bool do_superword, + bool do_stringopts, bool install_code) : _subsume_loads(subsume_loads), _do_escape_analysis(do_escape_analysis), @@ -205,6 +207,7 @@ class Options { _eliminate_boxing(eliminate_boxing), _do_locks_coarsening(do_locks_coarsening), _do_superword(do_superword), + _do_stringopts(do_stringopts), _install_code(install_code) { } @@ -217,6 +220,7 @@ class Options { /* eliminate_boxing = */ false, /* do_lock_coarsening = */ false, /* do_superword = */ true, + /* do_stringopts = */ false, /* install_code = */ true ); } @@ -600,6 +604,7 @@ class Compile : public Phase { /** Do locks coarsening. */ bool do_locks_coarsening() const { return _options._do_locks_coarsening; } bool do_superword() const { return _options._do_superword; } + bool do_stringopts() const { return _options._do_stringopts; } // Other fixed compilation parameters. ciMethod* method() const { return _method; } diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp index 9f1f67eae89..9543a2a86e6 100644 --- a/src/hotspot/share/opto/parseHelper.cpp +++ b/src/hotspot/share/opto/parseHelper.cpp @@ -321,7 +321,7 @@ void Parse::do_new() { // Keep track of whether opportunities exist for StringBuilder // optimizations. - if (OptimizeStringConcat && + if (C->do_stringopts() && (klass == C->env()->StringBuilder_klass() || klass == C->env()->StringBuffer_klass())) { C->set_has_stringbuilder(true); diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp index e79fa522051..16b34e149cf 100644 --- a/src/hotspot/share/opto/stringopts.cpp +++ b/src/hotspot/share/opto/stringopts.cpp @@ -25,6 +25,7 @@ #include "ci/ciSymbols.hpp" #include "classfile/javaClasses.hpp" #include "compiler/compileLog.hpp" +#include "opto/c2compiler.hpp" #include "opto/callnode.hpp" #include "opto/graphKit.hpp" #include "opto/idealKit.hpp" @@ -717,7 +718,7 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn): Phase(StringOpts), _gvn(gvn) { - assert(OptimizeStringConcat, "shouldn't be here"); + assert(C->do_stringopts(), "shouldn't be here"); // Collect the types needed to talk about the various slices of memory byte_adr_idx = C->get_alias_index(TypeAryPtr::BYTES); @@ -788,6 +789,9 @@ PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn): for (int c = 0; c < concats.length(); c++) { StringConcat* sc = concats.at(c); replace_string_concat(sc); + if (C->failing()) { + return; + } } remove_dead_nodes(); @@ -1910,6 +1914,8 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { coder = __ intcon(java_lang_String::CODER_UTF16); } + bool static_overflow = false; + for (int argi = 0; argi < sc->num_arguments(); argi++) { Node* arg = sc->argument(argi); switch (sc->mode(argi)) { @@ -2059,6 +2065,9 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { default: ShouldNotReachHere(); } + if (kit.stopped()) { + break; + } if (argi > 0) { // Check that the sum won't overflow the destination byte array. IfNode* iff = kit.create_and_map_if(kit.control(), @@ -2066,6 +2075,14 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { PROB_MIN, COUNT_UNKNOWN); kit.set_control(__ IfFalse(iff)); overflow->set_req(argi, __ IfTrue(iff)); + if (kit.stopped()) { + // There could be downstream users in either a later call to replace_string_concat + // or late inlines that expect a live result. This is an edge case: + // having a statically known overflowing concat, where we would throw an OOM error at runtime if it is reached, + // and at this point we have done destructive graph updates; hence do a bailout (with retry). + static_overflow = true; + break; + } } } @@ -2078,69 +2095,71 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { Deoptimization::Action_make_not_entrant); } + if (kit.stopped()) { + assert(static_overflow, "no reachable success path for a non-overflow case."); + C->record_failure(C2Compiler::retry_no_stringopts()); + return; + } + Node* result; - if (!kit.stopped()) { - assert(CompactStrings || (coder->is_Con() && coder->get_int() == java_lang_String::CODER_UTF16), - "Result string must be UTF16 encoded if CompactStrings is disabled"); - - Node* dst_array = nullptr; - if (sc->num_arguments() == 1 && - (sc->mode(0) == StringConcat::StringMode || - sc->mode(0) == StringConcat::StringNullCheckMode)) { - // Handle the case when there is only a single String argument. - // In this case, we can just pull the value from the String itself. - dst_array = kit.load_String_value(sc->argument(0), true); - } else { - // Allocate destination byte array according to coder - dst_array = allocate_byte_array(kit, nullptr, __ LShiftI(length, coder)); - - // Now copy the string representations into the final byte[] - Node* start = __ intcon(0); - for (int argi = 0; argi < sc->num_arguments(); argi++) { - Node* arg = sc->argument(argi); - switch (sc->mode(argi)) { - case StringConcat::NegativeIntCheckMode: - break; // Nothing to do, was only needed to add a runtime check earlier. - case StringConcat::IntMode: { - start = int_getChars(kit, arg, dst_array, coder, start, string_sizes->in(argi)); - break; - } - case StringConcat::StringNullCheckMode: - case StringConcat::StringMode: { - start = copy_string(kit, arg, dst_array, coder, start); - break; - } - case StringConcat::CharMode: { - start = copy_char(kit, arg, dst_array, coder, start); + assert(CompactStrings || (coder->is_Con() && coder->get_int() == java_lang_String::CODER_UTF16), + "Result string must be UTF16 encoded if CompactStrings is disabled"); + + Node* dst_array = nullptr; + if (sc->num_arguments() == 1 && + (sc->mode(0) == StringConcat::StringMode || + sc->mode(0) == StringConcat::StringNullCheckMode)) { + // Handle the case when there is only a single String argument. + // In this case, we can just pull the value from the String itself. + dst_array = kit.load_String_value(sc->argument(0), true); + } else { + // Allocate destination byte array according to coder + dst_array = allocate_byte_array(kit, nullptr, __ LShiftI(length, coder)); + + // Now copy the string representations into the final byte[] + Node* start = __ intcon(0); + for (int argi = 0; argi < sc->num_arguments(); argi++) { + Node* arg = sc->argument(argi); + switch (sc->mode(argi)) { + case StringConcat::NegativeIntCheckMode: + break; // Nothing to do, was only needed to add a runtime check earlier. + case StringConcat::IntMode: { + start = int_getChars(kit, arg, dst_array, coder, start, string_sizes->in(argi)); break; - } - default: - ShouldNotReachHere(); } + case StringConcat::StringNullCheckMode: + case StringConcat::StringMode: { + start = copy_string(kit, arg, dst_array, coder, start); + break; + } + case StringConcat::CharMode: { + start = copy_char(kit, arg, dst_array, coder, start); + break; + } + default: + ShouldNotReachHere(); } } + } - { - PreserveReexecuteState preexecs(&kit); - // The original jvms is for an allocation of either a String or - // StringBuffer so no stack adjustment is necessary for proper - // reexecution. - kit.jvms()->set_should_reexecute(true); - result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); - } + { + PreserveReexecuteState preexecs(&kit); + // The original jvms is for an allocation of either a String or + // StringBuffer so no stack adjustment is necessary for proper + // reexecution. + kit.jvms()->set_should_reexecute(true); + result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); + } - // Initialize the string - kit.store_String_value(result, dst_array); - kit.store_String_coder(result, coder); + // Initialize the string + kit.store_String_value(result, dst_array); + kit.store_String_coder(result, coder); - // The value field is final. Emit a barrier here to ensure that the effect - // of the initialization is committed to memory before any code publishes - // a reference to the newly constructed object (see Parse::do_exits()). - assert(AllocateNode::Ideal_allocation(result) != nullptr, "should be newly allocated"); - kit.insert_mem_bar(UseStoreStoreForCtor ? Op_MemBarStoreStore : Op_MemBarRelease, result); - } else { - result = C->top(); - } + // The value field is final. Emit a barrier here to ensure that the effect + // of the initialization is committed to memory before any code publishes + // a reference to the newly constructed object (see Parse::do_exits()). + assert(AllocateNode::Ideal_allocation(result) != nullptr, "should be newly allocated"); + kit.insert_mem_bar(UseStoreStoreForCtor ? Op_MemBarStoreStore : Op_MemBarRelease, result); // hook up the outgoing control and result kit.replace_call(sc->end(), result); diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyOverflow.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyOverflow.java new file mode 100644 index 00000000000..2e737d624b8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyOverflow.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8384260 + * @summary Test that C2 does not crash when re-using an overflowed StringConcat. + * @requires vm.compiler2.enabled + * @library /test/lib / + * @run main/othervm ${test.main.class} + * @run main/othervm -XX:-TieredCompilation -Xcomp + * -XX:+UnlockDiagnosticVMOptions + * -XX:+AbortVMOnCompilationFailure + * -XX:CompileOnly=${test.main.class}::* + * -XX:CompileCommand=inline,${test.main.class}::double30 + * -XX:CompileCommand=dontinline,java.lang.String::valueOf + * ${test.main.class} + */ + +package compiler.stringopts; + +import jdk.test.lib.Asserts; + +public class TestStackedConcatsManyOverflow { + + public static void main (String... args) { + new StringBuilder(); // Trigger loading of the StringBuilder class. + Asserts.assertThrows(OutOfMemoryError.class, TestStackedConcatsManyOverflow::f); + Asserts.assertThrows(OutOfMemoryError.class, TestStackedConcatsManyOverflow::g); + Asserts.assertThrows(OutOfMemoryError.class, TestStackedConcatsManyOverflow::h); + } + + static String double30() { + String s = "ab"; + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + + return s; + } + + // SIGSEGV (0xb) in PhaseStringOpts::get_constant_coder(GraphKit&, Node*) + static String f() { + // Creating an overflow top()-producing StringConcat + // and a few more doublings to get a target concat receiving top() as an input argument. + String s = double30(); + s = new StringBuilder().append(s).append(s).toString(); + s = new StringBuilder().append(s).append(s).toString(); + // stringopts sets the toString result to top(). Next, it's used in append(s) but stringopts expected live input. + s = new StringBuilder().append('a').append(s).toString(); + return s; + } + + // assert(Compile::current()->inlining_incrementally()) failed: shouldn't happen during parsing + static String g() { + String s = double30(); + s = new StringBuilder().append(s).append(s).toString(); + // if valueOf is not inlined, append will stay as a live late inlining candidate but with a top() input. + s = new StringBuilder().append(s).append(String.valueOf(s)).toString(); + return s; + } + + // assert(Compile::current()->inlining_incrementally()) failed: shouldn't happen during parsing + static String h() { + String s = double30(); + s = new StringBuilder().append(s).append(s).toString(); + // Overflowed and have a StringConcat returning top() + // Non-fluent chain that has an append late inline candidate that consumes top + StringBuilder sb = new StringBuilder(); + sb.append(s); + return sb.toString(); + } +} From b81b760b1567330157cfc94183816be6b603e30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Tue, 6 Oct 2026 06:23:49 +0000 Subject: [PATCH 58/98] 8393014: MappedMemoryUtils.force0 truncates length to DWORD (win) Reviewed-by: alanb --- src/java.base/windows/native/libnio/MappedMemoryUtils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/windows/native/libnio/MappedMemoryUtils.c b/src/java.base/windows/native/libnio/MappedMemoryUtils.c index 6e43dfb505f..cd407719c1c 100644 --- a/src/java.base/windows/native/libnio/MappedMemoryUtils.c +++ b/src/java.base/windows/native/libnio/MappedMemoryUtils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ void JNICALL MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo, */ retry = 0; do { - result = FlushViewOfFile(a, (DWORD)len); + result = FlushViewOfFile(a, (SIZE_T)len); if ((result != 0) || (GetLastError() != ERROR_LOCK_VIOLATION)) break; retry++; From c817d6cbfef05d5e16cba5810b784f57caca4e9f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 6 Oct 2026 06:28:16 +0000 Subject: [PATCH 59/98] 8393327: Missed ValueObjectMethods class initialization in JNIHandles::is_same_object Reviewed-by: heidinga, fparain, shade --- src/hotspot/share/c1/c1_Runtime1.cpp | 7 ++----- src/hotspot/share/interpreter/interpreterRuntime.cpp | 1 - src/hotspot/share/memory/universe.cpp | 10 ++++++++-- src/hotspot/share/prims/jvm.cpp | 1 - src/hotspot/share/runtime/sharedRuntime.cpp | 7 +------ src/hotspot/share/runtime/threads.cpp | 5 +++++ .../classes/java/lang/runtime/ValueObjectMethods.java | 8 ++++++-- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index bfed6fea6dc..1df3a033b7a 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -568,11 +568,8 @@ JRT_ENTRY(int, Runtime1::substitutability_check(JavaThread* current, oopDesc* le args.push_oop(Handle(THREAD, left)); args.push_oop(Handle(THREAD, right)); JavaValue result(T_BOOLEAN); - JavaCalls::call_static(&result, - vmClasses::ValueObjectMethods_klass(), - vmSymbols::isSubstitutable_name(), - vmSymbols::object_object_boolean_signature(), - &args, CHECK_0); + methodHandle method(current, Universe::is_substitutable_method()); + JavaCalls::call(&result, method, &args, CHECK_0); return result.get_jboolean() ? 1 : 0; JRT_END diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index d3740dfe0ef..9bed85d1e16 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -327,7 +327,6 @@ JRT_ENTRY(jboolean, InterpreterRuntime::is_substitutable(JavaThread* current, oo args.push_oop(ha); args.push_oop(hb); methodHandle method(current, Universe::is_substitutable_method()); - method->method_holder()->initialize(CHECK_false); // Ensure class ValueObjectMethods is initialized JavaCalls::call(&result, method, &args, THREAD); Exceptions::wrap_exception_in_internal_error("Internal error in substitutability test", CHECK_false); diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 5fd16e4e833..39bb2a8fcbf 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -1087,8 +1087,14 @@ Method* Universe::loader_addClass_method() { return _loader_addClass_c Method* Universe::throw_illegal_access_error() { return _throw_illegal_access_error_cache.get_method(); } Method* Universe::throw_no_such_method_error() { return _throw_no_such_method_error_cache.get_method(); } Method* Universe::do_stack_walk_method() { return _do_stack_walk_cache.get_method(); } -Method* Universe::is_substitutable_method() { return _is_substitutable_cache.get_method(); } -Method* Universe::value_object_hash_code_method() { return _value_object_hash_code_cache.get_method(); } +Method* Universe::is_substitutable_method() { + assert(vmClasses::ValueObjectMethods_klass()->is_initialized(), "value object comparison occurring too early"); + return _is_substitutable_cache.get_method(); +} +Method* Universe::value_object_hash_code_method() { + assert(vmClasses::ValueObjectMethods_klass()->is_initialized(), "value object hashcode occurring too early"); + return _value_object_hash_code_cache.get_method(); +} void Universe::initialize_known_methods(JavaThread* current) { // Set up static method for registering finalizers diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index d8a6fffaa40..e4e37636a87 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -805,7 +805,6 @@ JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle)) Handle ho(THREAD, obj); args.push_oop(ho); methodHandle method(THREAD, Universe::value_object_hash_code_method()); - method->method_holder()->initialize(CHECK_0); // Ensure class ValueObjectMethods is initialized JavaCalls::call(&result, method, &args, THREAD); Exceptions::wrap_exception_in_internal_error("Internal error in hashCode", CHECK_0); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index e21bdb3f2c6..b0fa8af184d 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1201,13 +1201,8 @@ Handle SharedRuntime::find_callee_info_helper(vframeStream& vfst, Bytecodes::Cod bc = Bytecodes::_invokestatic; methodHandle attached_method(THREAD, extract_attached_method(vfst)); assert(attached_method.not_null(), "must have attached method"); - vmClasses::ValueObjectMethods_klass()->initialize(CHECK_NH); LinkResolver::resolve_invoke(callinfo, receiver, attached_method, bc, false, CHECK_NH); -#ifdef ASSERT - Symbol* subst_method_name = vmSymbols::isSubstitutable_name(); - Method* is_subst = vmClasses::ValueObjectMethods_klass()->find_method(subst_method_name, vmSymbols::object_object_boolean_signature()); - assert(callinfo.selected_method() == is_subst, "must be isSubstitutable method"); -#endif + assert(callinfo.selected_method() == Universe::is_substitutable_method(), "must be isSubstitutable method"); return receiver; } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 1000a7563df..0e1b8519fc9 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -377,6 +377,11 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) { initialize_class(vmSymbols::jdk_internal_misc_UnsafeConstants(), CHECK); jdk_internal_misc_UnsafeConstants::set_unsafe_constants(); + // Initialize Value type support - this also initializes Unsafe. + if (Arguments::is_valhalla_enabled()) { + initialize_class(vmSymbols::java_lang_runtime_ValueObjectMethods(), CHECK); + } + // The VM preresolves methods to these classes. Make sure that they get initialized initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK); initialize_class(vmSymbols::java_lang_ref_Finalizer(), CHECK); diff --git a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java index bef030e5fc8..8263b448926 100644 --- a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java @@ -32,11 +32,15 @@ * * ValueObjectMethods::isSubstitutable and valueObjectHashCode are * private entry points called by VM. + * + * This class is initialized very early during VM initialization so that + * value objects can be used during the main bootstrap process. As a result + * the initialization of this class must have minimal dependencies on other + * core library classes. */ final class ValueObjectMethods { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final boolean VERBOSE = - System.getProperty("value.bsm.debug") != null; + private static final boolean VERBOSE = false; private ValueObjectMethods() { } From a7413fa8c0dfef2a77919d2cf501eff24f134133 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 6 Oct 2026 06:58:13 +0000 Subject: [PATCH 60/98] 8392839: Deprecation warning for SourcePositions.getStartPosition() usage when building the JDK Reviewed-by: liach, jlahoda --- make/langtools/tools/previewfeature/SetupPreviewFeature.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/make/langtools/tools/previewfeature/SetupPreviewFeature.java b/make/langtools/tools/previewfeature/SetupPreviewFeature.java index 5f9b00edc6d..8a6ceb39744 100644 --- a/make/langtools/tools/previewfeature/SetupPreviewFeature.java +++ b/make/langtools/tools/previewfeature/SetupPreviewFeature.java @@ -69,8 +69,7 @@ public static void main(String... args) throws Exception { var trees = Trees.instance(task); var elPath = trees.getPath(el); insertPosition = (int) trees.getSourcePositions() - .getStartPosition(elPath.getCompilationUnit(), - elPath.getLeaf()); + .getStartPosition(elPath.getLeaf()); } } } From f07100fca80db480e38caa59e7ed6362d908b9d7 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Tue, 6 Oct 2026 07:23:33 +0000 Subject: [PATCH 61/98] 8393458: Fix various include guard inconsistencies Reviewed-by: chagedorn, dholmes --- src/hotspot/cpu/aarch64/foreignGlobals_aarch64.hpp | 8 ++++---- src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp | 8 ++++---- src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp | 8 ++++---- .../cpu/arm/continuationFreezeThaw_arm.inline.hpp | 6 +++--- src/hotspot/cpu/arm/foreignGlobals_arm.hpp | 7 ++++--- src/hotspot/cpu/arm/stubDeclarations_arm.hpp | 8 ++++---- src/hotspot/cpu/arm/vmstorage_arm.hpp | 8 ++++---- .../cpu/ppc/continuationFreezeThaw_ppc.inline.hpp | 7 ++++--- src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp | 4 ++-- src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp | 8 ++++---- src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp | 8 ++++---- src/hotspot/cpu/ppc/vmstorage_ppc.hpp | 8 ++++---- .../cpu/riscv/continuationHelper_riscv.inline.hpp | 4 ++-- src/hotspot/cpu/riscv/foreignGlobals_riscv.hpp | 8 ++++---- .../gc/shared/cardTableBarrierSetAssembler_riscv.hpp | 2 +- src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp | 8 ++++---- src/hotspot/cpu/riscv/vmreg_riscv.inline.hpp | 8 ++++---- src/hotspot/cpu/riscv/vmstorage_riscv.hpp | 8 ++++---- .../cpu/s390/continuationFreezeThaw_s390.inline.hpp | 6 +++--- src/hotspot/cpu/s390/foreignGlobals_s390.hpp | 7 ++++--- src/hotspot/cpu/s390/stubDeclarations_s390.hpp | 6 +++--- src/hotspot/cpu/s390/vmstorage_s390.hpp | 8 ++++---- src/hotspot/cpu/x86/c2_intelJccErratum_x86.hpp | 9 ++++----- .../cpu/x86/continuationFreezeThaw_x86.inline.hpp | 4 ++-- src/hotspot/cpu/x86/foreignGlobals_x86.hpp | 8 ++++---- src/hotspot/cpu/x86/stubDeclarations_x86.hpp | 8 ++++---- src/hotspot/cpu/x86/vmstorage_x86.hpp | 8 ++++---- src/hotspot/cpu/zero/continuationEntry_zero.inline.hpp | 3 +-- .../cpu/zero/continuationFreezeThaw_zero.inline.hpp | 8 ++++---- src/hotspot/cpu/zero/foreignGlobals_zero.hpp | 8 ++++---- src/hotspot/cpu/zero/stubDeclarations_zero.hpp | 8 ++++---- src/hotspot/cpu/zero/vmstorage_zero.hpp | 8 ++++---- src/hotspot/cpu/zero/zeroInterpreter_zero.hpp | 8 ++++---- src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 6 +++--- src/hotspot/os/linux/cgroupUtil_linux.hpp | 6 +++--- src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 6 +++--- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 6 +++--- src/hotspot/os/linux/compilerThreadTimeout_linux.hpp | 8 ++++---- src/hotspot/os/windows/systemMemoryBarrier_windows.hpp | 8 ++++---- src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp | 8 ++++---- .../os_cpu/bsd_aarch64/pauth_bsd_aarch64.inline.hpp | 2 +- .../os_cpu/linux_aarch64/icache_linux_aarch64.hpp | 8 ++++---- src/hotspot/os_cpu/linux_riscv/copy_linux_riscv.hpp | 8 ++++---- src/hotspot/os_cpu/linux_riscv/globals_linux_riscv.hpp | 8 ++++---- .../os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp | 6 +++--- src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp | 8 ++++---- src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.hpp | 8 ++++---- .../os_cpu/windows_aarch64/icache_windows_aarch64.hpp | 8 ++++---- src/hotspot/share/cds/aotStreamedHeapLoader.hpp | 5 +++-- src/hotspot/share/ci/ciFlatArray.hpp | 6 +++--- src/hotspot/share/ci/ciFlatArrayKlass.hpp | 8 ++++---- src/hotspot/share/ci/ciRefArrayKlass.hpp | 8 ++++---- src/hotspot/share/ci/ciValueKlass.hpp | 6 +++--- src/hotspot/share/code/codeCache.inline.hpp | 8 ++++---- .../compiler/compilationMemStatInternals.inline.hpp | 8 ++++---- src/hotspot/share/compiler/oopMap.inline.hpp | 7 +++---- src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp | 1 - src/hotspot/share/gc/g1/g1MonotonicArena.hpp | 2 +- src/hotspot/share/gc/g1/g1NUMA.hpp | 8 ++++---- src/hotspot/share/gc/g1/g1NUMAStats.hpp | 8 ++++---- src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp | 6 +++--- src/hotspot/share/gc/serial/serialStringDedup.hpp | 7 ++++--- src/hotspot/share/gc/serial/serialStringDedup.inline.hpp | 7 ++++--- src/hotspot/share/gc/shared/fullGCForwarding.inline.hpp | 7 ++++--- src/hotspot/share/gc/shared/pretouchTask.hpp | 6 +++--- src/hotspot/share/gc/shared/scavengableNMethodsData.hpp | 8 ++++---- .../share/gc/shared/vmThreadCpuTimeScope.inline.hpp | 7 ++++++- .../heuristics/shenandoahGenerationalHeuristics.hpp | 1 - src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp | 6 +++--- .../share/gc/shenandoah/shenandoahGenerationalHeap.hpp | 6 +++--- .../share/gc/shenandoah/shenandoahGlobalGeneration.hpp | 7 +++---- .../share/gc/shenandoah/shenandoahInPlacePromoter.hpp | 1 - .../share/gc/shenandoah/shenandoahOldGeneration.hpp | 6 +++--- .../gc/shenandoah/shenandoahScanRemembered.inline.hpp | 6 +++--- .../share/gc/shenandoah/shenandoahUncommitThread.hpp | 6 +++--- src/hotspot/share/gc/shenandoah/shenandoahUnload.hpp | 6 +++--- .../share/gc/shenandoah/shenandoahYoungGeneration.hpp | 6 +++--- src/hotspot/share/gc/z/zMarkContext.inline.hpp | 4 ++-- src/hotspot/share/gc/z/zMarkingSMR.hpp | 8 ++++---- .../share/interpreter/zero/bytecodeInterpreter.hpp | 8 ++++---- .../interpreter/zero/bytecodeInterpreter.inline.hpp | 8 ++++---- src/hotspot/share/interpreter/zero/zeroInterpreter.hpp | 8 ++++---- .../share/interpreter/zero/zeroInterpreterGenerator.hpp | 8 ++++---- src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp | 8 ++++---- .../types/traceid/jfrTraceIdLoadBarrier.inline.hpp | 6 +++--- src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp | 8 ++++---- .../jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp | 8 ++++---- .../share/jfr/recorder/storage/jfrFullStorage.inline.hpp | 8 ++++---- src/hotspot/share/jfr/support/jfrResolution.hpp | 6 +++--- .../jfr/support/methodtracer/jfrClassFilterClosure.hpp | 6 +++--- src/hotspot/share/jfr/utilities/jfrConcurrentQueue.hpp | 3 +-- src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp | 8 ++++---- src/hotspot/share/memory/metaspace/counters.hpp | 3 +-- src/hotspot/share/memory/metaspace/metaspaceArena.hpp | 3 +-- src/hotspot/share/memory/metaspace/metaspaceContext.hpp | 3 +-- .../share/memory/metaspace/metaspaceStatistics.hpp | 3 +-- src/hotspot/share/nmt/mallocLimit.hpp | 8 ++++---- src/hotspot/share/nmt/memMapPrinter.hpp | 8 ++++---- src/hotspot/share/nmt/memTag.hpp | 6 +++--- src/hotspot/share/nmt/nmtPreInit.hpp | 9 ++++----- src/hotspot/share/nmt/regionsTree.hpp | 7 ++++--- src/hotspot/share/nmt/threadStackTracker.hpp | 3 +-- src/hotspot/share/oops/flatArrayKlass.hpp | 8 ++++---- src/hotspot/share/oops/flatArrayKlass.inline.hpp | 7 ++++--- src/hotspot/share/oops/flatArrayOop.hpp | 6 +++--- src/hotspot/share/oops/flatArrayOop.inline.hpp | 6 +++--- src/hotspot/share/oops/valueKlass.hpp | 6 +++--- src/hotspot/share/oops/valueKlass.inline.hpp | 9 +++++---- src/hotspot/share/oops/valuePayload.hpp | 6 +++--- src/hotspot/share/oops/valuePayload.inline.hpp | 6 +++--- src/hotspot/share/opto/printinlining.hpp | 8 ++++---- src/hotspot/share/opto/superwordVTransformBuilder.hpp | 8 ++++---- src/hotspot/share/opto/valuetypenode.hpp | 6 +++--- src/hotspot/share/prims/downcallLinker.hpp | 6 +++--- src/hotspot/share/prims/foreignGlobals.hpp | 8 ++++---- src/hotspot/share/prims/foreignGlobals.inline.hpp | 6 +++--- src/hotspot/share/prims/jvmtiTagMapTable.hpp | 8 ++++---- src/hotspot/share/prims/scopedMemoryAccess.hpp | 8 ++++---- src/hotspot/share/prims/upcallLinker.hpp | 8 ++++---- src/hotspot/share/runtime/continuation.hpp | 8 ++++---- src/hotspot/share/runtime/continuationEntry.hpp | 6 +++--- src/hotspot/share/runtime/continuationEntry.inline.hpp | 8 ++++---- src/hotspot/share/runtime/continuationHelper.hpp | 8 ++++---- src/hotspot/share/runtime/continuationHelper.inline.hpp | 8 ++++---- src/hotspot/share/runtime/continuationWrapper.inline.hpp | 8 ++++---- src/hotspot/share/runtime/flags/debug_globals.hpp | 8 ++++---- src/hotspot/share/runtime/osThreadBase.hpp | 6 +++--- src/hotspot/share/runtime/smallRegisterMap.inline.hpp | 8 ++++---- .../share/runtime/stackChunkFrameStream.inline.hpp | 6 +++--- src/hotspot/share/runtime/stackFrameStream.hpp | 4 +--- src/hotspot/share/runtime/stackFrameStream.inline.hpp | 4 +--- src/hotspot/share/sanitizers/leak.hpp | 3 ++- src/hotspot/share/utilities/checkedCast.hpp | 3 +-- src/hotspot/share/utilities/devirtualizer.inline.hpp | 1 - src/hotspot/share/utilities/parseInteger.hpp | 6 +++--- src/hotspot/share/utilities/resizableHashTable.hpp | 6 +++--- src/hotspot/share/utilities/tableStatistics.hpp | 6 +++--- src/hotspot/share/utilities/unalignedAccess.hpp | 6 +++--- src/hotspot/share/utilities/virtualizationSupport.hpp | 3 +-- src/hotspot/share/utilities/vmassert_reinstall.hpp | 3 +-- src/hotspot/share/utilities/vmassert_uninstall.hpp | 3 +-- 141 files changed, 452 insertions(+), 459 deletions(-) diff --git a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.hpp b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.hpp index c4f11266871..5ccba0e6878 100644 --- a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,8 +22,8 @@ * questions. */ -#ifndef CPU_AARCH64_VM_FOREIGN_GLOBALS_AARCH64_HPP -#define CPU_AARCH64_VM_FOREIGN_GLOBALS_AARCH64_HPP +#ifndef CPU_AARCH64_FOREIGNGLOBALS_AARCH64_HPP +#define CPU_AARCH64_FOREIGNGLOBALS_AARCH64_HPP #include "asm/macroAssembler.hpp" #include "utilities/growableArray.hpp" @@ -49,4 +49,4 @@ struct ABIDescriptor { bool is_volatile_reg(FloatRegister reg) const; }; -#endif // CPU_AARCH64_VM_FOREIGN_GLOBALS_AARCH64_HPP +#endif // CPU_AARCH64_FOREIGNGLOBALS_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index d1e0621f6a9..ba750cd23d6 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_AARCH64_STUBDECLARATIONS_HPP -#define CPU_AARCH64_STUBDECLARATIONS_HPP +#ifndef CPU_AARCH64_STUBDECLARATIONS_AARCH64_HPP +#define CPU_AARCH64_STUBDECLARATIONS_AARCH64_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -192,4 +192,4 @@ atomic_cmpxchg_8_seq_cst_impl) \ -#endif // CPU_AARCH64_STUBDECLARATIONS_HPP +#endif // CPU_AARCH64_STUBDECLARATIONS_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp b/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp index e46519e8862..a0065ae7828 100644 --- a/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_AARCH64_VMSTORAGE_AARCH64_INLINE_HPP -#define CPU_AARCH64_VMSTORAGE_AARCH64_INLINE_HPP +#ifndef CPU_AARCH64_VMSTORAGE_AARCH64_HPP +#define CPU_AARCH64_VMSTORAGE_AARCH64_HPP #include @@ -83,4 +83,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_AARCH64_VMSTORAGE_AARCH64_INLINE_HPP \ No newline at end of file +#endif // CPU_AARCH64_VMSTORAGE_AARCH64_HPP \ No newline at end of file diff --git a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp index 1868d61a831..79f12ba7942 100644 --- a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef CPU_ARM_CONTINUATION_ARM_INLINE_HPP -#define CPU_ARM_CONTINUATION_ARM_INLINE_HPP +#ifndef CPU_ARM_CONTINUATIONFREEZETHAW_ARM_INLINE_HPP +#define CPU_ARM_CONTINUATIONFREEZETHAW_ARM_INLINE_HPP #include "oops/stackChunkOop.inline.hpp" #include "runtime/frame.hpp" @@ -123,4 +123,4 @@ inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } -#endif // CPU_ARM_CONTINUATION_ARM_INLINE_HPP +#endif // CPU_ARM_CONTINUATIONFREEZETHAW_ARM_INLINE_HPP diff --git a/src/hotspot/cpu/arm/foreignGlobals_arm.hpp b/src/hotspot/cpu/arm/foreignGlobals_arm.hpp index 701647cc4b2..66783737e34 100644 --- a/src/hotspot/cpu/arm/foreignGlobals_arm.hpp +++ b/src/hotspot/cpu/arm/foreignGlobals_arm.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,9 +22,9 @@ * questions. */ -#ifndef CPU_ARM_VM_FOREIGN_GLOBALS_ARM_HPP -#define CPU_ARM_VM_FOREIGN_GLOBALS_ARM_HPP +#ifndef CPU_ARM_FOREIGNGLOBALS_ARM_HPP +#define CPU_ARM_FOREIGNGLOBALS_ARM_HPP class ABIDescriptor {}; -#endif // CPU_ARM_VM_FOREIGN_GLOBALS_ARM_HPP +#endif // CPU_ARM_FOREIGNGLOBALS_ARM_HPP diff --git a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp index 5fb0d4e901f..666cad441f4 100644 --- a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp +++ b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_ARM_STUBDECLARATIONS_HPP -#define CPU_ARM_STUBDECLARATIONS_HPP +#ifndef CPU_ARM_STUBDECLARATIONS_ARM_HPP +#define CPU_ARM_STUBDECLARATIONS_ARM_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -77,4 +77,4 @@ do_arch_blob(final, 22000) \ -#endif // CPU_ARM_STUBDECLARATIONS_HPP +#endif // CPU_ARM_STUBDECLARATIONS_ARM_HPP diff --git a/src/hotspot/cpu/arm/vmstorage_arm.hpp b/src/hotspot/cpu/arm/vmstorage_arm.hpp index 41bebd01fd7..be15afb450e 100644 --- a/src/hotspot/cpu/arm/vmstorage_arm.hpp +++ b/src/hotspot/cpu/arm/vmstorage_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_ARM_VMSTORAGE_ARM_INLINE_HPP -#define CPU_ARM_VMSTORAGE_ARM_INLINE_HPP +#ifndef CPU_ARM_VMSTORAGE_ARM_HPP +#define CPU_ARM_VMSTORAGE_ARM_HPP #include @@ -49,4 +49,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_ARM_VMSTORAGE_ARM_INLINE_HPP \ No newline at end of file +#endif // CPU_ARM_VMSTORAGE_ARM_HPP \ No newline at end of file diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index 3f81b1f482e..cf2f91841ab 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef CPU_PPC_CONTINUATION_PPC_INLINE_HPP -#define CPU_PPC_CONTINUATION_PPC_INLINE_HPP +#ifndef CPU_PPC_CONTINUATIONFREEZETHAW_PPC_INLINE_HPP +#define CPU_PPC_CONTINUATIONFREEZETHAW_PPC_INLINE_HPP #include "oops/stackChunkOop.inline.hpp" #include "runtime/frame.hpp" @@ -682,4 +682,5 @@ inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) { // ======================== (aligned) // // -#endif // CPU_PPC_CONTINUATION_PPC_INLINE_HPP + +#endif // CPU_PPC_CONTINUATIONFREEZETHAW_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp index d1bc6ee620a..95456c8e08e 100644 --- a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,4 +195,4 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f return f.fp(); } -#endif // CPU_PPC_CONTINUATIONFRAMEHELPERS_PPC_INLINE_HPP +#endif // CPU_PPC_CONTINUATIONHELPER_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp b/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp index b25ee28f192..b78e4f2b32c 100644 --- a/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp +++ b/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,8 +22,8 @@ * questions. */ -#ifndef CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP -#define CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP +#ifndef CPU_PPC_FOREIGNGLOBALS_PPC_HPP +#define CPU_PPC_FOREIGNGLOBALS_PPC_HPP #include "asm/macroAssembler.hpp" #include "utilities/growableArray.hpp" @@ -44,4 +44,4 @@ struct ABIDescriptor { bool is_volatile_reg(FloatRegister reg) const; }; -#endif // CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP +#endif // CPU_PPC_FOREIGNGLOBALS_PPC_HPP diff --git a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp index 41b8b71486d..ca242988a37 100644 --- a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp +++ b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_PPC_STUBDECLARATIONS_HPP -#define CPU_PPC_STUBDECLARATIONS_HPP +#ifndef CPU_PPC_STUBDECLARATIONS_PPC_HPP +#define CPU_PPC_STUBDECLARATIONS_PPC_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -66,4 +66,4 @@ do_arch_blob(final, 24000) \ -#endif // CPU_PPC_STUBDECLARATIONS_HPP +#endif // CPU_PPC_STUBDECLARATIONS_PPC_HPP diff --git a/src/hotspot/cpu/ppc/vmstorage_ppc.hpp b/src/hotspot/cpu/ppc/vmstorage_ppc.hpp index bfff78a5cb6..ad0b1bc15ee 100644 --- a/src/hotspot/cpu/ppc/vmstorage_ppc.hpp +++ b/src/hotspot/cpu/ppc/vmstorage_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,8 +22,8 @@ * questions. */ -#ifndef CPU_PPC_VMSTORAGE_PPC_INLINE_HPP -#define CPU_PPC_VMSTORAGE_PPC_INLINE_HPP +#ifndef CPU_PPC_VMSTORAGE_PPC_HPP +#define CPU_PPC_VMSTORAGE_PPC_HPP #include @@ -107,4 +107,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_PPC_VMSTORAGE_PPC_INLINE_HPP +#endif // CPU_PPC_VMSTORAGE_PPC_HPP diff --git a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp index 6aa6a3d7895..04d9325127d 100644 --- a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -161,4 +161,4 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f return f.fp(); } -#endif // CPU_RISCV_CONTINUATIONFRAMEHELPERS_RISCV_INLINE_HPP +#endif // CPU_RISCV_CONTINUATIONHELPER_RISCV_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/foreignGlobals_riscv.hpp b/src/hotspot/cpu/riscv/foreignGlobals_riscv.hpp index 98710a390ae..a559a2f3c74 100644 --- a/src/hotspot/cpu/riscv/foreignGlobals_riscv.hpp +++ b/src/hotspot/cpu/riscv/foreignGlobals_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_RISCV_FOREIGN_GLOBALS_RISCV_HPP -#define CPU_RISCV_FOREIGN_GLOBALS_RISCV_HPP +#ifndef CPU_RISCV_FOREIGNGLOBALS_RISCV_HPP +#define CPU_RISCV_FOREIGNGLOBALS_RISCV_HPP #include "asm/macroAssembler.hpp" #include "utilities/growableArray.hpp" @@ -49,4 +49,4 @@ struct ABIDescriptor { bool is_volatile_reg(FloatRegister reg) const; }; -#endif // CPU_RISCV_FOREIGN_GLOBALS_RISCV_HPP +#endif // CPU_RISCV_FOREIGNGLOBALS_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp index 1576f0a6dd8..bbdb9af313b 100644 --- a/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shared/cardTableBarrierSetAssembler_riscv.hpp @@ -53,4 +53,4 @@ class CardTableBarrierSetAssembler: public BarrierSetAssembler { Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); }; -#endif // #ifndef CPU_RISCV_GC_SHARED_CARDTABLEBARRIERSETASSEMBLER_RISCV_HPP +#endif // CPU_RISCV_GC_SHARED_CARDTABLEBARRIERSETASSEMBLER_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp index 890e354fd27..16532086cbf 100644 --- a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_RISCV_STUBDECLARATIONS_HPP -#define CPU_RISCV_STUBDECLARATIONS_HPP +#ifndef CPU_RISCV_STUBDECLARATIONS_RISCV_HPP +#define CPU_RISCV_STUBDECLARATIONS_RISCV_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -99,4 +99,4 @@ zero_blocks) \ -#endif // CPU_RISCV_STUBDECLARATIONS_HPP +#endif // CPU_RISCV_STUBDECLARATIONS_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/vmreg_riscv.inline.hpp b/src/hotspot/cpu/riscv/vmreg_riscv.inline.hpp index 6dce7b26f48..e6c95abbba7 100644 --- a/src/hotspot/cpu/riscv/vmreg_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/vmreg_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_RISCV_VM_VMREG_RISCV_INLINE_HPP -#define CPU_RISCV_VM_VMREG_RISCV_INLINE_HPP +#ifndef CPU_RISCV_VMREG_RISCV_INLINE_HPP +#define CPU_RISCV_VMREG_RISCV_INLINE_HPP inline VMReg Register::RegisterImpl::as_VMReg() const { return VMRegImpl::as_VMReg(encoding() * Register::max_slots_per_register); @@ -40,4 +40,4 @@ inline VMReg VectorRegister::VectorRegisterImpl::as_VMReg() const { ConcreteRegisterImpl::max_fpr); } -#endif // CPU_RISCV_VM_VMREG_RISCV_INLINE_HPP +#endif // CPU_RISCV_VMREG_RISCV_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/vmstorage_riscv.hpp b/src/hotspot/cpu/riscv/vmstorage_riscv.hpp index 1275af27ccc..105074fe9fd 100644 --- a/src/hotspot/cpu/riscv/vmstorage_riscv.hpp +++ b/src/hotspot/cpu/riscv/vmstorage_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_RISCV_VMSTORAGE_RISCV_INLINE_HPP -#define CPU_RISCV_VMSTORAGE_RISCV_INLINE_HPP +#ifndef CPU_RISCV_VMSTORAGE_RISCV_HPP +#define CPU_RISCV_VMSTORAGE_RISCV_HPP #include @@ -83,4 +83,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_RISCV_VMSTORAGE_RISCV_INLINE_HPP \ No newline at end of file +#endif // CPU_RISCV_VMSTORAGE_RISCV_HPP \ No newline at end of file diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index 450d48e1d06..725d3623128 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef CPU_S390_CONTINUATION_S390_INLINE_HPP -#define CPU_S390_CONTINUATION_S390_INLINE_HPP +#ifndef CPU_S390_CONTINUATIONFREEZETHAW_S390_INLINE_HPP +#define CPU_S390_CONTINUATIONFREEZETHAW_S390_INLINE_HPP #include "oops/stackChunkOop.inline.hpp" #include "runtime/frame.hpp" @@ -343,4 +343,4 @@ inline void ThawBase::prefetch_chunk_pd(void* start, int size) { // TODO: implement in future; } -#endif // CPU_S390_CONTINUATION_S390_INLINE_HPP +#endif // CPU_S390_CONTINUATIONFREEZETHAW_S390_INLINE_HPP diff --git a/src/hotspot/cpu/s390/foreignGlobals_s390.hpp b/src/hotspot/cpu/s390/foreignGlobals_s390.hpp index 4ff3b3e40b4..a6f79f9efd6 100644 --- a/src/hotspot/cpu/s390/foreignGlobals_s390.hpp +++ b/src/hotspot/cpu/s390/foreignGlobals_s390.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,8 +22,8 @@ * questions. */ -#ifndef CPU_S390_VM_FOREIGN_GLOBALS_S390_HPP -#define CPU_S390_VM_FOREIGN_GLOBALS_S390_HPP +#ifndef CPU_S390_FOREIGNGLOBALS_S390_HPP +#define CPU_S390_FOREIGNGLOBALS_S390_HPP struct ABIDescriptor { GrowableArray _integer_argument_registers; @@ -43,4 +44,4 @@ struct ABIDescriptor { bool is_volatile_reg(FloatRegister reg) const; }; -#endif // CPU_S390_VM_FOREIGN_GLOBALS_S390_HPP +#endif // CPU_S390_FOREIGNGLOBALS_S390_HPP diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp index d773b6ce759..1905dc093c9 100644 --- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp +++ b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp @@ -23,8 +23,8 @@ * */ -#ifndef CPU_S390_STUBDECLARATIONS_HPP -#define CPU_S390_STUBDECLARATIONS_HPP +#ifndef CPU_S390_STUBDECLARATIONS_S390_HPP +#define CPU_S390_STUBDECLARATIONS_S390_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -69,4 +69,4 @@ do_arch_blob(final, 20000) \ -#endif // CPU_S390_STUBDECLARATIONS_HPP +#endif // CPU_S390_STUBDECLARATIONS_S390_HPP diff --git a/src/hotspot/cpu/s390/vmstorage_s390.hpp b/src/hotspot/cpu/s390/vmstorage_s390.hpp index 44a2e91e920..c88c6a39dd8 100644 --- a/src/hotspot/cpu/s390/vmstorage_s390.hpp +++ b/src/hotspot/cpu/s390/vmstorage_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_S390_VMSTORAGE_S390_INLINE_HPP -#define CPU_S390_VMSTORAGE_S390_INLINE_HPP +#ifndef CPU_S390_VMSTORAGE_S390_HPP +#define CPU_S390_VMSTORAGE_S390_HPP #include @@ -105,4 +105,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_S390_VMSTORAGE_S390_INLINE_HPP +#endif // CPU_S390_VMSTORAGE_S390_HPP diff --git a/src/hotspot/cpu/x86/c2_intelJccErratum_x86.hpp b/src/hotspot/cpu/x86/c2_intelJccErratum_x86.hpp index 1644b36606e..717b1c8d23a 100644 --- a/src/hotspot/cpu/x86/c2_intelJccErratum_x86.hpp +++ b/src/hotspot/cpu/x86/c2_intelJccErratum_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef CPU_X86_INTELJCCERRATUM_X86_HPP -#define CPU_X86_INTELJCCERRATUM_X86_HPP +#ifndef CPU_X86_C2_INTELJCCERRATUM_X86_HPP +#define CPU_X86_C2_INTELJCCERRATUM_X86_HPP #include "memory/allStatic.hpp" #include "utilities/globalDefinitions.hpp" @@ -64,5 +64,4 @@ class IntelJccErratumAlignment { ~IntelJccErratumAlignment(); }; -#endif // CPU_X86_INTELJCCERRATUM_X86_HPP - +#endif // CPU_X86_C2_INTELJCCERRATUM_X86_HPP diff --git a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp index 943b8e7f968..1120c670db2 100644 --- a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -347,4 +347,4 @@ inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, c assert(f.at_absolute(frame::interpreter_frame_monitor_block_top_offset) <= frame::interpreter_frame_initial_sp_offset, ""); } -#endif // CPU_X86_CONTINUATIONFREEZE_THAW_X86_INLINE_HPP +#endif // CPU_X86_CONTINUATIONFREEZETHAW_X86_INLINE_HPP diff --git a/src/hotspot/cpu/x86/foreignGlobals_x86.hpp b/src/hotspot/cpu/x86/foreignGlobals_x86.hpp index 19999a416ea..7b3859ce661 100644 --- a/src/hotspot/cpu/x86/foreignGlobals_x86.hpp +++ b/src/hotspot/cpu/x86/foreignGlobals_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_X86_VM_FOREIGN_GLOBALS_X86_HPP -#define CPU_X86_VM_FOREIGN_GLOBALS_X86_HPP +#ifndef CPU_X86_FOREIGNGLOBALS_X86_HPP +#define CPU_X86_FOREIGNGLOBALS_X86_HPP #include "asm/macroAssembler.hpp" #include "utilities/growableArray.hpp" @@ -47,4 +47,4 @@ struct ABIDescriptor { bool is_volatile_reg(XMMRegister reg) const; }; -#endif // CPU_X86_VM_FOREIGN_GLOBALS_X86_HPP +#endif // CPU_X86_FOREIGNGLOBALS_X86_HPP diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index 28679045027..7669ef42bf4 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_X86_STUBDECLARATIONS_HPP -#define CPU_X86_STUBDECLARATIONS_HPP +#ifndef CPU_X86_STUBDECLARATIONS_X86_HPP +#define CPU_X86_STUBDECLARATIONS_X86_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -275,4 +275,4 @@ do_arch_blob(final, 33000 \ WINDOWS_ONLY(+22000) ZGC_ONLY(+20000)) \ -#endif // CPU_X86_STUBDECLARATIONS_HPP +#endif // CPU_X86_STUBDECLARATIONS_X86_HPP diff --git a/src/hotspot/cpu/x86/vmstorage_x86.hpp b/src/hotspot/cpu/x86/vmstorage_x86.hpp index af5b0e8a306..1ae3e58b2e0 100644 --- a/src/hotspot/cpu/x86/vmstorage_x86.hpp +++ b/src/hotspot/cpu/x86/vmstorage_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_X86_VMSTORAGE_X86_INLINE_HPP -#define CPU_X86_VMSTORAGE_X86_INLINE_HPP +#ifndef CPU_X86_VMSTORAGE_X86_HPP +#define CPU_X86_VMSTORAGE_X86_HPP #include @@ -97,4 +97,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { return VMStorage::invalid(); } -#endif // CPU_X86_VMSTORAGE_X86_INLINE_HPP \ No newline at end of file +#endif // CPU_X86_VMSTORAGE_X86_HPP \ No newline at end of file diff --git a/src/hotspot/cpu/zero/continuationEntry_zero.inline.hpp b/src/hotspot/cpu/zero/continuationEntry_zero.inline.hpp index c7e6aabc4db..59de6483659 100644 --- a/src/hotspot/cpu/zero/continuationEntry_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationEntry_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,4 +44,3 @@ inline void ContinuationEntry::update_register_map(RegisterMap* map) const { } #endif // CPU_ZERO_CONTINUATIONENTRY_ZERO_INLINE_HPP - diff --git a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp index f3c0ae80e0f..1da3427e77e 100644 --- a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef CPU_ZERO_CONTINUATION_ZERO_INLINE_HPP -#define CPU_ZERO_CONTINUATION_ZERO_INLINE_HPP +#ifndef CPU_ZERO_CONTINUATIONFREEZETHAW_ZERO_INLINE_HPP +#define CPU_ZERO_CONTINUATIONFREEZETHAW_ZERO_INLINE_HPP #include "oops/stackChunkOop.inline.hpp" #include "runtime/frame.hpp" @@ -123,4 +123,4 @@ inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } -#endif // CPU_ZERO_CONTINUATION_ZERO_INLINE_HPP +#endif // CPU_ZERO_CONTINUATIONFREEZETHAW_ZERO_INLINE_HPP diff --git a/src/hotspot/cpu/zero/foreignGlobals_zero.hpp b/src/hotspot/cpu/zero/foreignGlobals_zero.hpp index 0ec486b62c2..b6712da778c 100644 --- a/src/hotspot/cpu/zero/foreignGlobals_zero.hpp +++ b/src/hotspot/cpu/zero/foreignGlobals_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,9 @@ * questions. */ -#ifndef CPU_ZERO_VM_FOREIGN_GLOBALS_ZERO_HPP -#define CPU_ZERO_VM_FOREIGN_GLOBALS_ZERO_HPP +#ifndef CPU_ZERO_FOREIGNGLOBALS_ZERO_HPP +#define CPU_ZERO_FOREIGNGLOBALS_ZERO_HPP class ABIDescriptor {}; -#endif // CPU_ZERO_VM_FOREIGN_GLOBALS_ZERO_HPP +#endif // CPU_ZERO_FOREIGNGLOBALS_ZERO_HPP diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 9abe313b3a7..2aefe263046 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_ZERO_STUBDECLARATIONS_HPP -#define CPU_ZERO_STUBDECLARATIONS_HPP +#ifndef CPU_ZERO_STUBDECLARATIONS_ZERO_HPP +#define CPU_ZERO_STUBDECLARATIONS_ZERO_HPP #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ @@ -66,4 +66,4 @@ do_arch_blob(final, 0) \ -#endif // CPU_ZERO_STUBDECLARATIONS_HPP +#endif // CPU_ZERO_STUBDECLARATIONS_ZERO_HPP diff --git a/src/hotspot/cpu/zero/vmstorage_zero.hpp b/src/hotspot/cpu/zero/vmstorage_zero.hpp index 6a931b198bb..f12587ff3c6 100644 --- a/src/hotspot/cpu/zero/vmstorage_zero.hpp +++ b/src/hotspot/cpu/zero/vmstorage_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef CPU_ZERO_VMSTORAGE_ZERO_INLINE_HPP -#define CPU_ZERO_VMSTORAGE_ZERO_INLINE_HPP +#ifndef CPU_ZERO_VMSTORAGE_ZERO_HPP +#define CPU_ZERO_VMSTORAGE_ZERO_HPP #include @@ -50,4 +50,4 @@ inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { } -#endif // CPU_ZERO_VMSTORAGE_ZERO_INLINE_HPP \ No newline at end of file +#endif // CPU_ZERO_VMSTORAGE_ZERO_HPP \ No newline at end of file diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp index 3761dfc5814..ff2ae1b890c 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef CPU_ZERO_CPPINTERPRETER_ZERO_HPP -#define CPU_ZERO_CPPINTERPRETER_ZERO_HPP +#ifndef CPU_ZERO_ZEROINTERPRETER_ZERO_HPP +#define CPU_ZERO_ZEROINTERPRETER_ZERO_HPP protected: // Size of interpreter code @@ -46,4 +46,4 @@ // Main loop of normal_entry static void main_loop(int recurse, TRAPS); -#endif // CPU_ZERO_CPPINTERPRETER_ZERO_HPP +#endif // CPU_ZERO_ZEROINTERPRETER_ZERO_HPP diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index adde37e1c77..5d867ae5a11 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -22,8 +22,8 @@ * */ -#ifndef CGROUP_SUBSYSTEM_LINUX_HPP -#define CGROUP_SUBSYSTEM_LINUX_HPP +#ifndef OS_LINUX_CGROUPSUBSYSTEM_LINUX_HPP +#define OS_LINUX_CGROUPSUBSYSTEM_LINUX_HPP #include "logging/log.hpp" #include "memory/allocation.hpp" @@ -382,4 +382,4 @@ class CgroupSubsystemFactory: AllStatic { static void cleanup(CgroupInfo* cg_infos); }; -#endif // CGROUP_SUBSYSTEM_LINUX_HPP +#endif // OS_LINUX_CGROUPSUBSYSTEM_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index c5df7e8efa9..2bfe76f5d72 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -23,8 +23,8 @@ * */ -#ifndef CGROUP_UTIL_LINUX_HPP -#define CGROUP_UTIL_LINUX_HPP +#ifndef OS_LINUX_CGROUPUTIL_LINUX_HPP +#define OS_LINUX_CGROUPUTIL_LINUX_HPP #include "cgroupSubsystem_linux.hpp" #include "utilities/globalDefinitions.hpp" @@ -46,4 +46,4 @@ class CgroupUtil: AllStatic { static double get_updated_cpu_limit(CgroupCpuController* c, double lowest, double upper_bound); }; -#endif // CGROUP_UTIL_LINUX_HPP +#endif // OS_LINUX_CGROUPUTIL_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 6fe0c7babf7..750fe787f7b 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -22,8 +22,8 @@ * */ -#ifndef CGROUP_V1_SUBSYSTEM_LINUX_HPP -#define CGROUP_V1_SUBSYSTEM_LINUX_HPP +#ifndef OS_LINUX_CGROUPV1SUBSYSTEM_LINUX_HPP +#define OS_LINUX_CGROUPV1SUBSYSTEM_LINUX_HPP #include "cgroupSubsystem_linux.hpp" #include "cgroupUtil_linux.hpp" @@ -228,4 +228,4 @@ class CgroupV1Subsystem: public CgroupSubsystem { }; -#endif // CGROUP_V1_SUBSYSTEM_LINUX_HPP +#endif // OS_LINUX_CGROUPV1SUBSYSTEM_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 998145c0ff9..9f17a4a2122 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -23,8 +23,8 @@ * */ -#ifndef CGROUP_V2_SUBSYSTEM_LINUX_HPP -#define CGROUP_V2_SUBSYSTEM_LINUX_HPP +#ifndef OS_LINUX_CGROUPV2SUBSYSTEM_LINUX_HPP +#define OS_LINUX_CGROUPV2SUBSYSTEM_LINUX_HPP #include "cgroupSubsystem_linux.hpp" #include "cgroupUtil_linux.hpp" @@ -180,4 +180,4 @@ class CgroupV2Subsystem: public CgroupSubsystem { CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }; }; -#endif // CGROUP_V2_SUBSYSTEM_LINUX_HPP +#endif // OS_LINUX_CGROUPV2SUBSYSTEM_LINUX_HPP diff --git a/src/hotspot/os/linux/compilerThreadTimeout_linux.hpp b/src/hotspot/os/linux/compilerThreadTimeout_linux.hpp index 7c27080eb5e..ee1197b3e60 100644 --- a/src/hotspot/os/linux/compilerThreadTimeout_linux.hpp +++ b/src/hotspot/os/linux/compilerThreadTimeout_linux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef LINUX_COMPILER_THREAD_TIMEOUT_LINUX_HPP -#define LINUX_COMPILER_THREAD_TIMEOUT_LINUX_HPP +#ifndef OS_LINUX_COMPILERTHREADTIMEOUT_LINUX_HPP +#define OS_LINUX_COMPILERTHREADTIMEOUT_LINUX_HPP #include "memory/allocation.hpp" #include "nmt/memTag.hpp" @@ -52,4 +52,4 @@ class CompilerThreadTimeoutLinux : public CHeapObj { }; }; -#endif //LINUX_COMPILER_THREAD_TIMEOUT_LINUX_HPP +#endif //OS_LINUX_COMPILERTHREADTIMEOUT_LINUX_HPP diff --git a/src/hotspot/os/windows/systemMemoryBarrier_windows.hpp b/src/hotspot/os/windows/systemMemoryBarrier_windows.hpp index dc64199da36..02413e53c11 100644 --- a/src/hotspot/os/windows/systemMemoryBarrier_windows.hpp +++ b/src/hotspot/os/windows/systemMemoryBarrier_windows.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef OS_LINUX_SYSTEMMEMORYBARRIER_WINDOWS_HPP -#define OS_LINUX_SYSTEMMEMORYBARRIER_WINDOWS_HPP +#ifndef OS_WINDOWS_SYSTEMMEMORYBARRIER_WINDOWS_HPP +#define OS_WINDOWS_SYSTEMMEMORYBARRIER_WINDOWS_HPP #include "memory/allStatic.hpp" @@ -33,4 +33,4 @@ class WindowsSystemMemoryBarrier : public AllStatic { static void emit(); }; -#endif // OS_LINUX_SYSTEMMEMORYBARRIER_WINDOWS_HPP +#endif // OS_WINDOWS_SYSTEMMEMORYBARRIER_WINDOWS_HPP diff --git a/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp b/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp index bacf907389f..f19028753fe 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -24,8 +24,8 @@ * */ -#ifndef OS_CPU_BSD_AARCH64_ICACHE_AARCH64_HPP -#define OS_CPU_BSD_AARCH64_ICACHE_AARCH64_HPP +#ifndef OS_CPU_BSD_AARCH64_ICACHE_BSD_AARCH64_HPP +#define OS_CPU_BSD_AARCH64_ICACHE_BSD_AARCH64_HPP // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially @@ -42,4 +42,4 @@ class ICache : public AbstractICache { } }; -#endif // OS_CPU_BSD_AARCH64_ICACHE_AARCH64_HPP +#endif // OS_CPU_BSD_AARCH64_ICACHE_BSD_AARCH64_HPP diff --git a/src/hotspot/os_cpu/bsd_aarch64/pauth_bsd_aarch64.inline.hpp b/src/hotspot/os_cpu/bsd_aarch64/pauth_bsd_aarch64.inline.hpp index cfe98118b13..ff61e5034a7 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/pauth_bsd_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/pauth_bsd_aarch64.inline.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2023, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -44,4 +45,3 @@ inline address pauth_authenticate_return_address(address ret_addr) { } #endif // OS_CPU_BSD_AARCH64_PAUTH_BSD_AARCH64_INLINE_HPP - diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index 5121a875701..00ffc4e2c24 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP -#define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP +#ifndef OS_CPU_LINUX_AARCH64_ICACHE_LINUX_AARCH64_HPP +#define OS_CPU_LINUX_AARCH64_ICACHE_LINUX_AARCH64_HPP #include "memory/allocation.hpp" #include "runtime/vm_version.hpp" @@ -143,4 +143,4 @@ class AArch64ICacheInvalidationContext : StackObj { #define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext -#endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP +#endif // OS_CPU_LINUX_AARCH64_ICACHE_LINUX_AARCH64_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/copy_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/copy_linux_riscv.hpp index 147cfdf3c10..9ea3fddec6b 100644 --- a/src/hotspot/os_cpu/linux_riscv/copy_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/copy_linux_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,9 +23,9 @@ * */ -#ifndef OS_CPU_LINUX_RISCV_VM_COPY_LINUX_RISCV_HPP -#define OS_CPU_LINUX_RISCV_VM_COPY_LINUX_RISCV_HPP +#ifndef OS_CPU_LINUX_RISCV_COPY_LINUX_RISCV_HPP +#define OS_CPU_LINUX_RISCV_COPY_LINUX_RISCV_HPP // Empty for build system -#endif // OS_CPU_LINUX_RISCV_VM_COPY_LINUX_RISCV_HPP +#endif // OS_CPU_LINUX_RISCV_COPY_LINUX_RISCV_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/globals_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/globals_linux_riscv.hpp index 16e4b205547..1327e32e07a 100644 --- a/src/hotspot/os_cpu/linux_riscv/globals_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/globals_linux_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_LINUX_RISCV_VM_GLOBALS_LINUX_RISCV_HPP -#define OS_CPU_LINUX_RISCV_VM_GLOBALS_LINUX_RISCV_HPP +#ifndef OS_CPU_LINUX_RISCV_GLOBALS_LINUX_RISCV_HPP +#define OS_CPU_LINUX_RISCV_GLOBALS_LINUX_RISCV_HPP // Sets the default values for platform dependent flags used by the runtime system. // (see globals.hpp) @@ -39,4 +39,4 @@ define_pd_global(uintx, JVMInvokeMethodSlack, 8192); // Used on 64 bit platforms for UseCompressedOops base address define_pd_global(uintx, HeapBaseMinAddress, 2 * G); -#endif // OS_CPU_LINUX_RISCV_VM_GLOBALS_LINUX_RISCV_HPP +#endif // OS_CPU_LINUX_RISCV_GLOBALS_LINUX_RISCV_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp index c17054e8a0c..43c3fac9a55 100644 --- a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp +++ b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP -#define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP +#ifndef OS_CPU_LINUX_RISCV_PREFETCH_LINUX_RISCV_INLINE_HPP +#define OS_CPU_LINUX_RISCV_PREFETCH_LINUX_RISCV_INLINE_HPP // Included in runtime/prefetch.inline.hpp @@ -42,4 +42,4 @@ inline void Prefetch::write(void *loc, intx interval) { } } -#endif // OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP +#endif // OS_CPU_LINUX_RISCV_PREFETCH_LINUX_RISCV_INLINE_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp index 34742c173ab..6e07d11dfa1 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_flush_icache.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP -#define OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP +#ifndef OS_CPU_LINUX_RISCV_RISCV_FLUSH_ICACHE_HPP +#define OS_CPU_LINUX_RISCV_RISCV_FLUSH_ICACHE_HPP #include "memory/allStatic.hpp" #include "runtime/vm_version.hpp" @@ -36,4 +36,4 @@ class RiscvFlushIcache: public AllStatic { static void flush(uintptr_t start, uintptr_t end); }; -#endif // OS_LINUX_RISCV_FLUSH_ICACHE_LINUX_HPP +#endif // OS_CPU_LINUX_RISCV_RISCV_FLUSH_ICACHE_HPP diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.hpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.hpp index a8d2e8f74df..1eb61ada1cb 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.hpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef OS_LINUX_RISCV_HWPROBE_LINUX_HPP -#define OS_LINUX_RISCV_HWPROBE_LINUX_HPP +#ifndef OS_CPU_LINUX_RISCV_RISCV_HWPROBE_HPP +#define OS_CPU_LINUX_RISCV_RISCV_HWPROBE_HPP #include "memory/allStatic.hpp" #include "runtime/vm_version.hpp" @@ -36,4 +36,4 @@ class RiscvHwprobe: public AllStatic { static bool probe_features(); }; -#endif // OS_LINUX_RISCV_HWPROBE_LINUX_HPP +#endif // OS_CPU_LINUX_RISCV_RISCV_HWPROBE_HPP diff --git a/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp index b12aa545079..fd0c68a7623 100644 --- a/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_WINDOWS_AARCH64_ICACHE_AARCH64_HPP -#define OS_CPU_WINDOWS_AARCH64_ICACHE_AARCH64_HPP +#ifndef OS_CPU_WINDOWS_AARCH64_ICACHE_WINDOWS_AARCH64_HPP +#define OS_CPU_WINDOWS_AARCH64_ICACHE_WINDOWS_AARCH64_HPP // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially @@ -41,4 +41,4 @@ class ICache : public AbstractICache { } }; -#endif // OS_CPU_WINDOWS_AARCH64_ICACHE_AARCH64_HPP +#endif // OS_CPU_WINDOWS_AARCH64_ICACHE_WINDOWS_AARCH64_HPP diff --git a/src/hotspot/share/cds/aotStreamedHeapLoader.hpp b/src/hotspot/share/cds/aotStreamedHeapLoader.hpp index 065129a8a7b..1872f4c9465 100644 --- a/src/hotspot/share/cds/aotStreamedHeapLoader.hpp +++ b/src/hotspot/share/cds/aotStreamedHeapLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,5 +241,6 @@ class AOTStreamedHeapLoader { static AOTMapLogger::OopDataIterator* oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end); }; -#endif // SHARE_CDS_AOTSTREAMEDHEAPLOADER_HPP #endif // INCLUDE_CDS_JAVA_HEAP + +#endif // SHARE_CDS_AOTSTREAMEDHEAPLOADER_HPP diff --git a/src/hotspot/share/ci/ciFlatArray.hpp b/src/hotspot/share/ci/ciFlatArray.hpp index d159c984434..719f37e0d05 100644 --- a/src/hotspot/share/ci/ciFlatArray.hpp +++ b/src/hotspot/share/ci/ciFlatArray.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_CI_CIFLATARRAY_HPP -#define SHARE_VM_CI_CIFLATARRAY_HPP +#ifndef SHARE_CI_CIFLATARRAY_HPP +#define SHARE_CI_CIFLATARRAY_HPP #include "ci/ciArray.hpp" #include "ci/ciClassList.hpp" @@ -53,4 +53,4 @@ class ciFlatArray : public ciArray { ciConstant null_marker_of_element_by_index(int index); }; -#endif // SHARE_VM_CI_CIFLATARRAY_HPP +#endif // SHARE_CI_CIFLATARRAY_HPP diff --git a/src/hotspot/share/ci/ciFlatArrayKlass.hpp b/src/hotspot/share/ci/ciFlatArrayKlass.hpp index 4de6adff238..e89ed621f62 100644 --- a/src/hotspot/share/ci/ciFlatArrayKlass.hpp +++ b/src/hotspot/share/ci/ciFlatArrayKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_CI_CIFLATARRAYKLASS_HPP -#define SHARE_VM_CI_CIFLATARRAYKLASS_HPP +#ifndef SHARE_CI_CIFLATARRAYKLASS_HPP +#define SHARE_CI_CIFLATARRAYKLASS_HPP #include "ci/ciObjArrayKlass.hpp" #include "ci/ciValueKlass.hpp" @@ -70,4 +70,4 @@ class ciFlatArrayKlass : public ciObjArrayKlass { } }; -#endif // SHARE_VM_CI_CIFLATARRAYKLASS_HPP +#endif // SHARE_CI_CIFLATARRAYKLASS_HPP diff --git a/src/hotspot/share/ci/ciRefArrayKlass.hpp b/src/hotspot/share/ci/ciRefArrayKlass.hpp index 6555fe8fe9a..bced5b26468 100644 --- a/src/hotspot/share/ci/ciRefArrayKlass.hpp +++ b/src/hotspot/share/ci/ciRefArrayKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_CI_CIREFARRAYKLASS_HPP -#define SHARE_VM_CI_CIREFARRAYKLASS_HPP +#ifndef SHARE_CI_CIREFARRAYKLASS_HPP +#define SHARE_CI_CIREFARRAYKLASS_HPP #include "ci/ciObjArrayKlass.hpp" @@ -46,4 +46,4 @@ class ciRefArrayKlass : public ciObjArrayKlass { ciKlass* exact_klass() override { return this; } }; -#endif // SHARE_VM_CI_CIREFARRAYKLASS_HPP +#endif // SHARE_CI_CIREFARRAYKLASS_HPP diff --git a/src/hotspot/share/ci/ciValueKlass.hpp b/src/hotspot/share/ci/ciValueKlass.hpp index 3bd9292ff4f..14085afbb7d 100644 --- a/src/hotspot/share/ci/ciValueKlass.hpp +++ b/src/hotspot/share/ci/ciValueKlass.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_CI_CIVALUEKLASS_HPP -#define SHARE_VM_CI_CIVALUEKLASS_HPP +#ifndef SHARE_CI_CIVALUEKLASS_HPP +#define SHARE_CI_CIVALUEKLASS_HPP #include "ci/ciInstanceKlass.hpp" #include "oops/valueKlass.hpp" @@ -86,4 +86,4 @@ class ciValueKlass : public ciInstanceKlass { ArrayDescription array_description_of_array_properties(const ArrayProperties&); }; -#endif // SHARE_VM_CI_CIVALUEKLASS_HPP +#endif // SHARE_CI_CIVALUEKLASS_HPP diff --git a/src/hotspot/share/code/codeCache.inline.hpp b/src/hotspot/share/code/codeCache.inline.hpp index 1f7511940d0..3582486d84b 100644 --- a/src/hotspot/share/code/codeCache.inline.hpp +++ b/src/hotspot/share/code/codeCache.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_COMPILER_CODECACHE_INLINE_HPP -#define SHARE_VM_COMPILER_CODECACHE_INLINE_HPP +#ifndef SHARE_CODE_CODECACHE_INLINE_HPP +#define SHARE_CODE_CODECACHE_INLINE_HPP #include "code/codeCache.hpp" @@ -59,4 +59,4 @@ inline int CodeCache::find_oopmap_slot_fast(void* pc) { return -1; } -#endif // SHARE_VM_COMPILER_CODECACHE_INLINE_HPP +#endif // SHARE_CODE_CODECACHE_INLINE_HPP diff --git a/src/hotspot/share/compiler/compilationMemStatInternals.inline.hpp b/src/hotspot/share/compiler/compilationMemStatInternals.inline.hpp index 94be8606ac9..d4d27f57bc3 100644 --- a/src/hotspot/share/compiler/compilationMemStatInternals.inline.hpp +++ b/src/hotspot/share/compiler/compilationMemStatInternals.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, 2025, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_COMPILATIONMEMSTATINTERNALS_INLINE_HPP -#define SHARE_COMPILATIONMEMSTATINTERNALS_INLINE_HPP +#ifndef SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_INLINE_HPP +#define SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_INLINE_HPP #include "compiler/compilationMemStatInternals.hpp" @@ -92,4 +92,4 @@ inline void FootprintTimeline::on_footprint_change(size_t cur_abs, unsigned cur_ e._live_nodes.update(cur_nodes); } -#endif // SHARE_COMPILATIONMEMSTATINTERNALS_INLINE_HPP +#endif // SHARE_COMPILER_COMPILATIONMEMSTATINTERNALS_INLINE_HPP diff --git a/src/hotspot/share/compiler/oopMap.inline.hpp b/src/hotspot/share/compiler/oopMap.inline.hpp index 1542eb2571d..d897359602a 100644 --- a/src/hotspot/share/compiler/oopMap.inline.hpp +++ b/src/hotspot/share/compiler/oopMap.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_COMPILER_OOPMAP_INLINE_HPP -#define SHARE_VM_COMPILER_OOPMAP_INLINE_HPP +#ifndef SHARE_COMPILER_OOPMAP_INLINE_HPP +#define SHARE_COMPILER_OOPMAP_INLINE_HPP #include "compiler/oopMap.hpp" @@ -145,5 +145,4 @@ void OopMapDo::oops_do(const frame *fr, con iterate_oops_do(fr, reg_map, oopmap); } -#endif // SHARE_VM_COMPILER_OOPMAP_INLINE_HPP - +#endif // SHARE_COMPILER_OOPMAP_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp index 6905419e2cc..3b0a7fab724 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp @@ -73,4 +73,3 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { }; #endif /* SHARE_GC_G1_G1CONCURRENTMARKREMARKTASKS_HPP */ - diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp index d8e658b5a64..9f94b563e70 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp @@ -251,4 +251,4 @@ class G1MonotonicArena::AllocOptions { MemTag mem_tag() const {return _mem_tag; } }; -#endif //SHARE_GC_G1_MONOTONICARENA_HPP +#endif // SHARE_GC_G1_G1MONOTONICARENA_HPP diff --git a/src/hotspot/share/gc/g1/g1NUMA.hpp b/src/hotspot/share/gc/g1/g1NUMA.hpp index 72d6702822b..0f0a1ee6362 100644 --- a/src/hotspot/share/gc/g1/g1NUMA.hpp +++ b/src/hotspot/share/gc/g1/g1NUMA.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_G1_NUMA_HPP -#define SHARE_VM_GC_G1_NUMA_HPP +#ifndef SHARE_GC_G1_G1NUMA_HPP +#define SHARE_GC_G1_G1NUMA_HPP #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1NUMAStats.hpp" @@ -146,4 +146,4 @@ class G1NodeIndexCheckClosure : public G1HeapRegionClosure { bool do_heap_region(G1HeapRegion* hr); }; -#endif // SHARE_VM_GC_G1_NUMA_HPP +#endif // SHARE_GC_G1_G1NUMA_HPP diff --git a/src/hotspot/share/gc/g1/g1NUMAStats.hpp b/src/hotspot/share/gc/g1/g1NUMAStats.hpp index 22997ea1358..51a7142eb96 100644 --- a/src/hotspot/share/gc/g1/g1NUMAStats.hpp +++ b/src/hotspot/share/gc/g1/g1NUMAStats.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_G1_NODE_TIMES_HPP -#define SHARE_VM_GC_G1_NODE_TIMES_HPP +#ifndef SHARE_GC_G1_G1NUMASTATS_HPP +#define SHARE_GC_G1_G1NUMASTATS_HPP #include "memory/allocation.hpp" @@ -116,4 +116,4 @@ class G1NUMAStats : public CHeapObj { void print_statistics(); }; -#endif // SHARE_VM_GC_G1_NODE_TIMES_HPP +#endif // SHARE_GC_G1_G1NUMASTATS_HPP diff --git a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp index 24ae30a1c70..b50acb043b7 100644 --- a/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp +++ b/src/hotspot/share/gc/g1/g1RegionsOnNodes.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_G1_G1REGIONS_HPP -#define SHARE_VM_GC_G1_G1REGIONS_HPP +#ifndef SHARE_GC_G1_G1REGIONSONNODES_HPP +#define SHARE_GC_G1_G1REGIONSONNODES_HPP #include "memory/allocation.hpp" #include "runtime/atomic.hpp" @@ -49,4 +49,4 @@ class G1RegionsOnNodes : public StackObj { uint num_regions_on_node(uint node_index) const; }; -#endif // SHARE_VM_GC_G1_G1REGIONS_HPP +#endif // SHARE_GC_G1_G1REGIONSONNODES_HPP diff --git a/src/hotspot/share/gc/serial/serialStringDedup.hpp b/src/hotspot/share/gc/serial/serialStringDedup.hpp index 76af8a6f5ab..323450e0b8d 100644 --- a/src/hotspot/share/gc/serial/serialStringDedup.hpp +++ b/src/hotspot/share/gc/serial/serialStringDedup.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,8 +22,8 @@ * questions. */ -#ifndef SHARE_GC_SERIAL_STRINGDEDUP_HPP -#define SHARE_GC_SERIAL_STRINGDEDUP_HPP +#ifndef SHARE_GC_SERIAL_SERIALSTRINGDEDUP_HPP +#define SHARE_GC_SERIAL_SERIALSTRINGDEDUP_HPP #include "memory/allStatic.hpp" #include "oops/oopsHierarchy.hpp" @@ -43,4 +44,4 @@ class SerialStringDedup : AllStatic { }; -#endif // SHARE_GC_SERIAL_STRINGDEDUP_HPP +#endif // SHARE_GC_SERIAL_SERIALSTRINGDEDUP_HPP diff --git a/src/hotspot/share/gc/serial/serialStringDedup.inline.hpp b/src/hotspot/share/gc/serial/serialStringDedup.inline.hpp index bc54f3b34b9..a0a06b84949 100644 --- a/src/hotspot/share/gc/serial/serialStringDedup.inline.hpp +++ b/src/hotspot/share/gc/serial/serialStringDedup.inline.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,8 +22,8 @@ * questions. */ -#ifndef SHARE_GC_SERIAL_STRINGDEDUP_INLINE_HPP -#define SHARE_GC_SERIAL_STRINGDEDUP_INLINE_HPP +#ifndef SHARE_GC_SERIAL_SERIALSTRINGDEDUP_INLINE_HPP +#define SHARE_GC_SERIAL_SERIALSTRINGDEDUP_INLINE_HPP #include "gc/serial/serialStringDedup.hpp" @@ -38,4 +39,4 @@ bool SerialStringDedup::is_candidate_from_evacuation(oop obj, StringDedup::is_threshold_age(obj->age())); } -#endif // SHARE_GC_SERIAL_STRINGDEDUP_INLINE_HPP +#endif // SHARE_GC_SERIAL_SERIALSTRINGDEDUP_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/fullGCForwarding.inline.hpp b/src/hotspot/share/gc/shared/fullGCForwarding.inline.hpp index ebd280a454f..2988feb4187 100644 --- a/src/hotspot/share/gc/shared/fullGCForwarding.inline.hpp +++ b/src/hotspot/share/gc/shared/fullGCForwarding.inline.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,8 +23,8 @@ * */ -#ifndef GC_SHARED_FULLGCFORWARDING_INLINE_HPP -#define GC_SHARED_FULLGCFORWARDING_INLINE_HPP +#ifndef SHARE_GC_SHARED_FULLGCFORWARDING_INLINE_HPP +#define SHARE_GC_SHARED_FULLGCFORWARDING_INLINE_HPP #include "gc/shared/fullGCForwarding.hpp" @@ -57,4 +58,4 @@ bool FullGCForwarding::is_forwarded(oop obj) { return obj->mark().is_forwarded(); } -#endif // GC_SHARED_FULLGCFORWARDING_INLINE_HPP +#endif // SHARE_GC_SHARED_FULLGCFORWARDING_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp index 6355d61edf7..d43e406718b 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.hpp +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_SHARED_PRETOUCH_HPP -#define SHARE_GC_SHARED_PRETOUCH_HPP +#ifndef SHARE_GC_SHARED_PRETOUCHTASK_HPP +#define SHARE_GC_SHARED_PRETOUCHTASK_HPP #include "gc/shared/workerThread.hpp" #include "runtime/atomic.hpp" @@ -47,4 +47,4 @@ class PretouchTask : public WorkerTask { }; -#endif // SHARE_GC_SHARED_PRETOUCH_HPP +#endif // SHARE_GC_SHARED_PRETOUCHTASK_HPP diff --git a/src/hotspot/share/gc/shared/scavengableNMethodsData.hpp b/src/hotspot/share/gc/shared/scavengableNMethodsData.hpp index 9183050bb67..278778f6452 100644 --- a/src/hotspot/share/gc/shared/scavengableNMethodsData.hpp +++ b/src/hotspot/share/gc/shared/scavengableNMethodsData.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_SHARED_SCAVENGABLENMETHODDATAS_HPP -#define SHARE_GC_SHARED_SCAVENGABLENMETHODDATAS_HPP +#ifndef SHARE_GC_SHARED_SCAVENGABLENMETHODSDATA_HPP +#define SHARE_GC_SHARED_SCAVENGABLENMETHODSDATA_HPP #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" @@ -71,4 +71,4 @@ class ScavengableNMethodsData : public CHeapObj { void set_next(nmethod *n) { set_data(from_nmethod(n) | state()); } }; -#endif // SHARE_GC_SHARED_SCAVENGABLENMETHODDATAS_HPP +#endif // SHARE_GC_SHARED_SCAVENGABLENMETHODSDATA_HPP diff --git a/src/hotspot/share/gc/shared/vmThreadCpuTimeScope.inline.hpp b/src/hotspot/share/gc/shared/vmThreadCpuTimeScope.inline.hpp index db8b96b68bf..f986faea000 100644 --- a/src/hotspot/share/gc/shared/vmThreadCpuTimeScope.inline.hpp +++ b/src/hotspot/share/gc/shared/vmThreadCpuTimeScope.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,9 @@ * */ +#ifndef SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_INLINE_HPP +#define SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_INLINE_HPP + #include "gc/shared/vmThreadCpuTimeScope.hpp" #include "gc/shared/collectedHeap.inline.hpp" @@ -56,3 +59,5 @@ inline VMThreadCPUTimeScope::~VMThreadCPUTimeScope() { CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType::vm, end); } } + +#endif // SHARE_GC_SHARED_VMTHREADCPUTIMESCOPE_INLINE_HPP \ No newline at end of file diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 574e65aa6c0..3a69a5031aa 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -123,4 +123,3 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { #endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGENERATIONALHEURISTICS_HPP - diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index b81b2fe3ee8..0183db7d1b3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP #include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/shenandoahAffiliation.hpp" @@ -183,4 +183,4 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo { virtual void record_success_concurrent(bool abbreviated); }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 91e5522de43..6080b465dc1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP -#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_HPP #include "gc/shenandoah/shenandoahHeap.hpp" #include "memory/universe.hpp" @@ -139,4 +139,4 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap { MemoryPool* _old_gen_memory_pool; }; -#endif //SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp index 8b323d6ee47..66a3f8bcac1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP #include "gc/shenandoah/shenandoahGeneration.hpp" @@ -78,5 +78,4 @@ class ShenandoahGlobalGeneration : public ShenandoahGeneration { virtual void prepare_gc() override; }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP - +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp index b489a591be7..cc2e189d26b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp @@ -138,4 +138,3 @@ class ShenandoahInPlacePromoter { }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHINPLACEPROMOTER_HPP - diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index ba069d961f7..8f41b77fdc2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" @@ -359,4 +359,4 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { }; -#endif //SHARE_VM_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP +#endif //SHARE_GC_SHENANDOAH_SHENANDOAHOLDGENERATION_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp index 64624faeb7d..3544a15c6c0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP -#define SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_INLINE_HPP #include "gc/shenandoah/shenandoahScanRemembered.hpp" @@ -434,4 +434,4 @@ void ShenandoahDirectCardMarkRememberedSet::mark_card_as_dirty(HeapWord* p) cons bp[0] = CardTable::dirty_card_val(); } -#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBERED_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.hpp index caffd2ef87b..f98980580f4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD -#define SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD_HPP #include "gc/shared/concurrentGCThread.hpp" @@ -94,4 +94,4 @@ class ShenandoahUncommitThread : public ConcurrentGCThread { }; -#endif //SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHUNCOMMITTHREAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.hpp index 9456e21f58d..b31f2795850 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCLASSUNLOAD_HPP -#define SHARE_GC_SHENANDOAH_SHENANDOAHCLASSUNLOAD_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHUNLOAD_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHUNLOAD_HPP #include "memory/allocation.hpp" @@ -37,4 +37,4 @@ class ShenandoahUnload { void finish(); }; -#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCLASSUNLOAD_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHUNLOAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp index ed0c9c68bfa..f21f98642c4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP #include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" @@ -88,4 +88,4 @@ class ShenandoahYoungGeneration : public ShenandoahGeneration { void prepare_gc() override; }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP diff --git a/src/hotspot/share/gc/z/zMarkContext.inline.hpp b/src/hotspot/share/gc/z/zMarkContext.inline.hpp index 1609f73f3a0..28cf43cb678 100644 --- a/src/hotspot/share/gc/z/zMarkContext.inline.hpp +++ b/src/hotspot/share/gc/z/zMarkContext.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,4 +59,4 @@ inline void ZMarkContext::set_nstripes(size_t nstripes) { _nstripes = nstripes; } -#endif // SHARE_GC_Z_ZMARKCACHE_INLINE_HPP +#endif // SHARE_GC_Z_ZMARKCONTEXT_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zMarkingSMR.hpp b/src/hotspot/share/gc/z/zMarkingSMR.hpp index 26670d95959..d516da1eba4 100644 --- a/src/hotspot/share/gc/z/zMarkingSMR.hpp +++ b/src/hotspot/share/gc/z/zMarkingSMR.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef SHARE_GC_Z_ZMARKSTACKALLOCATOR_HPP -#define SHARE_GC_Z_ZMARKSTACKALLOCATOR_HPP +#ifndef SHARE_GC_Z_ZMARKINGSMR_HPP +#define SHARE_GC_Z_ZMARKINGSMR_HPP #include "gc/z/zArray.hpp" #include "gc/z/zValue.hpp" @@ -50,4 +50,4 @@ class ZMarkingSMR: public CHeapObj { ZMarkStackListNode* volatile* hazard_ptr(); }; -#endif // SHARE_GC_Z_ZMARKSTACKALLOCATOR_HPP +#endif // SHARE_GC_Z_ZMARKINGSMR_HPP diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp index 65d4ebebfe7..6796db0cbe4 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_INTERPRETER_BYTECODEINTERPRETER_HPP -#define SHARE_INTERPRETER_BYTECODEINTERPRETER_HPP +#ifndef SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_HPP +#define SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_HPP #include "memory/allocation.hpp" #include "oops/method.hpp" @@ -520,4 +520,4 @@ void print(); }; // BytecodeInterpreter -#endif // SHARE_INTERPRETER_BYTECODEINTERPRETER_HPP +#endif // SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_HPP diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp index f51a26d24bf..fbfc70bc019 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP -#define SHARE_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP +#ifndef SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_INLINE_HPP +#define SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_INLINE_HPP #include "interpreter/zero/bytecodeInterpreter.hpp" @@ -43,4 +43,4 @@ #define VERIFY_OOP(o) #endif -#endif // SHARE_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP +#endif // SHARE_INTERPRETER_ZERO_BYTECODEINTERPRETER_INLINE_HPP diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp index a98a4c8caed..15dffcbbb54 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_INTERPRETER_ZEROINTERPRETER_HPP -#define SHARE_INTERPRETER_ZEROINTERPRETER_HPP +#ifndef SHARE_INTERPRETER_ZERO_ZEROINTERPRETER_HPP +#define SHARE_INTERPRETER_ZERO_ZEROINTERPRETER_HPP #include "interpreter/abstractInterpreter.hpp" #include "utilities/macros.hpp" @@ -77,4 +77,4 @@ class ZeroInterpreter: public AbstractInterpreter { #endif // ZERO -#endif // SHARE_INTERPRETER_ZEROINTERPRETER_HPP +#endif // SHARE_INTERPRETER_ZERO_ZEROINTERPRETER_HPP diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp index d8dbd706905..ba1cdf319be 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_INTERPRETER_CPPINTERPRETERGENERATOR_HPP -#define SHARE_INTERPRETER_CPPINTERPRETERGENERATOR_HPP +#ifndef SHARE_INTERPRETER_ZERO_ZEROINTERPRETERGENERATOR_HPP +#define SHARE_INTERPRETER_ZERO_ZEROINTERPRETERGENERATOR_HPP // This file contains the platform-independent parts // of the Zero interpreter generator. @@ -69,4 +69,4 @@ class ZeroInterpreterGenerator: public AbstractInterpreterGenerator { } }; -#endif // SHARE_INTERPRETER_CPPINTERPRETERGENERATOR_HPP +#endif // SHARE_INTERPRETER_ZERO_ZEROINTERPRETERGENERATOR_HPP diff --git a/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp index 9a123a91629..036f640aad7 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/jfrbitset.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,12 @@ * */ -#ifndef SHARE_JFR_LEAKPROFILER_JFRBITSET_HPP -#define SHARE_JFR_LEAKPROFILER_JFRBITSET_HPP +#ifndef SHARE_JFR_LEAKPROFILER_CHAINS_JFRBITSET_HPP +#define SHARE_JFR_LEAKPROFILER_CHAINS_JFRBITSET_HPP #include "nmt/memTag.hpp" #include "utilities/objectBitSet.inline.hpp" typedef ObjectBitSet JFRBitSet; -#endif // SHARE_JFR_LEAKPROFILER_JFRBITSET_HPP +#endif // SHARE_JFR_LEAKPROFILER_CHAINS_JFRBITSET_HPP diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp index 1eb876f4315..6b537699546 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBARRIER_INLINE_HPP -#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBARRIER_INLINE_HPP +#ifndef SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_INLINE_HPP +#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_INLINE_HPP #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.hpp" @@ -215,4 +215,4 @@ inline traceid JfrTraceIdLoadBarrier::load_leakp_previous_epoch(const Klass* kla return (METHOD_ID(klass, method)); } -#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDBARRIER_INLINE_HPP +#endif // SHARE_JFR_RECORDER_CHECKPOINT_TYPES_TRACEID_JFRTRACEIDLOADBARRIER_INLINE_HPP diff --git a/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp b/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp index 91b42948181..e583aad077d 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrChunk.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP -#define SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP +#ifndef SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNK_HPP +#define SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNK_HPP #include "jfr/utilities/jfrAllocation.hpp" @@ -93,4 +93,4 @@ class JfrChunk : public JfrCHeapObj { u1 next_generation() const; }; -#endif // SHARE_VM_JFR_RECORDER_REPOSITORY_JFRRCHUNK_HPP +#endif // SHARE_JFR_RECORDER_REPOSITORY_JFRCHUNK_HPP diff --git a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp index cc75d974acb..2035cbbd3ca 100644 --- a/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp +++ b/src/hotspot/share/jfr/recorder/stacktrace/jfrStackFilterRegistry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP -#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP +#ifndef SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTERREGISTRY_HPP +#define SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTERREGISTRY_HPP #include "jfr/utilities/jfrAllocation.hpp" #include "jni.h" @@ -40,4 +40,4 @@ class JfrStackFilterRegistry : AllStatic { static const JfrStackFilter* lookup(int64_t id); }; -#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTCKFILTERREGISTRY_HPP +#endif // SHARE_JFR_RECORDER_STACKTRACE_JFRSTACKFILTERREGISTRY_HPP diff --git a/src/hotspot/share/jfr/recorder/storage/jfrFullStorage.inline.hpp b/src/hotspot/share/jfr/recorder/storage/jfrFullStorage.inline.hpp index 620c68cd5cc..875899225f1 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrFullStorage.inline.hpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrFullStorage.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEFULLLIST_INLINE_HPP -#define SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEFULLLIST_INLINE_HPP +#ifndef SHARE_JFR_RECORDER_STORAGE_JFRFULLSTORAGE_INLINE_HPP +#define SHARE_JFR_RECORDER_STORAGE_JFRFULLSTORAGE_INLINE_HPP #include "jfr/recorder/storage/jfrFullStorage.hpp" @@ -122,4 +122,4 @@ void JfrFullStorage::iterate(Callback& cb) { _queue->iterate(cb); } -#endif // SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEFULLLIST_INLINE_HPP +#endif // SHARE_JFR_RECORDER_STORAGE_JFRFULLSTORAGE_INLINE_HPP diff --git a/src/hotspot/share/jfr/support/jfrResolution.hpp b/src/hotspot/share/jfr/support/jfrResolution.hpp index 740ba01dd35..236ff8362f6 100644 --- a/src/hotspot/share/jfr/support/jfrResolution.hpp +++ b/src/hotspot/share/jfr/support/jfrResolution.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_SUPPORT_JFRRESOLUTION_HPP -#define SHARE_SUPPORT_JFRRESOLUTION_HPP +#ifndef SHARE_JFR_SUPPORT_JFRRESOLUTION_HPP +#define SHARE_JFR_SUPPORT_JFRRESOLUTION_HPP #include "memory/allocation.hpp" #include "utilities/exceptions.hpp" @@ -43,4 +43,4 @@ class JfrResolution : AllStatic { static void on_backpatching(const Method* callee_method, JavaThread* jt); }; -#endif // SHARE_SUPPORT_JFRRESOLUTION_HPP +#endif // SHARE_JFR_SUPPORT_JFRRESOLUTION_HPP diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp index 593c40be899..e9712e616b6 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_JFR_SUPPORT_METHODTRACER_JFRFILTERCLASSCLOSURE_HPP -#define SHARE_JFR_SUPPORT_METHODTRACER_JFRFILTERCLASSCLOSURE_HPP +#ifndef SHARE_JFR_SUPPORT_METHODTRACER_JFRCLASSFILTERCLOSURE_HPP +#define SHARE_JFR_SUPPORT_METHODTRACER_JFRCLASSFILTERCLOSURE_HPP #include "jfr/support/methodtracer/jfrInstrumentedClass.hpp" #include "jfr/utilities/jfrRelation.hpp" @@ -86,4 +86,4 @@ class JfrFilterClassClosure : public KlassClosure { bool do_entry(const traceid& id, const InstanceKlass*& ik); }; -#endif // SHARE_JFR_SUPPORT_METHODTRACER_JFRFILTERCLASSCLOSURE_HPP +#endif // SHARE_JFR_SUPPORT_METHODTRACER_JFRCLASSFILTERCLOSURE_HPP diff --git a/src/hotspot/share/jfr/utilities/jfrConcurrentQueue.hpp b/src/hotspot/share/jfr/utilities/jfrConcurrentQueue.hpp index faf789c6d96..bd0cf93220d 100644 --- a/src/hotspot/share/jfr/utilities/jfrConcurrentQueue.hpp +++ b/src/hotspot/share/jfr/utilities/jfrConcurrentQueue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,4 +66,3 @@ class JfrConcurrentQueue : public AllocPolicy { }; #endif // SHARE_JFR_UTILITIES_JFRCONCURRENTQUEUE_HPP - diff --git a/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp index 08658d13803..dd1e5c684bd 100644 --- a/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp +++ b/src/hotspot/share/jfr/utilities/jfrThreadIterator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP -#define SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP +#ifndef SHARE_JFR_UTILITIES_JFRTHREADITERATOR_HPP +#define SHARE_JFR_UTILITIES_JFRTHREADITERATOR_HPP #include "memory/allocation.hpp" #include "runtime/javaThread.hpp" @@ -75,4 +75,4 @@ class JfrNonJavaThreadIteratorAdapter { typedef JfrThreadIterator JfrJavaThreadIterator; typedef JfrThreadIterator JfrNonJavaThreadIterator; -#endif // SHARE_VM_JFR_UTILITIES_JFRTHREADITERATOR_HPP +#endif // SHARE_JFR_UTILITIES_JFRTHREADITERATOR_HPP diff --git a/src/hotspot/share/memory/metaspace/counters.hpp b/src/hotspot/share/memory/metaspace/counters.hpp index 66b1ab7f40d..7f4d8de10e9 100644 --- a/src/hotspot/share/memory/metaspace/counters.hpp +++ b/src/hotspot/share/memory/metaspace/counters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -168,4 +168,3 @@ typedef AbstractMemoryRangeCounter MemRangeCounter; } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_COUNTERS_HPP - diff --git a/src/hotspot/share/memory/metaspace/metaspaceArena.hpp b/src/hotspot/share/memory/metaspace/metaspaceArena.hpp index 75d1ec5318c..36b904437b3 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceArena.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceArena.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -173,4 +173,3 @@ class MetaspaceArena : public CHeapObj { } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_METASPACEARENA_HPP - diff --git a/src/hotspot/share/memory/metaspace/metaspaceContext.hpp b/src/hotspot/share/memory/metaspace/metaspaceContext.hpp index 6601810ce8d..b9f0a574b01 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceContext.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceContext.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -114,4 +114,3 @@ class MetaspaceContext : public CHeapObj { } // end namespace #endif // SHARE_MEMORY_METASPACE_METASPACECONTEXT_HPP - diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp index d9dd48ead17..5dddcea0e1e 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -166,4 +166,3 @@ struct ClmsStats { } // namespace metaspace #endif // SHARE_MEMORY_METASPACE_METASPACESTATISTICS_HPP - diff --git a/src/hotspot/share/nmt/mallocLimit.hpp b/src/hotspot/share/nmt/mallocLimit.hpp index d41eaabea46..4d3a56c0208 100644 --- a/src/hotspot/share/nmt/mallocLimit.hpp +++ b/src/hotspot/share/nmt/mallocLimit.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2023 SAP SE. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_SERVICES_MALLOCLIMIT_HPP -#define SHARE_SERVICES_MALLOCLIMIT_HPP +#ifndef SHARE_NMT_MALLOCLIMIT_HPP +#define SHARE_NMT_MALLOCLIMIT_HPP #include "memory/allStatic.hpp" #include "nmt/memTag.hpp" @@ -78,4 +78,4 @@ class MallocLimitHandler : public AllStatic { static bool have_limit() { return _have_limit; } }; -#endif // SHARE_SERVICES_MALLOCLIMIT_HPP +#endif // SHARE_NMT_MALLOCLIMIT_HPP diff --git a/src/hotspot/share/nmt/memMapPrinter.hpp b/src/hotspot/share/nmt/memMapPrinter.hpp index 34897a0c75c..968b9df6a2c 100644 --- a/src/hotspot/share/nmt/memMapPrinter.hpp +++ b/src/hotspot/share/nmt/memMapPrinter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_SERVICES_MEMMAPPRINTER_HPP -#define SHARE_SERVICES_MEMMAPPRINTER_HPP +#ifndef SHARE_NMT_MEMMAPPRINTER_HPP +#define SHARE_NMT_MEMMAPPRINTER_HPP #include "memory/allStatic.hpp" #include "nmt/memTag.hpp" @@ -53,4 +53,4 @@ class MemMapPrinter : public AllStatic { #endif // LINUX -#endif // SHARE_SERVICES_MEMMAPPRINTER_HPP +#endif // SHARE_NMT_MEMMAPPRINTER_HPP diff --git a/src/hotspot/share/nmt/memTag.hpp b/src/hotspot/share/nmt/memTag.hpp index 0f70b7ccc12..8fcb8a3f2ea 100644 --- a/src/hotspot/share/nmt/memTag.hpp +++ b/src/hotspot/share/nmt/memTag.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_NMT_MEM_TAG_HPP -#define SHARE_NMT_MEM_TAG_HPP +#ifndef SHARE_NMT_MEMTAG_HPP +#define SHARE_NMT_MEMTAG_HPP #include "utilities/globalDefinitions.hpp" @@ -79,4 +79,4 @@ MEMORY_TAG_DO(MEMORY_TAG_SHORTNAME) // Make an int version of the sentinel end value. constexpr int mt_number_of_tags = static_cast(MemTag::mt_number_of_tags); -#endif // SHARE_NMT_MEM_TAG_HPP +#endif // SHARE_NMT_MEMTAG_HPP diff --git a/src/hotspot/share/nmt/nmtPreInit.hpp b/src/hotspot/share/nmt/nmtPreInit.hpp index d85dde816a3..9c5968a7b78 100644 --- a/src/hotspot/share/nmt/nmtPreInit.hpp +++ b/src/hotspot/share/nmt/nmtPreInit.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023 SAP SE. All rights reserved. - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_NMT_NMT_PREINIT_HPP -#define SHARE_NMT_NMT_PREINIT_HPP +#ifndef SHARE_NMT_NMTPREINIT_HPP +#define SHARE_NMT_NMTPREINIT_HPP #include "memory/allStatic.hpp" #include "nmt/memTracker.hpp" @@ -377,5 +377,4 @@ class NMTPreInit : public AllStatic { DEBUG_ONLY(static void verify();) }; -#endif // SHARE_NMT_NMT_PREINIT_HPP - +#endif // SHARE_NMT_NMTPREINIT_HPP diff --git a/src/hotspot/share/nmt/regionsTree.hpp b/src/hotspot/share/nmt/regionsTree.hpp index 4b27423db8c..e93a5c4d62f 100644 --- a/src/hotspot/share/nmt/regionsTree.hpp +++ b/src/hotspot/share/nmt/regionsTree.hpp @@ -21,8 +21,9 @@ * questions. * */ -#ifndef NMT_REGIONSTREE_HPP -#define NMT_REGIONSTREE_HPP + +#ifndef SHARE_NMT_REGIONSTREE_HPP +#define SHARE_NMT_REGIONSTREE_HPP #include "logging/log.hpp" #include "nmt/nmtCommon.hpp" @@ -109,4 +110,4 @@ class RegionsTree : public VMATree { size_t committed_size(const VirtualMemoryRegion& rgn); }; -#endif // NMT_REGIONSTREE_HPP +#endif // SHARE_NMT_REGIONSTREE_HPP diff --git a/src/hotspot/share/nmt/threadStackTracker.hpp b/src/hotspot/share/nmt/threadStackTracker.hpp index 0be7f528264..03009fd17d7 100644 --- a/src/hotspot/share/nmt/threadStackTracker.hpp +++ b/src/hotspot/share/nmt/threadStackTracker.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2019, 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * 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,4 +40,3 @@ class ThreadStackTracker : AllStatic { }; #endif // SHARE_NMT_THREADSTACKTRACKER_HPP - diff --git a/src/hotspot/share/oops/flatArrayKlass.hpp b/src/hotspot/share/oops/flatArrayKlass.hpp index d3191a9c55c..f9626001910 100644 --- a/src/hotspot/share/oops/flatArrayKlass.hpp +++ b/src/hotspot/share/oops/flatArrayKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_FLATARRAYKLASS_HPP -#define SHARE_VM_OOPS_FLATARRAYKLASS_HPP +#ifndef SHARE_OOPS_FLATARRAYKLASS_HPP +#define SHARE_OOPS_FLATARRAYKLASS_HPP #include "classfile/classLoaderData.hpp" #include "oops/arrayKlass.hpp" @@ -153,4 +153,4 @@ class FlatArrayKlass : public ObjArrayKlass { void oop_verify_on(oop obj, outputStream* st) override; }; -#endif +#endif // SHARE_OOPS_FLATARRAYKLASS_HPP diff --git a/src/hotspot/share/oops/flatArrayKlass.inline.hpp b/src/hotspot/share/oops/flatArrayKlass.inline.hpp index 674192e4bc9..e48526c5005 100644 --- a/src/hotspot/share/oops/flatArrayKlass.inline.hpp +++ b/src/hotspot/share/oops/flatArrayKlass.inline.hpp @@ -21,8 +21,9 @@ * questions. * */ -#ifndef SHARE_VM_OOPS_FLATARRAYKLASS_INLINE_HPP -#define SHARE_VM_OOPS_FLATARRAYKLASS_INLINE_HPP + +#ifndef SHARE_OOPS_FLATARRAYKLASS_INLINE_HPP +#define SHARE_OOPS_FLATARRAYKLASS_INLINE_HPP #include "oops/flatArrayKlass.hpp" @@ -130,4 +131,4 @@ void FlatArrayKlass::oop_oop_iterate_elements_range(flatArrayOop a, OopClosureTy } } -#endif // SHARE_VM_OOPS_FLATARRAYKLASS_INLINE_HPP +#endif // SHARE_OOPS_FLATARRAYKLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/flatArrayOop.hpp b/src/hotspot/share/oops/flatArrayOop.hpp index 69957f27391..6349539fe55 100644 --- a/src/hotspot/share/oops/flatArrayOop.hpp +++ b/src/hotspot/share/oops/flatArrayOop.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_FLATARRAYOOP_HPP -#define SHARE_VM_OOPS_FLATARRAYOOP_HPP +#ifndef SHARE_OOPS_FLATARRAYOOP_HPP +#define SHARE_OOPS_FLATARRAYOOP_HPP #include "oops/klass.hpp" #include "oops/objArrayOop.hpp" @@ -87,4 +87,4 @@ class flatArrayOopDesc : public objArrayOopDesc { // See similar requirement for oopDesc. static_assert(std::is_trivially_default_constructible::value); -#endif // SHARE_VM_OOPS_FLATARRAYOOP_HPP +#endif // SHARE_OOPS_FLATARRAYOOP_HPP diff --git a/src/hotspot/share/oops/flatArrayOop.inline.hpp b/src/hotspot/share/oops/flatArrayOop.inline.hpp index 3006efd1bf2..e4233999c4b 100644 --- a/src/hotspot/share/oops/flatArrayOop.inline.hpp +++ b/src/hotspot/share/oops/flatArrayOop.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_FLATARRAYOOP_INLINE_HPP -#define SHARE_VM_OOPS_FLATARRAYOOP_INLINE_HPP +#ifndef SHARE_OOPS_FLATARRAYOOP_INLINE_HPP +#define SHARE_OOPS_FLATARRAYOOP_INLINE_HPP #include "oops/flatArrayOop.hpp" @@ -128,4 +128,4 @@ void flatArrayOopDesc::oop_iterate_elements_range(OopClosureType* blk, int start } } -#endif // SHARE_VM_OOPS_FLATARRAYOOP_INLINE_HPP +#endif // SHARE_OOPS_FLATARRAYOOP_INLINE_HPP diff --git a/src/hotspot/share/oops/valueKlass.hpp b/src/hotspot/share/oops/valueKlass.hpp index 3d6c0049f14..67585e30c84 100644 --- a/src/hotspot/share/oops/valueKlass.hpp +++ b/src/hotspot/share/oops/valueKlass.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_VALUEKLASS_HPP -#define SHARE_VM_OOPS_VALUEKLASS_HPP +#ifndef SHARE_OOPS_VALUEKLASS_HPP +#define SHARE_OOPS_VALUEKLASS_HPP #include "oops/instanceKlass.hpp" #include "oops/layoutKind.hpp" @@ -426,4 +426,4 @@ class ValueKlass: public InstanceKlass { void oop_verify_on(oop obj, outputStream* st) override; }; -#endif // SHARE_VM_OOPS_VALUEKLASS_HPP +#endif // SHARE_OOPS_VALUEKLASS_HPP diff --git a/src/hotspot/share/oops/valueKlass.inline.hpp b/src/hotspot/share/oops/valueKlass.inline.hpp index 201c4ce2f8f..37f2da3c3cb 100644 --- a/src/hotspot/share/oops/valueKlass.inline.hpp +++ b/src/hotspot/share/oops/valueKlass.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,9 @@ * questions. * */ -#ifndef SHARE_VM_OOPS_VALUEKLASS_INLINE_HPP -#define SHARE_VM_OOPS_VALUEKLASS_INLINE_HPP + +#ifndef SHARE_OOPS_VALUEKLASS_INLINE_HPP +#define SHARE_OOPS_VALUEKLASS_INLINE_HPP #include "oops/valueKlass.hpp" @@ -169,4 +170,4 @@ inline void ValueKlass::oop_iterate_value_payload_bounded(address payload, OopCl } } -#endif // SHARE_VM_OOPS_VALUEKLASS_INLINE_HPP +#endif // SHARE_OOPS_VALUEKLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/valuePayload.hpp b/src/hotspot/share/oops/valuePayload.hpp index 63673f12efe..09fb3702226 100644 --- a/src/hotspot/share/oops/valuePayload.hpp +++ b/src/hotspot/share/oops/valuePayload.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_VALUEPAYLOAD_HPP -#define SHARE_VM_OOPS_VALUEPAYLOAD_HPP +#ifndef SHARE_OOPS_VALUEPAYLOAD_HPP +#define SHARE_OOPS_VALUEPAYLOAD_HPP #include "oops/instanceKlass.hpp" #include "oops/layoutKind.hpp" @@ -413,4 +413,4 @@ class FlatArrayPayload::OopHandle : public FlatValuePayload::OopHandle { inline flatArrayOop container() const; }; -#endif // SHARE_VM_OOPS_VALUEPAYLOAD_HPP +#endif // SHARE_OOPS_VALUEPAYLOAD_HPP diff --git a/src/hotspot/share/oops/valuePayload.inline.hpp b/src/hotspot/share/oops/valuePayload.inline.hpp index 07fa1ba4238..74c3090add7 100644 --- a/src/hotspot/share/oops/valuePayload.inline.hpp +++ b/src/hotspot/share/oops/valuePayload.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OOPS_VALUEPAYLOAD_INLINE_HPP -#define SHARE_VM_OOPS_VALUEPAYLOAD_INLINE_HPP +#ifndef SHARE_OOPS_VALUEPAYLOAD_INLINE_HPP +#define SHARE_OOPS_VALUEPAYLOAD_INLINE_HPP #include "oops/valuePayload.hpp" @@ -883,4 +883,4 @@ inline FlatArrayPayload::OopHandle FlatArrayPayload::make_oop_handle(OopStorage* return OopHandle(*this, storage); } -#endif // SHARE_VM_OOPS_VALUEPAYLOAD_INLINE_HPP +#endif // SHARE_OOPS_VALUEPAYLOAD_INLINE_HPP diff --git a/src/hotspot/share/opto/printinlining.hpp b/src/hotspot/share/opto/printinlining.hpp index e45f5075a8d..37bbba3b304 100644 --- a/src/hotspot/share/opto/printinlining.hpp +++ b/src/hotspot/share/opto/printinlining.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef PRINTINLINING_HPP -#define PRINTINLINING_HPP +#ifndef SHARE_OPTO_PRINTINLINING_HPP +#define SHARE_OPTO_PRINTINLINING_HPP #include "memory/allocation.hpp" #include "utilities/growableArray.hpp" @@ -152,4 +152,4 @@ class InlinePrinterSuspendScope : public StackObj { ~InlinePrinterSuspendScope() { _printer->resume(); } }; -#endif // PRINTINLINING_HPP +#endif // SHARE_OPTO_PRINTINLINING_HPP diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp index 3c5d31cc9c2..b3a47b5ba23 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,8 @@ #include "opto/superword.hpp" #include "opto/vtransform.hpp" -#ifndef SHARE_OPTO_SUPERWORD_VTRANSFORM_BUILDER_HPP -#define SHARE_OPTO_SUPERWORD_VTRANSFORM_BUILDER_HPP +#ifndef SHARE_OPTO_SUPERWORDVTRANSFORMBUILDER_HPP +#define SHARE_OPTO_SUPERWORDVTRANSFORMBUILDER_HPP // Facility class that builds a VTransform from a SuperWord PackSet. class SuperWordVTransformBuilder : public StackObj { @@ -86,4 +86,4 @@ class SuperWordVTransformBuilder : public StackObj { LoadNode::ControlDependency load_control_dependency(const Node_List* pack) const; }; -#endif // SHARE_OPTO_SUPERWORD_VTRANSFORM_BUILDER_HPP +#endif // SHARE_OPTO_SUPERWORDVTRANSFORMBUILDER_HPP diff --git a/src/hotspot/share/opto/valuetypenode.hpp b/src/hotspot/share/opto/valuetypenode.hpp index 5bc2010e7da..b70a335c94a 100644 --- a/src/hotspot/share/opto/valuetypenode.hpp +++ b/src/hotspot/share/opto/valuetypenode.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_OPTO_VALUETYPENODE_HPP -#define SHARE_VM_OPTO_VALUETYPENODE_HPP +#ifndef SHARE_OPTO_VALUETYPENODE_HPP +#define SHARE_OPTO_VALUETYPENODE_HPP #include "ci/ciValueKlass.hpp" #include "oops/accessDecorators.hpp" @@ -261,4 +261,4 @@ class StoreFlatNode final : public SafePointNode { static Node* set_payload_value(PhaseIterGVN& igvn, BasicType payload_bt, Node* payload, BasicType val_bt, Node* value, int offset); }; -#endif // SHARE_VM_OPTO_VALUETYPENODE_HPP +#endif // SHARE_OPTO_VALUETYPENODE_HPP diff --git a/src/hotspot/share/prims/downcallLinker.hpp b/src/hotspot/share/prims/downcallLinker.hpp index 9ab86a72de4..fce0fc3e549 100644 --- a/src/hotspot/share/prims/downcallLinker.hpp +++ b/src/hotspot/share/prims/downcallLinker.hpp @@ -21,8 +21,8 @@ * questions. */ -#ifndef SHARE_VM_PRIMS_DOWNCALLLINKER_HPP -#define SHARE_VM_PRIMS_DOWNCALLLINKER_HPP +#ifndef SHARE_PRIMS_DOWNCALLLINKER_HPP +#define SHARE_PRIMS_DOWNCALLLINKER_HPP #include "prims/foreignGlobals.hpp" #include "runtime/stubCodeGenerator.hpp" @@ -103,4 +103,4 @@ class DowncallLinker: AllStatic { }; }; -#endif // SHARE_VM_PRIMS_DOWNCALLLINKER_HPP +#endif // SHARE_PRIMS_DOWNCALLLINKER_HPP diff --git a/src/hotspot/share/prims/foreignGlobals.hpp b/src/hotspot/share/prims/foreignGlobals.hpp index d00dbd3ce0e..b86180b1426 100644 --- a/src/hotspot/share/prims/foreignGlobals.hpp +++ b/src/hotspot/share/prims/foreignGlobals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef SHARE_PRIMS_FOREIGN_GLOBALS -#define SHARE_PRIMS_FOREIGN_GLOBALS +#ifndef SHARE_PRIMS_FOREIGNGLOBALS_HPP +#define SHARE_PRIMS_FOREIGNGLOBALS_HPP #include "code/vmreg.hpp" #include "oops/oopsHierarchy.hpp" @@ -150,4 +150,4 @@ class ArgumentShuffle { void pd_generate(MacroAssembler* masm, VMStorage tmp, int in_stk_bias, int out_stk_bias) const; }; -#endif // SHARE_PRIMS_FOREIGN_GLOBALS +#endif // SHARE_PRIMS_FOREIGNGLOBALS_HPP diff --git a/src/hotspot/share/prims/foreignGlobals.inline.hpp b/src/hotspot/share/prims/foreignGlobals.inline.hpp index 66de575eed1..da28b153f1d 100644 --- a/src/hotspot/share/prims/foreignGlobals.inline.hpp +++ b/src/hotspot/share/prims/foreignGlobals.inline.hpp @@ -21,8 +21,8 @@ * questions. */ -#ifndef SHARE_PRIMS_FOREIGN_GLOBALS_INLINE_HPP -#define SHARE_PRIMS_FOREIGN_GLOBALS_INLINE_HPP +#ifndef SHARE_PRIMS_FOREIGNGLOBALS_INLINE_HPP +#define SHARE_PRIMS_FOREIGNGLOBALS_INLINE_HPP #include "prims/foreignGlobals.hpp" @@ -46,4 +46,4 @@ inline const char* null_safe_string(const char* str) { return str == nullptr ? "null" : str; } -#endif // SHARE_PRIMS_FOREIGN_GLOBALS_INLINE_HPP +#endif // SHARE_PRIMS_FOREIGNGLOBALS_INLINE_HPP diff --git a/src/hotspot/share/prims/jvmtiTagMapTable.hpp b/src/hotspot/share/prims/jvmtiTagMapTable.hpp index 6f39cb3c193..07ed4cfc443 100644 --- a/src/hotspot/share/prims/jvmtiTagMapTable.hpp +++ b/src/hotspot/share/prims/jvmtiTagMapTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_PRIMS_TAGMAPTABLE_HPP -#define SHARE_VM_PRIMS_TAGMAPTABLE_HPP +#ifndef SHARE_PRIMS_JVMTITAGMAPTABLE_HPP +#define SHARE_PRIMS_JVMTITAGMAPTABLE_HPP #include "gc/shared/collectedHeap.hpp" #include "memory/allocation.hpp" @@ -226,4 +226,4 @@ class JvmtiFlatTagMapTable: public CHeapObj { void clear(); }; -#endif // SHARE_VM_PRIMS_TAGMAPTABLE_HPP +#endif // SHARE_PRIMS_JVMTITAGMAPTABLE_HPP diff --git a/src/hotspot/share/prims/scopedMemoryAccess.hpp b/src/hotspot/share/prims/scopedMemoryAccess.hpp index 0b99f3a51ab..6b0513096c3 100644 --- a/src/hotspot/share/prims/scopedMemoryAccess.hpp +++ b/src/hotspot/share/prims/scopedMemoryAccess.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ */ -#ifndef SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP -#define SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP +#ifndef SHARE_PRIMS_SCOPEDMEMORYACCESS_HPP +#define SHARE_PRIMS_SCOPEDMEMORYACCESS_HPP #include "jni.h" @@ -32,4 +32,4 @@ extern "C" { void JNICALL JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass); } -#endif // SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP +#endif // SHARE_PRIMS_SCOPEDMEMORYACCESS_HPP diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index 65699e0f1ff..e77450db017 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,8 @@ * questions. */ -#ifndef SHARE_VM_PRIMS_UPCALLLINKER_HPP -#define SHARE_VM_PRIMS_UPCALLLINKER_HPP +#ifndef SHARE_PRIMS_UPCALLLINKER_HPP +#define SHARE_PRIMS_UPCALLLINKER_HPP #include "asm/codeBuffer.hpp" #include "code/codeBlob.hpp" @@ -47,4 +47,4 @@ class UpcallLinker { static void handle_uncaught_exception(oopDesc* exception); }; -#endif // SHARE_VM_PRIMS_UPCALLLINKER_HPP +#endif // SHARE_PRIMS_UPCALLLINKER_HPP diff --git a/src/hotspot/share/runtime/continuation.hpp b/src/hotspot/share/runtime/continuation.hpp index c54a56ce7eb..abad3694c7d 100644 --- a/src/hotspot/share/runtime/continuation.hpp +++ b/src/hotspot/share/runtime/continuation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATION_HPP -#define SHARE_VM_RUNTIME_CONTINUATION_HPP +#ifndef SHARE_RUNTIME_CONTINUATION_HPP +#define SHARE_RUNTIME_CONTINUATION_HPP #include "jni.h" #include "memory/allStatic.hpp" @@ -145,4 +145,4 @@ class Continuation : AllStatic { void CONT_RegisterNativeMethods(JNIEnv *env, jclass cls); -#endif // SHARE_VM_RUNTIME_CONTINUATION_HPP +#endif // SHARE_RUNTIME_CONTINUATION_HPP diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp index de44ca54b44..e99ec4a91cb 100644 --- a/src/hotspot/share/runtime/continuationEntry.hpp +++ b/src/hotspot/share/runtime/continuationEntry.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP -#define SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP +#ifndef SHARE_RUNTIME_CONTINUATIONENTRY_HPP +#define SHARE_RUNTIME_CONTINUATIONENTRY_HPP #include "oops/oop.hpp" #include "oops/oopsHierarchy.hpp" @@ -150,4 +150,4 @@ class ContinuationEntry { #endif }; -#endif // SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP +#endif // SHARE_RUNTIME_CONTINUATIONENTRY_HPP diff --git a/src/hotspot/share/runtime/continuationEntry.inline.hpp b/src/hotspot/share/runtime/continuationEntry.inline.hpp index 902ab120332..ad9697ccae1 100644 --- a/src/hotspot/share/runtime/continuationEntry.inline.hpp +++ b/src/hotspot/share/runtime/continuationEntry.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATIONENTRY_INLINE_HPP -#define SHARE_VM_RUNTIME_CONTINUATIONENTRY_INLINE_HPP +#ifndef SHARE_RUNTIME_CONTINUATIONENTRY_INLINE_HPP +#define SHARE_RUNTIME_CONTINUATIONENTRY_INLINE_HPP #include "runtime/continuationEntry.hpp" @@ -70,4 +70,4 @@ inline oop ContinuationEntry::scope(const JavaThread* thread) const { return Continuation::continuation_scope(cont_oop(thread)); } -#endif // SHARE_VM_RUNTIME_CONTINUATIONENTRY_INLINE_HPP +#endif // SHARE_RUNTIME_CONTINUATIONENTRY_INLINE_HPP diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index dffbf333bcc..67d5e82d25a 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATIONHELPER_HPP -#define SHARE_VM_RUNTIME_CONTINUATIONHELPER_HPP +#ifndef SHARE_RUNTIME_CONTINUATIONHELPER_HPP +#define SHARE_RUNTIME_CONTINUATIONHELPER_HPP #include "code/scopeDesc.hpp" #include "compiler/oopMap.hpp" @@ -159,4 +159,4 @@ class ContinuationHelper::StubFrame : public ContinuationHelper::NonInterpretedF static int stack_argsize(const frame& f) { return 0; } }; -#endif // SHARE_VM_RUNTIME_CONTINUATIONHELPER_HPP +#endif // SHARE_RUNTIME_CONTINUATIONHELPER_HPP diff --git a/src/hotspot/share/runtime/continuationHelper.inline.hpp b/src/hotspot/share/runtime/continuationHelper.inline.hpp index 7bbdbd4935d..bfc59e5e872 100644 --- a/src/hotspot/share/runtime/continuationHelper.inline.hpp +++ b/src/hotspot/share/runtime/continuationHelper.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATIONHELPER_INLINE_HPP -#define SHARE_VM_RUNTIME_CONTINUATIONHELPER_INLINE_HPP +#ifndef SHARE_RUNTIME_CONTINUATIONHELPER_INLINE_HPP +#define SHARE_RUNTIME_CONTINUATIONHELPER_INLINE_HPP #include "runtime/continuationHelper.hpp" @@ -220,4 +220,4 @@ inline bool ContinuationHelper::StubFrame::is_instance(const frame& f) { return !f.is_interpreted_frame() && is_stub(f.cb()); } -#endif // SHARE_VM_RUNTIME_CONTINUATIONHELPER_INLINE_HPP +#endif // SHARE_RUNTIME_CONTINUATIONHELPER_INLINE_HPP diff --git a/src/hotspot/share/runtime/continuationWrapper.inline.hpp b/src/hotspot/share/runtime/continuationWrapper.inline.hpp index 1f9226f8bd3..e89a81e40d2 100644 --- a/src/hotspot/share/runtime/continuationWrapper.inline.hpp +++ b/src/hotspot/share/runtime/continuationWrapper.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP -#define SHARE_VM_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP +#ifndef SHARE_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP +#define SHARE_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP // There is no continuationWrapper.hpp file @@ -183,4 +183,4 @@ inline stackChunkOop ContinuationWrapper::last_nonempty_chunk() const { return chunk; } -#endif // SHARE_VM_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP +#endif // SHARE_RUNTIME_CONTINUATIONWRAPPER_INLINE_HPP diff --git a/src/hotspot/share/runtime/flags/debug_globals.hpp b/src/hotspot/share/runtime/flags/debug_globals.hpp index b1da29ffa97..930315bf75f 100644 --- a/src/hotspot/share/runtime/flags/debug_globals.hpp +++ b/src/hotspot/share/runtime/flags/debug_globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_RUNTIME_DEBUG_GLOBALS_HPP -#define SHARE_RUNTIME_DEBUG_GLOBALS_HPP +#ifndef SHARE_RUNTIME_FLAGS_DEBUG_GLOBALS_HPP +#define SHARE_RUNTIME_FLAGS_DEBUG_GLOBALS_HPP #include "runtime/globals_shared.hpp" #include "utilities/macros.hpp" @@ -90,4 +90,4 @@ DECLARE_FLAGS(DEBUG_RUNTIME_FLAGS) -#endif // SHARE_RUNTIME_DEBUG_GLOBALS_HPP +#endif // SHARE_RUNTIME_FLAGS_DEBUG_GLOBALS_HPP diff --git a/src/hotspot/share/runtime/osThreadBase.hpp b/src/hotspot/share/runtime/osThreadBase.hpp index e7e09f6e53d..bfe012601a4 100644 --- a/src/hotspot/share/runtime/osThreadBase.hpp +++ b/src/hotspot/share/runtime/osThreadBase.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_RUNTIME_OSTHREAD_BASE_HPP -#define SHARE_RUNTIME_OSTHREAD_BASE_HPP +#ifndef SHARE_RUNTIME_OSTHREADBASE_HPP +#define SHARE_RUNTIME_OSTHREADBASE_HPP #include "memory/allocation.hpp" @@ -111,4 +111,4 @@ class OSThreadContendState : public StackObj { } }; -#endif // SHARE_RUNTIME_OSTHREAD_BASE_HPP +#endif // SHARE_RUNTIME_OSTHREADBASE_HPP diff --git a/src/hotspot/share/runtime/smallRegisterMap.inline.hpp b/src/hotspot/share/runtime/smallRegisterMap.inline.hpp index f3923c01c2f..852d273b38c 100644 --- a/src/hotspot/share/runtime/smallRegisterMap.inline.hpp +++ b/src/hotspot/share/runtime/smallRegisterMap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_RUNTIME_SMALLREGISTERMAP_HPP -#define SHARE_RUNTIME_SMALLREGISTERMAP_HPP +#ifndef SHARE_RUNTIME_SMALLREGISTERMAP_INLINE_HPP +#define SHARE_RUNTIME_SMALLREGISTERMAP_INLINE_HPP #include "utilities/macros.hpp" @@ -44,4 +44,4 @@ class SmallRegisterMap : AllStatic { } }; -#endif // SHARE_RUNTIME_SMALLREGISTERMAP_HPP +#endif // SHARE_RUNTIME_SMALLREGISTERMAP_INLINE_HPP diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp index bfdf39b1c3b..6e1fc058c80 100644 --- a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp +++ b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP -#define SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP +#ifndef SHARE_RUNTIME_STACKCHUNKFRAMESTREAM_INLINE_HPP +#define SHARE_RUNTIME_STACKCHUNKFRAMESTREAM_INLINE_HPP #include "runtime/stackChunkFrameStream.hpp" @@ -467,4 +467,4 @@ void StackChunkFrameStream::assert_is_interpreted_and_frame_type_mix #endif -#endif // SHARE_OOPS_STACKCHUNKFRAMESTREAM_INLINE_HPP +#endif // SHARE_RUNTIME_STACKCHUNKFRAMESTREAM_INLINE_HPP diff --git a/src/hotspot/share/runtime/stackFrameStream.hpp b/src/hotspot/share/runtime/stackFrameStream.hpp index cd259372b03..7f420afd86a 100644 --- a/src/hotspot/share/runtime/stackFrameStream.hpp +++ b/src/hotspot/share/runtime/stackFrameStream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,5 +68,3 @@ class StackFrameStream : public StackObj { }; #endif // SHARE_RUNTIME_STACKFRAMESTREAM_HPP - - diff --git a/src/hotspot/share/runtime/stackFrameStream.inline.hpp b/src/hotspot/share/runtime/stackFrameStream.inline.hpp index c1cbd64c3de..dca8940650f 100644 --- a/src/hotspot/share/runtime/stackFrameStream.inline.hpp +++ b/src/hotspot/share/runtime/stackFrameStream.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * 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,5 +34,3 @@ inline bool StackFrameStream::is_done() { } #endif // SHARE_RUNTIME_STACKFRAMESTREAM_INLINE_HPP - - diff --git a/src/hotspot/share/sanitizers/leak.hpp b/src/hotspot/share/sanitizers/leak.hpp index f3d7a23bf48..c038eaa69cb 100644 --- a/src/hotspot/share/sanitizers/leak.hpp +++ b/src/hotspot/share/sanitizers/leak.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -90,4 +91,4 @@ #define LSAN_DO_RECOVERABLE_LEAK_CHECK() ((int) 0) #endif -#endif // SHARE_SANITIZERS_ADDRESS_HPP +#endif // SHARE_SANITIZERS_LEAK_HPP diff --git a/src/hotspot/share/utilities/checkedCast.hpp b/src/hotspot/share/utilities/checkedCast.hpp index 290028f99e3..59989fef001 100644 --- a/src/hotspot/share/utilities/checkedCast.hpp +++ b/src/hotspot/share/utilities/checkedCast.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,4 +43,3 @@ constexpr T2 checked_cast(T1 thing) { } #endif // SHARE_UTILITIES_CHECKEDCAST_HPP - diff --git a/src/hotspot/share/utilities/devirtualizer.inline.hpp b/src/hotspot/share/utilities/devirtualizer.inline.hpp index 10d6e2de2cb..9bf83d0857d 100644 --- a/src/hotspot/share/utilities/devirtualizer.inline.hpp +++ b/src/hotspot/share/utilities/devirtualizer.inline.hpp @@ -179,4 +179,3 @@ inline void Devirtualizer::do_derived_oop(DerivedOopClosureType* closure, derive } #endif // SHARE_UTILITIES_DEVIRTUALIZER_INLINE_HPP - diff --git a/src/hotspot/share/utilities/parseInteger.hpp b/src/hotspot/share/utilities/parseInteger.hpp index dbc339341ae..37f4280ae31 100644 --- a/src/hotspot/share/utilities/parseInteger.hpp +++ b/src/hotspot/share/utilities/parseInteger.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_UTILITIES_PARSE_INTEGER_HPP -#define SHARE_UTILITIES_PARSE_INTEGER_HPP +#ifndef SHARE_UTILITIES_PARSEINTEGER_HPP +#define SHARE_UTILITIES_PARSEINTEGER_HPP #include "cppstdlib/cstdlib.hpp" #include "cppstdlib/limits.hpp" @@ -170,4 +170,4 @@ inline bool parse_integer(const char *s, T* result) { return rc; } -#endif // SHARE_UTILITIES_PARSE_INTEGER_HPP +#endif // SHARE_UTILITIES_PARSEINTEGER_HPP diff --git a/src/hotspot/share/utilities/resizableHashTable.hpp b/src/hotspot/share/utilities/resizableHashTable.hpp index 8e6528f6a3c..ad51caff5e1 100644 --- a/src/hotspot/share/utilities/resizableHashTable.hpp +++ b/src/hotspot/share/utilities/resizableHashTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP -#define SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP +#ifndef SHARE_UTILITIES_RESIZABLEHASHTABLE_HPP +#define SHARE_UTILITIES_RESIZABLEHASHTABLE_HPP #include "utilities/hashTable.hpp" diff --git a/src/hotspot/share/utilities/tableStatistics.hpp b/src/hotspot/share/utilities/tableStatistics.hpp index 95856114833..5b565dc6145 100644 --- a/src/hotspot/share/utilities/tableStatistics.hpp +++ b/src/hotspot/share/utilities/tableStatistics.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_UTILITIES_TABLE_STATISTICS_HPP -#define SHARE_UTILITIES_TABLE_STATISTICS_HPP +#ifndef SHARE_UTILITIES_TABLESTATISTICS_HPP +#define SHARE_UTILITIES_TABLESTATISTICS_HPP #include "memory/allocation.hpp" #include "runtime/atomic.hpp" @@ -90,4 +90,4 @@ class TableStatistics : CHeapObj { void print(outputStream* st, const char *table_name); }; -#endif // SHARE_UTILITIES_TABLE_STATISTICS_HPP +#endif // SHARE_UTILITIES_TABLESTATISTICS_HPP diff --git a/src/hotspot/share/utilities/unalignedAccess.hpp b/src/hotspot/share/utilities/unalignedAccess.hpp index dd18381e927..fd2b8924820 100644 --- a/src/hotspot/share/utilities/unalignedAccess.hpp +++ b/src/hotspot/share/utilities/unalignedAccess.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_UTILITIES_UNALIGNED_ACCESS_HPP -#define SHARE_UTILITIES_UNALIGNED_ACCESS_HPP +#ifndef SHARE_UTILITIES_UNALIGNEDACCESS_HPP +#define SHARE_UTILITIES_UNALIGNEDACCESS_HPP #include "memory/allStatic.hpp" #include "utilities/debug.hpp" @@ -172,4 +172,4 @@ struct UnalignedAccess::LoadImpl { #undef SANITIZER_HAS_UNALIGNED_ACCESS -#endif // SHARE_UTILITIES_UNALIGNED_ACCESS_HPP +#endif // SHARE_UTILITIES_UNALIGNEDACCESS_HPP diff --git a/src/hotspot/share/utilities/virtualizationSupport.hpp b/src/hotspot/share/utilities/virtualizationSupport.hpp index dc0b3a13898..f680b0793ca 100644 --- a/src/hotspot/share/utilities/virtualizationSupport.hpp +++ b/src/hotspot/share/utilities/virtualizationSupport.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -53,4 +53,3 @@ class VirtualizationSupport { }; #endif - diff --git a/src/hotspot/share/utilities/vmassert_reinstall.hpp b/src/hotspot/share/utilities/vmassert_reinstall.hpp index 32d31ac0c4f..e6b34768905 100644 --- a/src/hotspot/share/utilities/vmassert_reinstall.hpp +++ b/src/hotspot/share/utilities/vmassert_reinstall.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,4 +33,3 @@ #ifdef vmassert #define assert(p, ...) vmassert(p, __VA_ARGS__) #endif - diff --git a/src/hotspot/share/utilities/vmassert_uninstall.hpp b/src/hotspot/share/utilities/vmassert_uninstall.hpp index dd6d51633dd..439156666f5 100644 --- a/src/hotspot/share/utilities/vmassert_uninstall.hpp +++ b/src/hotspot/share/utilities/vmassert_uninstall.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * 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,4 +42,3 @@ #ifdef vmassert #undef assert #endif // vmassert - From 835df8ad50d1198baa3a098db8ca83ab3a972e5c Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Tue, 6 Oct 2026 07:32:18 +0000 Subject: [PATCH 62/98] 8393452: Remove cached null-marker offset Reviewed-by: jsjolen, aboldtch --- src/hotspot/share/ci/ciField.cpp | 4 ++-- src/hotspot/share/classfile/fieldLayoutBuilder.cpp | 2 -- src/hotspot/share/oops/instanceKlass.cpp | 14 +++++++++++--- src/hotspot/share/oops/instanceKlass.hpp | 12 ++---------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 1e33abd993f..08f6a692d1b 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -315,8 +315,8 @@ void ciField::initialize_from(fieldDescriptor* fd) { _is_flat = fd->is_flat(); _is_null_free = fd->is_null_free_value_type(); if (fd->has_null_marker()) { - ValueFieldLayoutInfo* li = field_holder->value_field_layout_info_adr(fd->index()); - _null_marker_offset = li->null_marker_offset(); + ValueKlass* vk = fd->flat_field_klass(); + _null_marker_offset = _offset + vk->null_marker_offset_in_payload(); } else { _null_marker_offset = -1; } diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index c44a72b55c5..0c44f4bbfbb 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -461,7 +461,6 @@ LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawB if (LayoutKindHelper::is_nullable_flat(block->layout_kind())) { int nm_offset = block->value_klass()->null_marker_offset() - block->value_klass()->payload_offset() + block->offset(); _field_info->adr_at(block->field_index())->set_null_marker_offset(nm_offset); - _value_field_layout_info_array->adr_at(block->field_index())->set_null_marker_offset(nm_offset); } return block; @@ -638,7 +637,6 @@ void FieldLayout::shift_fields(int shift) { if (LayoutKindHelper::is_nullable_flat(b->layout_kind())) { int new_nm_offset = _field_info->adr_at(b->field_index())->null_marker_offset() + shift; _field_info->adr_at(b->field_index())->set_null_marker_offset(new_nm_offset); - _value_field_layout_info_array->adr_at(b->field_index())->set_null_marker_offset(new_nm_offset); } } assert(b->block_kind() == LayoutRawBlock::EMPTY || b->offset() % b->alignment() == 0, "Must still be correctly aligned"); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 928bc034e27..a5e6f2dee3f 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -175,9 +175,6 @@ void ValueFieldLayoutInfo::print_on(outputStream* st) const { st->print("_layout: "); LayoutKindHelper::print_on(_kind, st); - st->cr(); - - st->print("_null_marker_offset: %d", _null_marker_offset); } // A value class is considered naturally atomic if its layout, @@ -236,6 +233,17 @@ bool InstanceKlass::is_class_in_loadable_descriptors_attribute(Symbol* name) con return false; } +int InstanceKlass::field_null_marker_offset(int index) const { + const FieldInfo field = this->field(index); + if (!field.field_flags().has_null_marker()) { + return -1; + } + + const ValueKlass* const vk = value_field_layout_info(index).klass(); + const int nm_offset = vk->null_marker_offset_in_payload(); + return field_offset(index) + nm_offset; +} + static inline bool is_stack_chunk_class(const Symbol* class_name, const ClassLoaderData* loader_data) { return (class_name == vmSymbols::jdk_internal_vm_StackChunk() && diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 7662441eddb..2ca8a187dac 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -132,10 +132,9 @@ class ValueFieldLayoutInfo : public MetaspaceObj { ValueKlass* _klass; LayoutKind _kind; - int _null_marker_offset; // null marker offset for this field, relative to the beginning of the current container public: - ValueFieldLayoutInfo(): _klass(nullptr), _kind(LayoutKind::UNKNOWN), _null_marker_offset(-1) {} + ValueFieldLayoutInfo(): _klass(nullptr), _kind(LayoutKind::UNKNOWN) {} ValueKlass* klass() const { return _klass; } void set_klass(ValueKlass* k) { _klass = k; } @@ -146,17 +145,10 @@ class ValueFieldLayoutInfo : public MetaspaceObj { } void set_kind(LayoutKind lk) { _kind = lk; } - int null_marker_offset() const { - assert(_null_marker_offset != -1, "Not set"); - return _null_marker_offset; - } - void set_null_marker_offset(int o) { _null_marker_offset = o; } - void metaspace_pointers_do(MetaspaceClosure* it); MetaspaceObj::Type type() const { return ValueFieldLayoutInfoType; } static ByteSize klass_offset() { return byte_offset_of(ValueFieldLayoutInfo, _klass); } - static ByteSize null_marker_offset_offset() { return byte_offset_of(ValueFieldLayoutInfo, _null_marker_offset); } // Print void print() const; @@ -465,7 +457,7 @@ class InstanceKlass: public Klass { bool field_is_null_free_value_type(int index) const; bool is_class_in_loadable_descriptors_attribute(Symbol* name) const; - int field_null_marker_offset(int index) const { return value_field_layout_info(index).null_marker_offset(); } + int field_null_marker_offset(int index) const; // Number of Java declared fields int java_fields_count() const; From b4c404d403686ef3bbe737fd7507ce08022fd491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Tue, 6 Oct 2026 07:39:09 +0000 Subject: [PATCH 63/98] 8392927: RISC-V: explicitly narrow unsigned subword types in extract_v Reviewed-by: fyang, dzhang --- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 6 ++++++ .../cpu/riscv/macroAssembler_riscv.cpp | 20 +++++++++++++++++++ .../cpu/riscv/macroAssembler_riscv.hpp | 3 +++ 3 files changed, 29 insertions(+) diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 99898e251ee..bdcf60f41c7 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -3348,6 +3348,9 @@ void C2_MacroAssembler::extract_v(Register dst, VectorRegister src, slidedown_v(vtmp, src, idx); vmv_x_s(dst, vtmp); } + if (is_unsigned_subword_type(bt)) { + narrow_subword_type(dst, bt); + } } // Extract a scalar element from a vector at position 'idx'. @@ -3359,6 +3362,9 @@ void C2_MacroAssembler::extract_v(Register dst, VectorRegister src, vsetvli_helper(bt, 1); vslidedown_vx(vtmp, src, idx); vmv_x_s(dst, vtmp); + if (is_unsigned_subword_type(bt)) { + narrow_subword_type(dst, bt); + } } // Extract a scalar element from an vector at position 'idx'. diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 6e6ca69da08..82fc4c6120c 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -7031,6 +7031,26 @@ void MacroAssembler::sext(Register dst, Register src, int bits) { srai(dst, dst, XLEN - bits); } +void MacroAssembler::narrow_subword_type(Register reg, BasicType bt) { + assert(is_subword_type(bt), "expected subword type"); + switch (bt) { + case T_SHORT: + sext(reg, reg, 16); + break; + case T_CHAR: + zext(reg, reg, 16); + break; + case T_BYTE: + sext(reg, reg, 8); + break; + case T_BOOLEAN: + andi(reg, reg, 1); + break; + default: + ShouldNotReachHere(); + } +} + void MacroAssembler::cmp_x2i(Register dst, Register src1, Register src2, Register tmp, bool is_signed) { if (src1 == src2) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 98781132419..780ffa4ebc6 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -32,6 +32,7 @@ #include "code/vmreg.hpp" #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" #include "runtime/signature.hpp" @@ -1707,6 +1708,8 @@ INSN(sd_release, sd, sd_rl); void zext(Register dst, Register src, int bits); void sext(Register dst, Register src, int bits); + void narrow_subword_type(Register reg, BasicType bt); + private: void cmp_x2i(Register dst, Register src1, Register src2, Register tmp, bool is_signed = true); From 40878144a3222c55f2fcbb913e7972b5aa79d903 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 6 Oct 2026 07:44:54 +0000 Subject: [PATCH 64/98] 8393013: G1: Improve card set group ownership Reviewed-by: ayang, aboldtch --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 10 ++--- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 13 +++--- .../share/gc/g1/g1CollectedHeap.inline.hpp | 3 +- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 44 ++++++++----------- src/hotspot/share/gc/g1/g1CollectionSet.hpp | 17 +++---- .../share/gc/g1/g1CollectionSet.inline.hpp | 2 +- .../share/gc/g1/g1CollectionSetCandidates.cpp | 3 +- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 6 +-- src/hotspot/share/gc/g1/g1HeapRegion.cpp | 3 +- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 2 +- src/hotspot/share/gc/g1/g1Policy.cpp | 6 +-- src/hotspot/share/gc/g1/g1Policy.hpp | 2 +- src/hotspot/share/gc/g1/g1RemSetSummary.cpp | 4 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 6 +-- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 4 +- 15 files changed, 56 insertions(+), 69 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 9f59d1c65c7..8577e9e0659 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -31,7 +31,6 @@ #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" -#include "gc/g1/g1CardSetGroup.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" @@ -1327,11 +1326,10 @@ G1CollectedHeap::G1CollectedHeap() : _gc_tracer_stw(new G1NewTracer()), _policy(new G1Policy(_gc_timer_stw)), _heap_sizing_policy(nullptr), - _collection_set(this, _policy), _rem_set(nullptr), _card_set_config(), _card_set_freelist_pool(G1CardSetConfiguration::num_mem_object_types()), - _young_regions_card_set_group(card_set_config(), &_card_set_freelist_pool, G1CardSetGroup::YoungId), + _collection_set(this, _policy), _cm(nullptr), _cr(nullptr), _task_queues(nullptr), @@ -1622,6 +1620,7 @@ jint G1CollectedHeap::initialize() { // values in the heap have been properly initialized. _monitoring_support = new G1MonitoringSupport(this); + _collection_set_candidates.initialize(max_num_regions()); _collection_set.initialize(max_num_regions()); start_new_collection_set(); @@ -2848,8 +2847,7 @@ void G1CollectedHeap::set_humongous_stats(uint num_humongous_total, uint num_hum } bool G1CollectedHeap::should_sample_collection_set_candidates() const { - const G1CollectionSetCandidates* candidates = collection_set()->candidates(); - return !candidates->is_empty(); + return !collection_set_candidates()->is_empty(); } void G1CollectedHeap::set_collection_set_candidates_stats(G1MonotonicArenaMemoryStats& stats) { @@ -2904,7 +2902,7 @@ void G1CollectedHeap::free_region(G1HeapRegion* hr, G1FreeRegionList* free_list) void G1CollectedHeap::retain_region(G1HeapRegion* hr) { MutexLocker x(G1RareEvent_lock, Mutex::_no_safepoint_check_flag); - collection_set()->candidates()->add_retained_region_unsorted(hr); + collection_set_candidates()->add_retained_region_unsorted(hr); } void G1CollectedHeap::free_humongous_region(G1HeapRegion* hr, diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index b1bdeebdfab..cd8ea2124dc 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -28,9 +28,9 @@ #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BiasedArray.hpp" #include "gc/g1/g1CardSet.hpp" -#include "gc/g1/g1CardSetGroup.hpp" #include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1CollectionSet.hpp" +#include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1EvacStats.hpp" @@ -395,8 +395,6 @@ class G1CollectedHeap : public CollectedHeap { G1Policy* _policy; G1HeapSizingPolicy* _heap_sizing_policy; - G1CollectionSet _collection_set; - // Try to allocate a single non-humongous G1HeapRegion sufficient for // an allocation of the given word_size. If do_expand is true, // attempt to expand the heap if necessary to satisfy the allocation @@ -794,13 +792,16 @@ class G1CollectedHeap : public CollectedHeap { G1MonotonicArenaFreePool _card_set_freelist_pool; - // Young-region card set group - G1CardSetGroup _young_regions_card_set_group; + G1CollectionSet _collection_set; + + // All old gen collection set candidate regions. + G1CollectionSetCandidates _collection_set_candidates; public: G1CardSetConfiguration* card_set_config() { return &_card_set_config; } - G1CardSetGroup* young_regions_card_set_group() { return &_young_regions_card_set_group; } + G1CollectionSetCandidates* collection_set_candidates() { return &_collection_set_candidates; } + const G1CollectionSetCandidates* collection_set_candidates() const { return &_collection_set_candidates; } // After a collection pause, reset eden and the collection set. void clear_eden(); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 6a0ddb66656..86f0872ea13 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -296,8 +296,7 @@ inline void G1CollectedHeap::set_humongous_is_live(oop obj) { } inline bool G1CollectedHeap::is_collection_set_candidate(const G1HeapRegion* r) const { - const G1CollectionSetCandidates* candidates = collection_set()->candidates(); - return candidates->contains(r); + return collection_set_candidates()->contains(r); } inline uint G1CollectedHeap::target_num_eden_regions() const { diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 5314f7449cc..846a515113e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -57,10 +57,10 @@ G1GCPhaseTimes* G1CollectionSet::phase_times() { G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : _g1h(g1h), _policy(policy), - _candidates(), _regions(nullptr), _max_num_regions(0), _num_regions(0), + _young_regions_card_set_group(g1h->card_set_config(), g1h->card_set_freelist_pool(), G1CardSetGroup::YoungId), _selected_groups(), _num_eden_regions(0), _num_survivor_regions(0), @@ -73,7 +73,6 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) : G1CollectionSet::~G1CollectionSet() { FREE_C_HEAP_ARRAY(_regions); - abandon_all_candidates(); } void G1CollectionSet::prepare_for_collection(uint num_eden_cset_regions, @@ -95,25 +94,18 @@ void G1CollectionSet::initialize(uint max_num_regions) { guarantee(_regions == nullptr, "Must only initialize once."); _max_num_regions = max_num_regions; _regions = NEW_C_HEAP_ARRAY(uint, max_num_regions, mtGC); - - _candidates.initialize(max_num_regions); } void G1CollectionSet::abandon() { - _g1h->young_regions_card_set_group()->clear(true /* uninstall_card_set_group */); + young_regions_card_set_group()->clear(true /* uninstall_card_set_group */); clear(); - abandon_all_candidates(); + _num_initial_old_regions = 0; stop_incremental_building(); } -void G1CollectionSet::abandon_all_candidates() { - _candidates.clear(); - _num_initial_old_regions = 0; -} - void G1CollectionSet::prepare_for_scan () { - _g1h->young_regions_card_set_group()->card_set()->reset_table_scanner_for_groups(); + young_regions_card_set_group()->card_set()->reset_table_scanner_for_groups(); _selected_groups.prepare_for_scan(); } @@ -146,8 +138,7 @@ void G1CollectionSet::start() { continue_incremental_building(); - G1CardSetGroup* young_group = _g1h->young_regions_card_set_group(); - young_group->clear(); + young_regions_card_set_group()->clear(); } void G1CollectionSet::continue_incremental_building() { @@ -220,7 +211,7 @@ void G1CollectionSet::add_young_region_common(G1HeapRegion* hr) { assert(_inc_build_state == CSetBuildType::Active, "Precondition"); _g1h->policy()->remset_tracker()->update_at_allocate(hr); - _g1h->young_regions_card_set_group()->add(hr); + young_regions_card_set_group()->add(hr); // Synchronize with the region attribute table. _g1h->register_young_region_with_region_attr(hr); @@ -375,9 +366,10 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi // long time. void G1CollectionSet::finalize_old_part(double time_remaining_ms) { Ticks start_time = Ticks::now(); + G1CollectionSetCandidates* candidates = _g1h->collection_set_candidates(); - if (!candidates()->is_empty()) { - candidates()->verify(); + if (!candidates->is_empty()) { + candidates->verify(); if (collector_state()->is_in_mixed_phase()) { time_remaining_ms = select_candidates_from_marking(time_remaining_ms); @@ -385,7 +377,7 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); } - if (candidates()->retained_groups().num_regions() > 0) { + if (candidates->retained_groups().num_regions() > 0) { select_candidates_from_retained(time_remaining_ms); } // Optional groups are selected separately from marking and retained candidate @@ -393,7 +385,7 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { _optional_groups.sort_by_efficiency(); _optional_groups.verify(); - candidates()->verify(); + candidates->verify(); } else { log_debug(gc, ergo, cset)("No candidates to reclaim."); } @@ -429,11 +421,13 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) double optional_threshold_ms = time_remaining_ms * _policy->optional_prediction_fraction(); - uint min_num_old_cset_regions = _policy->calc_min_num_old_cset_regions(candidates()->num_last_marking_candidate_regions()); + G1CollectionSetCandidates* candidates = _g1h->collection_set_candidates(); + + uint min_num_old_cset_regions = _policy->calc_min_num_old_cset_regions(candidates->num_last_marking_candidate_regions()); uint max_num_old_cset_regions = MAX2(min_num_old_cset_regions, _policy->calc_max_num_old_cset_regions()); bool check_time_remaining = _policy->use_adaptive_num_young_regions(); - G1CardSetGroupList* from_marking_groups = &candidates()->from_marking_groups(); + G1CardSetGroupList* from_marking_groups = &candidates->from_marking_groups(); bool make_first_group_optional = G1ForceOptionalEvacuation; @@ -498,7 +492,7 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) // Remove selected groups from the collection set candidates. if (num_initial_groups > 0) { - candidates()->remove(&selected_groups); + candidates->remove(&selected_groups); } if (from_marking_groups->length() == 0) { @@ -539,7 +533,7 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms) double optional_time_remaining_ms = _policy->max_time_for_retaining(); time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); - G1CardSetGroupList* retained_groups = &candidates()->retained_groups(); + G1CardSetGroupList* retained_groups = &_g1h->collection_set_candidates()->retained_groups(); log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " "Min %u regions, available %u regions (%u groups), " @@ -612,7 +606,7 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms) // Remove groups from retained and also do some bookkeeping on CandidateOrigin // for the regions in these groups. - candidates()->remove(&remove_from_retained); + _g1h->collection_set_candidates()->remove(&remove_from_retained); groups_to_abandon.clear(true /* uninstall_card_set_group */); @@ -656,7 +650,7 @@ double G1CollectionSet::select_candidates_from_optional_groups(double time_remai // Remove selected groups from collection set candidates and optional groups. if (selected.length() > 0) { _optional_groups.remove(&selected); - candidates()->remove(&selected); + _g1h->collection_set_candidates()->remove(&selected); } return total_prediction_ms; } diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index d3eb4edd08d..82afa9fef7c 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -26,7 +26,6 @@ #define SHARE_GC_G1_G1COLLECTIONSET_HPP #include "gc/g1/g1CardSetGroup.hpp" -#include "gc/g1/g1CollectionSetCandidates.hpp" #include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -136,10 +135,7 @@ class G1CollectionSet { G1CollectedHeap* _g1h; G1Policy* _policy; - // All old gen collection set candidate regions. - G1CollectionSetCandidates _candidates; - - // The actual collection set as an array of region indices. + // The actual collection set as a set of region indices. // // All regions in _regions below _num_regions are assumed to be part of the // collection set. @@ -156,6 +152,9 @@ class G1CollectionSet { Atomic _num_regions; + // Card set group for the young generation regions. + G1CardSetGroup _young_regions_card_set_group; + // Card set groups selected for evacuation. G1CardSetGroupList _selected_groups; @@ -168,6 +167,7 @@ class G1CollectionSet { // When doing mixed collections we can add old regions to the collection set, which // will be collected only if there is enough time. We call these optional (old) // groups. Regions are reachable via this list as well. + // Groups in this list are not owned by the collection set. G1CardSetGroupList _optional_groups; #ifdef ASSERT @@ -243,13 +243,10 @@ class G1CollectionSet { // Initializes the collection set giving the maximum possible number of regions in the collection set. void initialize(uint max_num_regions); - // Drop the collection set and collection set candidates. + // Drop the collection set. void abandon(); - // Drop all collection set candidates (only the candidates). - void abandon_all_candidates(); - G1CollectionSetCandidates* candidates() { return &_candidates; } - const G1CollectionSetCandidates* candidates() const { return &_candidates; } + G1CardSetGroup* young_regions_card_set_group() { return &_young_regions_card_set_group; } void prepare_for_scan(); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp index 4a64cdcbebe..599e4fe511f 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp @@ -33,7 +33,7 @@ template inline void G1CollectionSet::merge_collection_set_card_set_groups(CardOrRangeVisitor& cl, uint worker_id, uint num_workers) { uint offset = _selected_groups_inc_part_start; if (offset == 0) { - _g1h->young_regions_card_set_group()->iterate_for_merge(cl); + young_regions_card_set_group()->iterate_for_merge(cl); } const uint next_selected_group_increment = num_selected_groups_in_increment(); diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 5b98686947a..2cd1085ca4a 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -37,9 +37,8 @@ G1CollectionSetCandidates::G1CollectionSetCandidates() : { } G1CollectionSetCandidates::~G1CollectionSetCandidates() { + clear(); FREE_C_HEAP_ARRAY(_contains_map); - _from_marking_groups.clear(); - _retained_groups.clear(); } bool G1CollectionSetCandidates::is_from_marking(G1HeapRegion* r) const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 7312886391d..d6cbc82a6f4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -1418,7 +1418,7 @@ void G1ConcurrentMark::remark() { if (_needs_remembered_set_rebuild) { GrowableArrayCHeap* selected = cl.sort_and_prune_old_selected(); - _g1h->policy()->candidates()->set_candidates_from_marking(selected); + _g1h->collection_set_candidates()->set_candidates_from_marking(selected); } } @@ -3266,9 +3266,9 @@ void G1PrintRegionLivenessInfoClosure::log_card_set_groups() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - log_card_set_group_add_total(g1h->young_regions_card_set_group(), "Y"); + log_card_set_group_add_total(g1h->collection_set()->young_regions_card_set_group(), "Y"); - G1CollectionSetCandidates* candidates = g1h->policy()->candidates(); + G1CollectionSetCandidates* candidates = g1h->collection_set_candidates(); log_card_set_group_list(candidates->from_marking_groups(), "M"); log_card_set_group_list(candidates->retained_groups(), "R"); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index ef2afdf96c0..3103bb551d4 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -27,7 +27,6 @@ #include "gc/g1/g1BlockOffsetTable.inline.hpp" #include "gc/g1/g1CardSetGroup.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionBounds.inline.hpp" @@ -438,7 +437,7 @@ void G1HeapRegion::print_on(outputStream* st) const { if (in_collection_set()) { st->print("|CS"); } else if (is_collection_set_candidate()) { - G1CollectionSetCandidates* candidates = G1CollectedHeap::heap()->collection_set()->candidates(); + G1CollectionSetCandidates* candidates = G1CollectedHeap::heap()->collection_set_candidates(); st->print("|%s", candidates->get_short_type_str(this)); } else { st->print("| "); diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index b477138873a..837fcf3f9b8 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -434,7 +434,7 @@ void G1HeapVerifier::verify_region_sets() { _g1h->heap_region_iterate(&cl); cl.verify_counts(&_g1h->_old_set, &_g1h->_humongous_set, &_g1h->_hrm); - _g1h->collection_set()->candidates()->verify(); + _g1h->collection_set_candidates()->verify(); } class G1VerifyRegionMarkingStateClosure : public G1HeapRegionClosure { diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 066d057c4d6..34918b7ce3f 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -585,7 +585,7 @@ void G1Policy::record_full_collection_start() { record_pause_start_time(); // Release the future to-space so that it is available for compaction into. collector_state()->set_in_full_gc(); - _collection_set->abandon_all_candidates(); + candidates()->clear(); } void G1Policy::record_full_collection_end(size_t allocation_word_size) { @@ -720,7 +720,7 @@ void G1Policy::record_concurrent_mark_remark_end() { } G1CollectionSetCandidates* G1Policy::candidates() const { - return _collection_set->candidates(); + return _g1h->collection_set_candidates(); } double G1Policy::average_time_ms(G1GCPhaseTimes::GCParPhases phase) const { @@ -1375,7 +1375,7 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se } void G1Policy::abandon_collection_set_candidates() { - _collection_set->abandon_all_candidates(); + candidates()->clear(); } void G1Policy::update_gc_pause_time_ratios(Pause gc_type, double start_time_sec, double end_time_sec) { diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 13b5ae1e719..b2374b16026 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -172,9 +172,9 @@ class G1Policy: public CHeapObj { return _mmu_tracker->max_gc_time() * 1000.0; } +private: G1CollectionSetCandidates* candidates() const; -private: G1CollectionSet* _collection_set; double average_time_ms(G1GCPhaseTimes::GCParPhases phase) const; diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index bc5aacbca74..86f5b10b26a 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -315,9 +315,9 @@ class G1HeapRegionStatsClosure: public G1HeapRegionClosure { void do_card_set_groups() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - accumulate_stats_for_group(g1h->young_regions_card_set_group(), &_young); + accumulate_stats_for_group(g1h->collection_set()->young_regions_card_set_group(), &_young); - G1CollectionSetCandidates* candidates = g1h->policy()->candidates(); + G1CollectionSetCandidates* candidates = g1h->collection_set_candidates(); for (G1CardSetGroup* group : candidates->from_marking_groups()) { accumulate_stats_for_group(group, &_old); } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index ac10c2e9468..ebba745998f 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -556,7 +556,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) Tickspan task_time = run_task_timed(&g1_prep_task); G1MonotonicArenaMemoryStats sampled_card_set_stats = g1_prep_task.all_card_set_stats(); - sampled_card_set_stats.add(_g1h->young_regions_card_set_group()->card_set_memory_stats()); + sampled_card_set_stats.add(collection_set()->young_regions_card_set_group()->card_set_memory_stats()); _g1h->set_young_gen_card_set_stats(sampled_card_set_stats); _g1h->set_humongous_stats(g1_prep_task.humongous_total(), g1_prep_task.humongous_candidates()); @@ -1024,7 +1024,7 @@ void G1YoungCollector::post_evacuate_cleanup_2(G1ParScanThreadStateSet* per_thre void G1YoungCollector::enqueue_candidates_as_root_regions() { assert(collector_state()->is_in_concurrent_start_gc(), "must be"); - G1CollectionSetCandidates* candidates = collection_set()->candidates(); + G1CollectionSetCandidates* candidates = _g1h->collection_set_candidates(); candidates->iterate_regions([&] (G1HeapRegion* r) { _g1h->concurrent_mark()->add_root_region_set_bottom(r); }); @@ -1119,7 +1119,7 @@ void G1YoungCollector::collect() { G1YoungGCJFRTracerMark jtm(this); // JStat/MXBeans G1YoungGCMonitoringScope ms(monitoring_support(), - !collection_set()->candidates()->is_empty() /* all_memory_pools_affected */); + !_g1h->collection_set_candidates()->is_empty() /* all_memory_pools_affected */); // Create the heap printer before internal pause timing to have // heap information printed as last part of detailed GC log. G1HeapPrinterMark hpm(_g1h); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 9259893c534..824a4321d4b 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -108,7 +108,7 @@ class G1PostEvacuateCollectionSetCleanupTask1::SampleCollectionSetCandidatesTask G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1MonotonicArenaMemoryStats _total; - G1CollectionSetCandidates* candidates = g1h->collection_set()->candidates(); + G1CollectionSetCandidates* candidates = g1h->collection_set_candidates(); for (G1CardSetGroup* gr : candidates->from_marking_groups()) { _total.add(gr->card_set_memory_stats()); } @@ -853,7 +853,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::FreeCollectionSetTask : public G1 bool has_new_retained_regions = _num_retained_regions.load_relaxed() != 0; if (has_new_retained_regions) { - G1CollectionSetCandidates* candidates = _g1h->collection_set()->candidates(); + G1CollectionSetCandidates* candidates = _g1h->collection_set_candidates(); candidates->sort_by_efficiency(); } From 15f21d13a01509a91abc2fb380ffda1b3c7b61e2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 6 Oct 2026 10:03:59 +0000 Subject: [PATCH 65/98] 8392855: G1: Errorneous PLAB size calculations with G1LastPLABAverageOccupancy Reviewed-by: ayang, aboldtch --- src/hotspot/share/gc/g1/g1EvacStats.cpp | 9 +- src/hotspot/share/gc/g1/g1EvacStats.hpp | 2 + src/hotspot/share/gc/g1/g1_globals.hpp | 2 +- test/hotspot/gtest/gc/g1/test_g1EvacStats.cpp | 129 ++++++++++++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/gtest/gc/g1/test_g1EvacStats.cpp diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp index 58d7b3fec78..cbb3fb9f92b 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp @@ -111,6 +111,10 @@ size_t G1EvacStats::compute_desired_plab_size() const { // what we consider as "used_for_waste_calculation" below). This is not // completely fair, but is a conservative assumption because PLABs may be sized // flexibly while we cannot adjust inline allocations. + // + // With a maximum TargetPLABWastePct of 100(%), the natural cap for the amplification + // potentially provided by G1LastPLABAverageOccupancy is at 2x used(). + // // Allocation during GC will try to minimize region end waste so this impact // should be minimal. // @@ -122,8 +126,9 @@ size_t G1EvacStats::compute_desired_plab_size() const { // which is an okay reaction. size_t const used_for_waste_calculation = used() > region_end_waste() ? used() - region_end_waste() : 0; - size_t const total_waste_allowed = used_for_waste_calculation * TargetPLABWastePct; - return (size_t)((double)total_waste_allowed / (100 - G1LastPLABAverageOccupancy)); + double const total_waste_allowed = (double)used_for_waste_calculation * TargetPLABWastePct; + return (size_t)MIN2(total_waste_allowed / (100 - G1LastPLABAverageOccupancy), + (double)used() * (100 + TargetPLABWastePct) / 100); } G1EvacStats::G1EvacStats(const char* description, size_t default_per_thread_plab_size, unsigned wt) : diff --git a/src/hotspot/share/gc/g1/g1EvacStats.hpp b/src/hotspot/share/gc/g1/g1EvacStats.hpp index e133d9feaf3..e115622bcf3 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.hpp @@ -32,6 +32,8 @@ // Records various memory allocation statistics gathered during evacuation. All sizes // are in HeapWords. class G1EvacStats : public PLABStats { + friend class G1EvacStatsTest; + size_t _default_plab_size; size_t _desired_net_plab_size; // Output of filter (below), suitably trimmed and quantized AdaptiveWeightedAverage diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 9252455b7fc..b33b575380c 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -152,7 +152,7 @@ product(double, G1LastPLABAverageOccupancy, 50.0, EXPERIMENTAL, \ "The expected average occupancy of the last PLAB in " \ "percent.") \ - range(0.001, 100.0) \ + range(0.0, 99.999) \ \ product(size_t, G1SATBBufferSize, 1*K, \ "Number of entries in an SATB log buffer.") \ diff --git a/test/hotspot/gtest/gc/g1/test_g1EvacStats.cpp b/test/hotspot/gtest/gc/g1/test_g1EvacStats.cpp new file mode 100644 index 00000000000..367ed2150ca --- /dev/null +++ b/test/hotspot/gtest/gc/g1/test_g1EvacStats.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "gc/g1/g1EvacStats.inline.hpp" +#include "gc/shared/gc_globals.hpp" +#include "gc/shared/plab.inline.hpp" +#include "utilities/autoRestore.hpp" + +#include "unittest.hpp" + +class G1EvacStatsTest { + +public: + static void compute_desired_plab_size_bounds(uint target_plab_waste_percent, double last_plab_average_occupancy); + static void compute_desired_plab_size_zero_used(uint target_plab_waste_percent, double last_plab_average_occupancy); + static void compute_desired_plab_size_larger_waste(uint target_plab_waste_percent, double last_plab_average_occupancy); + static void compute_desired_plab_size_check_overflow(size_t allocation_words); +}; + +void G1EvacStatsTest::compute_desired_plab_size_bounds(uint target_plab_waste_percent, double last_plab_average_occupancy) { + UIntFlagSetting fl1(TargetPLABWastePct, target_plab_waste_percent); + AutoModifyRestore fl2(G1LastPLABAverageOccupancy, last_plab_average_occupancy); + + G1EvacStats stats("Test-Dummy-Old", OldPLABSize, PLABWeight); + stats.add_allocated(600000); + + size_t expected = stats.used() + stats.used() * TargetPLABWastePct / 100; + size_t actual = stats.compute_desired_plab_size(); + ASSERT_GT(actual, size_t(0)); + ASSERT_LE(actual, expected); +} + +void G1EvacStatsTest::compute_desired_plab_size_zero_used(uint target_plab_waste_percent, double last_plab_average_occupancy) { + UIntFlagSetting fl1(TargetPLABWastePct, target_plab_waste_percent); + AutoModifyRestore fl2(G1LastPLABAverageOccupancy, last_plab_average_occupancy); + + G1EvacStats stats("Test-Dummy-Old", OldPLABSize, PLABWeight); + stats.add_allocated(0); + + size_t actual = stats.compute_desired_plab_size(); + ASSERT_EQ(actual, size_t(0)); +} + +void G1EvacStatsTest::compute_desired_plab_size_larger_waste(uint target_plab_waste_percent, double last_plab_average_occupancy) { + UIntFlagSetting fl1(TargetPLABWastePct, target_plab_waste_percent); + AutoModifyRestore fl2(G1LastPLABAverageOccupancy, last_plab_average_occupancy); + + G1EvacStats stats("Test-Dummy-Old", OldPLABSize, PLABWeight); + stats.add_allocated(500000); + stats.add_region_end_waste(500001); + + size_t actual = stats.compute_desired_plab_size(); + ASSERT_EQ(actual, size_t(0)); + + stats.reset(); + + stats.add_allocated(500000); + stats.add_region_end_waste(500000); + + actual = stats.compute_desired_plab_size(); + ASSERT_EQ(actual, size_t(0)); +} + +void G1EvacStatsTest::compute_desired_plab_size_check_overflow(size_t allocation_words) { + UIntFlagSetting fl1(TargetPLABWastePct, 100); + AutoModifyRestore fl2(G1LastPLABAverageOccupancy, 50.0); + + G1EvacStats stats("Test-Dummy-Old", OldPLABSize, PLABWeight); + stats.add_allocated(allocation_words); + + size_t actual = stats.compute_desired_plab_size(); + ASSERT_GE(actual, allocation_words); +} + +TEST_VM(G1EvacStats, compute_desired_plab_size_bounds) { + G1EvacStatsTest::compute_desired_plab_size_bounds(1, 0.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(1, 50.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(1, 99.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(1, 99.999); + + G1EvacStatsTest::compute_desired_plab_size_bounds(10, 0.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(10, 50.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(10, 99.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(10, 99.999); + + G1EvacStatsTest::compute_desired_plab_size_bounds(100, 0.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(100, 50.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(100, 99.0); + G1EvacStatsTest::compute_desired_plab_size_bounds(100, 99.999); +} + + +TEST_VM(G1EvacStats, compute_desired_plab_size_zero_used) { + G1EvacStatsTest::compute_desired_plab_size_zero_used(1, 0.0); + G1EvacStatsTest::compute_desired_plab_size_zero_used(10, 50.0); + G1EvacStatsTest::compute_desired_plab_size_zero_used(100, 99.999); +} + +TEST_VM(G1EvacStats, compute_desired_plab_size_larger_waste) { + G1EvacStatsTest::compute_desired_plab_size_larger_waste(1, 0.0); + G1EvacStatsTest::compute_desired_plab_size_larger_waste(10, 50.0); + G1EvacStatsTest::compute_desired_plab_size_larger_waste(100, 99.999); +} + +TEST_VM(G1EvacStats, compute_desired_plab_size_check_overflow) { + G1EvacStatsTest::compute_desired_plab_size_check_overflow(SIZE_MAX / 100 - 1); + G1EvacStatsTest::compute_desired_plab_size_check_overflow(SIZE_MAX / 100); + G1EvacStatsTest::compute_desired_plab_size_check_overflow(SIZE_MAX / 100 + 1); +} From 7a9f39f0a58e067e06595a8c09e9ce62d08b0950 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 6 Oct 2026 10:04:50 +0000 Subject: [PATCH 66/98] 8392947: Partial Array Task stepper may assert on large object arrays in debug builds Reviewed-by: ayang, aboldtch --- .../share/gc/shared/partialArrayTaskStepper.cpp | 6 +++++- .../gc/shared/partialArrayTaskStepper.inline.hpp | 4 ++-- .../gc/shared/test_partialArrayTaskStepper.cpp | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp index f7d53c9348a..126c6c30220 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp @@ -51,4 +51,8 @@ static uint compute_task_fanout(uint task_limit) { PartialArrayTaskStepper::PartialArrayTaskStepper(uint n_workers) : _task_limit(compute_task_limit(n_workers)), _task_fanout(compute_task_fanout(_task_limit)) -{} +{ + // Current stepper code only supports up to 2^31 elements in the array. + // arrayOopDesc::max_array_length() is not constexpr, so we workaround using the return type's maximum value. + static_assert(std::numeric_limits::max() <= INT32_MAX); +} diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index 538815698f2..fb58400cf27 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -69,7 +69,7 @@ PartialArrayTaskStepper::next_impl(size_t length, size_t chunk_size, Atomic (F-1)*N + 1 assert(_task_limit > 0, "precondition"); assert(_task_fanout > 0, "precondition"); - uint max_pending = (_task_fanout - 1) * task_num + 1; + uint64_t max_pending = (uint64_t)(_task_fanout - 1) * task_num + 1; // The actual pending may be less than that. Bound by remaining_tasks to // not overrun. Also bound by _task_limit to avoid spawning an excessive @@ -78,7 +78,7 @@ PartialArrayTaskStepper::next_impl(size_t length, size_t chunk_size, Atomic(max_pending, remaining_tasks, _task_limit); uint ncreate = MIN2(_task_fanout, MIN2(remaining_tasks, _task_limit + 1) - pending); return Step{ start, ncreate }; } diff --git a/test/hotspot/gtest/gc/shared/test_partialArrayTaskStepper.cpp b/test/hotspot/gtest/gc/shared/test_partialArrayTaskStepper.cpp index 3f5b6c0706c..6f2ccc6c14e 100644 --- a/test/hotspot/gtest/gc/shared/test_partialArrayTaskStepper.cpp +++ b/test/hotspot/gtest/gc/shared/test_partialArrayTaskStepper.cpp @@ -79,3 +79,19 @@ TEST(PartialArrayTaskStepperTest, doit) { } } } + +// Verifies that with below constants the stepper won't create tasks beyond the original array. +TEST(PartialArrayTaskStepperTest, overflow_beyond_array) { + const size_t Length = INT32_MAX; + const size_t ChunkSize = 1; + const size_t Index = 1431655765; + const size_t NumWorkers = 16; // Fanout is 4. + + const Stepper stepper(NumWorkers); + + Atomic to_length; + to_length.store_relaxed(Index); + + Step step = StepperSupport::next(&stepper, Length, ChunkSize, &to_length); + ASSERT_EQ(1u, step._ncreate); +} From 06df892e8cf4be18a62d3ab7dd21726a58f2ff3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 6 Oct 2026 11:13:19 +0000 Subject: [PATCH 67/98] 8383792: Simplify internal DocFinder API Reviewed-by: nbenalla --- .../html/taglets/InheritDocTaglet.java | 10 +--- .../formats/html/taglets/ParamTaglet.java | 15 ++--- .../formats/html/taglets/ReturnTaglet.java | 10 +--- .../formats/html/taglets/SimpleTaglet.java | 10 +--- .../formats/html/taglets/ThrowsTaglet.java | 8 +-- .../doclets/toolkit/util/DocFinder.java | 56 ++++++++++++++----- 6 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java index 7f8fd7ff741..c31378cbafd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/InheritDocTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,12 +121,8 @@ private Content retrieveInheritedDocumentation(TagletWriter writer, if (holderTag.getKind() == DocTree.Kind.DOC_COMMENT) { try { var docFinder = utils.docFinder(); - Result d; - if (src == null) { - d = docFinder.find(method, m -> extractMainDescription(m, isFirstSentence, utils)); - } else { - d = docFinder.search(src, m -> extractMainDescription(m, isFirstSentence, utils)); - } + Result d = docFinder.searchInherited( + method, src, m -> extractMainDescription(m, isFirstSentence, utils)); if (d instanceof Result.Conclude doc) { replacement = writer.commentTagsToOutput(doc.value().method, inheritDoc, doc.value().mainDescription, isFirstSentence); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java index 8a3c47bbfd4..63654b7b8e5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ParamTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,16 +95,9 @@ public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSent try { var docFinder = utils.docFinder(); - Optional r; - if (src != null){ - r = docFinder.search((ExecutableElement) src, - m -> DocFinder.Result.fromOptional(extract(utils, m, position, param.isTypeParameter()))) - .toOptional(); - } else { - r = docFinder.find((ExecutableElement) dst, - m -> DocFinder.Result.fromOptional(extract(utils, m, position, param.isTypeParameter()))) - .toOptional(); - } + Optional r = docFinder.searchInherited(method, (ExecutableElement) src, + m -> DocFinder.Result.fromOptional(extract(utils, m, position, param.isTypeParameter()))) + .toOptional(); return r.map(result -> new Output(result.paramTree, result.method, result.paramTree.getDescription(), true)) .orElseGet(() -> new Output(null, null, List.of(), true)); } catch (DocFinder.NoOverriddenMethodFound e) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java index 994d45b8914..094f2818033 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ReturnTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,12 +67,8 @@ public boolean isBlockTag() { public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) { try { var docFinder = utils.docFinder(); - Optional r; - if (src == null) { - r = docFinder.find((ExecutableElement) dst, m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional(); - } else { - r = docFinder.search((ExecutableElement) src, m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional(); - } + Optional r = docFinder.searchInherited((ExecutableElement) dst, (ExecutableElement) src, + m -> DocFinder.Result.fromOptional(extract(utils, m))).toOptional(); return r.map(result -> new Output(result.returnTree, result.method, result.returnTree.getDescription(), true)) .orElseGet(() -> new Output(null, null, List.of(), true)); } catch (DocFinder.NoOverriddenMethodFound e) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java index 6ce38b3b515..adf979697c5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java @@ -162,14 +162,8 @@ public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSent assert !isFirstSentence; try { var docFinder = utils.docFinder(); - Optional r; - if (src == null) { - r = docFinder.find((ExecutableElement) dst, - m -> DocFinder.Result.fromOptional(extractFirst(m))).toOptional(); - } else { - r = docFinder.search((ExecutableElement) src, - m -> DocFinder.Result.fromOptional(extractFirst(m))).toOptional(); - } + Optional r = docFinder.searchInherited((ExecutableElement) dst, (ExecutableElement) src, + m -> DocFinder.Result.fromOptional(extractFirst(m))).toOptional(); return r.map(result -> new Output(result.tag, result.method, result.description, true)) .orElseGet(()->new Output(null, null, List.of(), true)); } catch (DocFinder.NoOverriddenMethodFound e) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java index 9668fb27e07..81a43e81066 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/ThrowsTaglet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -634,11 +634,7 @@ private Map expandShallowly(Element exceptionType } DocFinder.Result> result; try { - if (src.isPresent()) { - result = utils.docFinder().search(src.get(), criterion); - } else { - result = utils.docFinder().find(holder, criterion); - } + result = utils.docFinder().searchInherited(holder, src.orElse(null), criterion); } catch (Failure.NotExceptionType | Failure.ExceptionTypeNotFound | Failure.UnsupportedTypeParameter x) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocFinder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocFinder.java index 73e70866d07..354d5ddc864 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocFinder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,23 +57,55 @@ public static final class NoOverriddenMethodFound extends Exception { private NoOverriddenMethodFound() { } } + /** + * Search for documentation beginning at {@code method}, then iterating through + * the list of methods it overrides, until {@code criterion} returns a valid result, + * throws an exception or the end of the hierarchy is reached. + * + * @param method the method where to start searching + * @param criterion function to compute the result + * @return the result + * @param result type + * @param exception type + * @throws X thrown by {@code criterion} + */ public Result search(ExecutableElement method, Criterion criterion) throws X { try { - return search0(method, true, false, criterion); + return search0(method, false, criterion); } catch (NoOverriddenMethodFound e) { // should not happen because the exception flag is unset throw new AssertionError(e); } } - public Result find(ExecutableElement method, - Criterion criterion) + /** + * Search for documentation in methods overridden by {@code method}. + * If {@code src} is not null, it is the overridden method where to begin searching. + * If {@code src} is null, all methods overridden by {@code method} are searched. + * If {@code src} is null and {@code method} does not override any methods, + * {@code NoOverriddenMethodFound} is thrown. + * + * @param method the overriding method + * @param src the method where to start searching, or null to search in all overridden methods + * @param criterion function to compute the result + * @return the result + * @param result type + * @param exception type + * @throws NoOverriddenMethodFound if {@code src} is null and {@code method} does not + * override any methods. + * @throws X thrown by {@code criterion} + */ + public Result searchInherited(ExecutableElement method, + ExecutableElement src, + Criterion criterion) throws NoOverriddenMethodFound, X { - return search0(method, false, true, criterion); + return src == null + ? search0(method, true, criterion) + : search0(src, false, criterion); } /* @@ -93,22 +125,20 @@ public Result find(ExecutableElement method, * recent call to Criterion::apply. * * If the given method overrides no methods (i.e. hierarchy consists of the - * given method only) and the search is instructed to detect that, the - * search terminates with an exception. + * given method only) and the search is instructed to search overridden + * methods only, the search terminates with an exception. */ private Result search0(ExecutableElement method, - boolean includeMethodInSearch, - boolean throwExceptionIfDoesNotOverride, + boolean overriddenMethodsOnly, Criterion criterion) throws NoOverriddenMethodFound, X { - // if the "overrides" check is requested and does not pass, throw the exception - // first so that it trumps the result that the search would otherwise had + // Throw exception if overriddenMethodsOnly is true and method does not override any methods. Iterator methods = overriddenMethodLookup.apply(method).iterator(); - if (throwExceptionIfDoesNotOverride && !methods.hasNext() ) { + if (overriddenMethodsOnly && !methods.hasNext() ) { throw new NoOverriddenMethodFound(); } - Result r = includeMethodInSearch ? criterion.apply(method) : Result.CONTINUE(); + Result r = overriddenMethodsOnly ? Result.CONTINUE() : criterion.apply(method); if (!(r instanceof Result.Continue)) { return r; } From 57bddb5e7e9f29d2d402d50032fd4adc8a409e9d Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Tue, 6 Oct 2026 11:31:58 +0000 Subject: [PATCH 68/98] 8393530: RISC-V: Fix TestImplicitNullChecks with Zalasr enabled Reviewed-by: fyang, dzhang --- .../compiler/gcbarriers/TestImplicitNullChecks.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java b/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java index a41cb4e1831..d25079f16cc 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,12 +82,21 @@ static Object testLoad(Outer o) { @Test // On aarch64, volatile loads always use indirect memory operands, which // leads to a pattern that cannot be exploited by the current C2 analysis. + // The same holds on RISC-V when UseZalasr is enabled. // On PPC64, volatile loads are preceded by membar_volatile instructions, // which also inhibits the current C2 analysis. - @IR(applyIfPlatformAnd = {"aarch64", "false", "ppc", "false"}, + @IR(applyIfPlatformAnd = {"aarch64", "false", "ppc", "false", "riscv64", "false"}, applyIfOr = {"UseZGC", "true", "UseG1GC", "true"}, counts = {IRNode.NULL_CHECK, "1"}, phase = CompilePhase.FINAL_CODE) + @IR(applyIfPlatform = {"riscv64", "true"}, + applyIfAnd = {"UseZalasr", "false", "UseZGC", "true"}, + counts = {IRNode.NULL_CHECK, "1"}, + phase = CompilePhase.FINAL_CODE) + @IR(applyIfPlatform = {"riscv64", "true"}, + applyIfAnd = {"UseZalasr", "false", "UseG1GC", "true"}, + counts = {IRNode.NULL_CHECK, "1"}, + phase = CompilePhase.FINAL_CODE) static Object testLoadVolatile(OuterWithVolatileField o) { return o.f; } From e3c17483bfdb32be5e16b33f5b11a1f25fe3e366 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Tue, 6 Oct 2026 11:51:45 +0000 Subject: [PATCH 69/98] 8393646: [BACKOUT] C2 VerifyIterativeGVN: fix many missing optimization/notification cases for arithmetic nodes Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/addnode.cpp | 4 - src/hotspot/share/opto/loopopts.cpp | 2 - src/hotspot/share/opto/node.cpp | 4 - src/hotspot/share/opto/phaseX.cpp | 532 ++++++++++-------- src/hotspot/share/opto/superword.cpp | 6 - .../loopopts/superword/TestReductions.java | 13 +- 6 files changed, 320 insertions(+), 241 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 4a6659db879..aa523fe9be8 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1610,8 +1610,6 @@ Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { } if (can_reshape) { return fold_subI_no_underflow_pattern(this, phase); - } else { - phase->record_for_igvn(this); } return nullptr; } @@ -1644,8 +1642,6 @@ Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { } if (can_reshape) { return fold_subI_no_underflow_pattern(this, phase); - } else { - phase->record_for_igvn(this); } return nullptr; } diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index d5332dbef54..77ccf3b31bd 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -612,8 +612,6 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { n_op == Op_MulD) { if (n2_loop == n_loop) { assert(n1_loop != n_loop, ""); - // IGVN may want to swap the edges back later, to commute (canonicalize) the operation. - _igvn.rehash_node_delayed(n); n->swap_edges(1, 2); } } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 163b9ee8978..d16edc38e23 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -1238,10 +1238,6 @@ bool Node::has_special_unique_user() const { } else if (op == Op_LoadUS && n->Opcode() == Op_LShiftI) { // Condition for RShiftI(LShiftI(LoadUS(...), 16), 16) => LoadS(...), see RShiftINode::Ideal return true; - } else if ((op == Op_LoadB || op == Op_LoadS) && n->Opcode() == Op_AndI) { - // AndINode::Ideal turns AndI(LoadB/S) into AndI(LoadUB/US), if the LoadB - // only has a single use. - return true; } else if (op == Op_AddL) { // Condition for convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) return n->Opcode() == Op_ConvL2I && n->in(1) == this; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 9587d0d046d..c3f255ab08e 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -440,34 +440,22 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, assert(worklist.is_subset_of(_useful), "only useful nodes should still be in the worklist"); - // While we don't need to rehash, some optimizations are sensitive to the - // relative ordering of node IDs, for example AddNode::commute wants to keep - // in1 < in2. Therefore, we keep the relative ordering stable. - uint next_idx = 0; - for (int old_idx = 0; old_idx < _old2new_map.length(); old_idx++) { - if (_useful.member_set().test(old_idx)) { - _old2new_map.at_put(old_idx, next_idx++); - } - } - assert(next_idx == _useful.size(), "must map all useful nodes"); - // Iterate over the set of live nodes. for (uint current_idx = 0; current_idx < _useful.size(); current_idx++) { Node* n = _useful.at(current_idx); - uint old_idx = n->_idx; - int new_idx = _old2new_map.at(old_idx); - assert(new_idx >= 0, "every useful node must have new idx"); - const Type* type = gvn->type_or_null(n); - _new_type_array.map(new_idx, type); + _new_type_array.map(current_idx, type); + + assert(_old2new_map.at(n->_idx) == -1, "already seen"); + _old2new_map.at_put(n->_idx, current_idx); if (old_node_note_array != nullptr) { - Node_Notes* nn = C->locate_node_notes(old_node_note_array, old_idx); - C->set_node_notes_at(new_idx, nn); + Node_Notes* nn = C->locate_node_notes(old_node_note_array, n->_idx); + C->set_node_notes_at(current_idx, nn); } - n->set_idx(new_idx); // Update node ID. + n->set_idx(current_idx); // Update node ID. if (update_embedded_ids(n) < 0) { _delayed.push(n); // has embedded IDs; handle later @@ -1391,51 +1379,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. switch (n->Opcode()) { - // BoolNode::Ideal canonicalizes integer comparisons, but only if "!is_counted_loop_exit_test". - // We only optimize if: - // Cmp -> Bool -> If - // And not if: - // Cmp -> Bool -> CountedLoopEnd - // In loop unswitching, we turn a CountedLoopEnd into an If. - // We would now have to notify the input of the modified node, - // which we don't do so far. - // One simple hack would be to just push the Bool node on - // the worklist in that optimization, in an ad-hoc fashon. - // But more principled would probably be to do this as part - // of replace_input_of, which calls set_req_X, which already - // has some notification logic, for example using - // Node::has_special_unique_user. There are also other places - // the graph could be updated and lose the CountedLoopEnd node, - // and in those places we also use Node::has_special_unique_user, - // and other related code. - // I would suggest we refactor and extend that notification - // code in a future RFE. - // - // Found with: - // -XX:VerifyIterativeGVN=1110 - // compiler/loopopts/TestBaseCountedEndLoopUnswitchCandidate.java - case Op_Bool: - return; - - // I have encountered some issues with AddHF. - // It could be due to issues arising from JDK-8388873, we should - // check again after that is fixed. - // - // Found on aarch64 with: - // -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110 - // compiler/c2/irTests/TestFloat16ScalarOperations.java - case Op_AddHF: - return; - - // In XorI/LNode::Ideal, we optimize Xor(x, -1), but - // only if is_used_in_only_arithmetic: so we would - // need notification if the Xor only has arithmetic - // nodes. I think this also calls for a generalization - // of Node::has_special_unique_user. - case Op_XorI: - case Op_XorL: - return; - // RegionNode::Ideal does "Skip around the useless IF diamond". // 245 IfTrue === 244 // 258 If === 245 257 @@ -1455,6 +1398,182 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_Region: return; + // In AddNode::Ideal, we call "commute", which swaps the inputs so + // that smaller idx are first. Tracking it back, it led me to + // PhaseIdealLoop::remix_address_expressions which swapped the edges. + // + // Example: + // Before PhaseIdealLoop::remix_address_expressions + // 154 AddI === _ 12 144 + // After PhaseIdealLoop::remix_address_expressions + // 154 AddI === _ 144 12 + // After AddNode::Ideal + // 154 AddI === _ 12 144 + // + // I suspect that the node should be added to the IGVN worklist after + // PhaseIdealLoop::remix_address_expressions + // + // This is the only case I looked at, there may be others. Found like this: + // java -XX:VerifyIterativeGVN=0100 -Xbatch --version + // + // The following hit the same logic in PhaseIdealLoop::remix_address_expressions. + // + // Note: currently all of these fail also for other reasons, for example + // because of "commute" doing the reordering with the phi below. Once + // that is resolved, we can come back to this issue here. + // + // case Op_AddD: + // case Op_AddI: + // case Op_AddL: + // case Op_AddF: + // case Op_MulI: + // case Op_MulL: + // case Op_MulF: + // case Op_MulD: + // if (n->in(1)->_idx > n->in(2)->_idx) { + // // Expect "commute" to revert this case. + // return false; + // } + // break; // keep verifying + + // AddFPNode::Ideal calls "commute", which can reorder the inputs for this: + // Check for tight loop increments: Loop-phi of Add of loop-phi + // It wants to take the phi into in(1): + // 471 Phi === 435 38 390 + // 390 AddF === _ 471 391 + // + // Other Associative operators are also affected equally. + // + // Investigate why this does not happen earlier during IGVN. + // + // Found with: + // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java + // -XX:VerifyIterativeGVN=1110 + case Op_AddD: + //case Op_AddI: // Also affected for other reasons, see case further down. + //case Op_AddL: // Also affected for other reasons, see case further down. + case Op_AddF: + case Op_MulI: + case Op_MulL: + case Op_MulF: + case Op_MulD: + case Op_MinF: + case Op_MinD: + case Op_MaxF: + case Op_MaxD: + // XorINode::Ideal + // Found with: + // compiler/intrinsics/chacha/TestChaCha20.java + // -XX:VerifyIterativeGVN=1110 + case Op_XorI: + case Op_XorL: + // It seems we may have similar issues with the HF cases. + // Found with aarch64: + // compiler/vectorization/TestFloat16VectorOperations.java + // -XX:VerifyIterativeGVN=1110 + case Op_AddHF: + case Op_MulHF: + case Op_MaxHF: + case Op_MinHF: + return; + + // In MulNode::Ideal the edges can be swapped to help value numbering: + // + // // We are OK if right is a constant, or right is a load and + // // left is a non-constant. + // if( !(t2->singleton() || + // (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) { + // if( t1->singleton() || // Left input is a constant? + // // Otherwise, sort inputs (commutativity) to help value numbering. + // (in(1)->_idx > in(2)->_idx) ) { + // swap_edges(1, 2); + // + // Why was this not done earlier during IGVN? + // + // Found with: + // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java + // -XX:VerifyIterativeGVN=1110 + case Op_AndI: + // Same for AndL. + // Found with: + // compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java + // -XX:VerifyIterativeGVN=1110 + case Op_AndL: + return; + + // SubLNode::Ideal does transform like: + // Convert "c1 - (y+c0)" into "(c1-c0) - y" + // + // In IGVN before verification: + // 8423 ConvI2L === _ 3519 [[ 8424 ]] #long:-2 + // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www + // 8424 AddL === _ 8422 8423 [[ 8383 ]] !orig=[8382] + // 8016 ConL === 0 [[ 8383 ]] #long:0 + // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154] + // + // And then in verification: + // 8338 ConL === 0 [[ 8339 8424 ]] #long:-2 <----- Was constant folded. + // 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www + // 8424 AddL === _ 8422 8338 [[ 8383 ]] !orig=[8382] + // 8016 ConL === 0 [[ 8383 ]] #long:0 + // 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154] + // + // So the form changed from: + // c1 - (y + [8423 ConvI2L]) + // to + // c1 - (y + -2) + // but the SubL was not added to the IGVN worklist. Investigate why. + // There could be other issues too. + // + // There seems to be a related AddL IGVN optimization that triggers + // the same SubL optimization, so investigate that too. + // + // Found with: + // java -XX:VerifyIterativeGVN=0100 -Xcomp --version + case Op_SubL: + return; + + // SubINode::Ideal does + // Convert "x - (y+c0)" into "(x-y) - c0" AND + // Convert "c1 - (y+c0)" into "(c1-c0) - y" + // + // Investigate why this does not yet happen during IGVN. + // + // Found with: + // test/hotspot/jtreg/compiler/c2/IVTest.java + // -XX:VerifyIterativeGVN=1110 + case Op_SubI: + return; + + // AddNode::IdealIL does transform like: + // Convert x + (con - y) into "(x - y) + con" + // + // In IGVN before verification: + // 8382 ConvI2L + // 8381 ConvI2L === _ 791 [[ 8383 ]] #long:0 + // 8383 SubL === _ 8381 8382 + // 8168 ConvI2L + // 8156 AddL === _ 8168 8383 [[ 8158 ]] + // + // And then in verification: + // 8424 AddL + // 8016 ConL === 0 [[ 8383 ]] #long:0 <--- Was constant folded. + // 8383 SubL === _ 8016 8424 + // 8168 ConvI2L + // 8156 AddL === _ 8168 8383 [[ 8158 ]] + // + // So the form changed from: + // x + (ConvI2L(0) - [8382 ConvI2L]) + // to + // x + (0 - [8424 AddL]) + // but the AddL was not added to the IGVN worklist. Investigate why. + // There could be other issues, too. For example with "commute", see above. + // + // Found with: + // java -XX:VerifyIterativeGVN=0100 -Xcomp --version + case Op_AddL: + return; + // SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does // Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass))); // record_for_cleanup(cmp, phase); @@ -1552,6 +1671,20 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_ConvI2L: return; + // AddNode::IdealIL can do this transform (and similar other ones): + // Convert "a*b+a*c into a*(b+c) + // The example had AddI(MulI(a, b), MulI(a, c)). Why did this not happen + // during IGVN? There was a mutation for one of the MulI, and only + // after that the pattern was as needed for the optimization. The MulI + // was added to the IGVN worklist, but not the AddI. This probably + // can be fixed by adding the correct pattern in add_users_of_use_to_worklist. + // + // Found with: + // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java + // -XX:VerifyIterativeGVN=1110 + case Op_AddI: + return; + // ArrayCopyNode::Ideal // calls ArrayCopyNode::prepare_array_copy // calls Compile::conv_I2X_index -> is called with sizetype = intcon(0), I think that @@ -1598,6 +1731,67 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit case Op_CastII: return; + // MaxLNode::Ideal + // calls AddNode::Ideal + // calls commute -> decides to swap edges + // + // Another notification issue, because we check inputs of inputs? + // MaxL -> Phi -> Loop + // MaxL -> Phi -> MaxL + // + // Found with: + // compiler/c2/irTests/TestIfMinMax.java + // -XX:VerifyIterativeGVN=1110 + case Op_MaxL: + case Op_MinL: + return; + + // OrINode::Ideal + // calls AddNode::Ideal + // calls commute -> left is Load, right not -> commute. + // + // Not sure why notification does not work here, seems like + // the depth is only 1, so it should work. Needs investigation. + // + // Found with: + // compiler/codegen/TestCharVect2.java#id0 + // -XX:VerifyIterativeGVN=1110 + case Op_OrI: + case Op_OrL: + return; + + // Bool -> constant folded to 1. + // Issue with notification? + // + // Found with: + // compiler/c2/irTests/TestVectorizationMismatchedAccess.java + // -XX:VerifyIterativeGVN=1110 + case Op_Bool: + return; + + // LShiftLNode::Ideal + // Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)" + // Probably a notification issue. + // + // Found with: + // compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java + // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 + case Op_LShiftL: + return; + + // LShiftINode::Ideal + // pattern: ((x + con1) << con2) -> x << con2 + con1 << con2 + // Could be issue with notification of inputs of inputs + // + // Side-note: should cases like these not be shared between + // LShiftI and LShiftL? + // + // Found with: + // compiler/escapeAnalysis/Test6689060.java + // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 + case Op_LShiftI: + return; + // AddPNode::Ideal seems to do set_req without removing lock first. // Found with various vector tests tier1-tier3. case Op_AddP: @@ -1647,6 +1841,13 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape, bool deep_revisit // java -XX:VerifyIterativeGVN=1110 -Xcomp --version case Op_CmpP: return; + + // MinINode::Ideal + // Did not investigate, but there are some patterns that might + // need more notification. + case Op_MinI: + case Op_MaxI: // preemptively removed it as well. + return; } if (n->is_Store()) { @@ -1852,6 +2053,24 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { case Op_CheckCastPP: return; + // In SubNode::Identity, we do: + // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y + // In the example, the AddI had an input replaced, the AddI is + // added to the IGVN worklist, but the SubI is one link further + // down and is not added. I checked add_users_of_use_to_worklist + // where I would expect the SubI would be added, and I cannot + // find the pattern, only this one: + // If changed AddI/SubI inputs, check CmpU for range check optimization. + // + // Fix this "notification" issue and check if there are any other + // issues. + // + // Found with: + // java -XX:VerifyIterativeGVN=1000 -Xcomp --version + case Op_SubI: + case Op_SubL: + return; + // PhiNode::Identity checks for patterns like: // r = (x != con) ? x : con; // that can be constant folded to "x". @@ -1865,6 +2084,22 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { // -XX:VerifyIterativeGVN=1110 case Op_Phi: return; + + // ConvI2LNode::Identity does + // convert I2L(L2I(x)) => x + // + // Investigate why this did not already happen during IGVN. + // + // Found with: + // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A + // -XX:VerifyIterativeGVN=1110 + case Op_ConvI2L: + return; + + // AbsINode::Identity + // Not investigated yet. + case Op_AbsI: + return; } if (n->is_Load()) { @@ -2298,18 +2533,8 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } - // AndLNode::Ideal folds GraphKit::mark_word_test patterns. Give it a chance to run. - if (n->is_Load() && use->is_Phi()) { - for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) { - Node* u = use->fast_out(i); - if (u->Opcode() == Op_AndL) { - worklist.push(u); - } - } - } - - const int use_op = use->Opcode(); - if (use->is_Cmp()) { // Enable CMP/BOOL optimization + int use_op = use->Opcode(); + if(use->is_Cmp()) { // Enable CMP/BOOL optimization add_users_to_worklist0(use, worklist); // Put Bool on worklist for (DUIterator_Fast jmax, j = use->fast_outs(jmax); j < jmax; j++) { Node* bol = use->fast_out(j); @@ -2455,18 +2680,6 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_AndI || u->Opcode() == Op_AndL; }); } - // OrI/LNode::Ideal converts shift/or patterns into rotates, which - // needs to trigger when x and y common. - // e.g. (x << 14) | (y >>> 18) - if (use_op == Op_LShiftI || use_op == Op_URShiftI) { - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->Opcode() == Op_OrI; - }); - } else if (use_op == Op_LShiftL || use_op == Op_URShiftL) { - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->Opcode() == Op_OrL; - }); - } // If changed LShift inputs, check CompressBits and ExpandBits users for // compress(x, 1 << n), compress(x, -1 << n), // expand(x, 1 << n), expand(x, -1 << n) optimizations. @@ -2496,33 +2709,6 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_CmpU; }); } - // LShiftNode::IdealIL: - // (x + c1) << c2 -> (x << c2) + c3 - // (c1 - x) << c2 -> c3 - (x << c2) - if (use_op == Op_AddI || use_op == Op_AddL || - use_op == Op_SubI || use_op == Op_SubL) { - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL; - }); - } - // AddNode::Ideal reassociates constants to the outside, e.g.: - // (x + C) + y -> (x + y) + C - // (x | C) | y -> (x | y) | C - if (use->is_Add()) { - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == use_op; - }); - } - // If changed AddI/AddL inputs, check for add users: - // e.g. x - (y + n) -> x - (y + C) -> (x - y) + (-C) - // before mutation after mutation optimized by - // AddNode::Ideal - if (use_op == Op_AddI || use_op == Op_AddL) { - int sub_op = (use_op == Op_AddI) ? Op_SubI : Op_SubL; - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == sub_op; - }); - } // If changed AddI/AddL inputs, check URShift users for // "((X << z) + Y) >>> z" optimization in URShift{I,L}Node::Ideal. if (use_op == Op_AddI || use_op == Op_AddL) { @@ -2551,20 +2737,10 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity - // LShiftNode::IdealIL optimizes patterns like: LShift(And(RShift(x, c), Y), c) if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { return u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL || - u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL || - u->Opcode() == Op_LShiftI || u->Opcode() == Op_LShiftL; - }); - } - // MulNode::AndIL_sum_and_mask can optimize: - // AndX(AddX(x, y), mask) -> AndX(x, mask) - if (use_op == Op_AddI || use_op == Op_AddL) { - const int and_op = (use_op == Op_AddI) ? Op_AndI : Op_AndL; - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == and_op; + u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; }); } // Check for redundant conversion patterns: @@ -2574,20 +2750,15 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // ConvI2F->ConvF2I->ConvI2F // Note: there may be other 3-nodes conversion chains that would require to be added here, but these // are the only ones that are known to trigger missed optimizations otherwise - // - // ConvI2LNode::Identity optimizes the following 2-hop optimization, once x is proven to be in int range: - // ConvI2L(ConvL2I(x)) -> x if (use_op == Op_ConvL2D || use_op == Op_ConvI2F || use_op == Op_ConvL2F || - use_op == Op_ConvF2I || - use_op == Op_ConvL2I) { - add_users_to_worklist_if(worklist, use, [&](Node* u) { + use_op == Op_ConvF2I) { + add_users_to_worklist_if(worklist, use, [=](Node* u) { return (use_op == Op_ConvL2D && u->Opcode() == Op_ConvD2L) || (use_op == Op_ConvI2F && u->Opcode() == Op_ConvF2I) || (use_op == Op_ConvL2F && u->Opcode() == Op_ConvF2L) || - (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F) || - (use_op == Op_ConvL2I && u->Opcode() == Op_ConvI2L); + (use_op == Op_ConvF2I && u->Opcode() == Op_ConvI2F); }); } // ConvD2F::Ideal matches ConvD2F(SqrtD(ConvF2D(x))) => SqrtF(x). @@ -2606,7 +2777,7 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // address expression flattening. if (use_op == Op_AddP) { bool offset_changed = n == use->in(AddPNode::Offset); - add_users_to_worklist_if(worklist, use, [&](Node* u) { + add_users_to_worklist_if(worklist, use, [=](Node* u) { return u->is_Mem() || (offset_changed && u->is_AddP() && u->in(AddPNode::Offset)->is_Con()); }); @@ -2619,12 +2790,8 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }); } // Check for Max/Min(A, Max/Min(B, C)) where A == B or A == C - // MinMaxNode::IdealI also optimizes these cases (if no overflow): - // MinI(AddI(x, -1), x) -> AddI(x, -1) - if (use->is_MinMax() || use->Opcode() == Op_AddI) { - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->is_MinMax(); - }); + if (use->is_MinMax()) { + add_users_to_worklist_if(worklist, use, [](Node* u) { return u->is_MinMax(); }); } // Check for A | (B | C) and (B | C) | A where A == B or A == C. if (use_op == Op_OrI || use_op == Op_OrL) { @@ -2790,90 +2957,9 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ // If changed Sub inputs, check Add for identity. // e.g., (x - y) + y -> x; x + (y - x) -> y. - // SubI/LNode::Ideal also optimize this: - // (x - y) - x -> -y if (use_op == Op_SubI || use_op == Op_SubL) { const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == add_op || u->Opcode() == use_op; - }); - } - - // If changed Mul inputs, check Add/Sub for common-factor reassociation. - // e.g., (a * b) + (a * c) -> a * (b + c). - if (use_op == Op_MulI || use_op == Op_MulL) { - const int add_op = (use_op == Op_MulI) ? Op_AddI : Op_AddL; - const int sub_op = (use_op == Op_MulI) ? Op_SubI : Op_SubL; - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == add_op || u->Opcode() == sub_op; - }); - } - - // MulNode::Ideal distributes constant multiplication: - // e.g., (x + c1) * c2 -> x * c2 + (c1 * c2) - if (use_op == Op_AddI || use_op == Op_AddL) { - const int mul_op = (use_op == Op_AddI) ? Op_MulI : Op_MulL; - add_users_to_worklist_if(worklist, use, [&](Node* u) { return u->Opcode() == mul_op; }); - } - - // We may have a loop-phi that is about to close the AddI recurrence, - // and we would have to ensure AddNode::commute is called for the AddI. - // e.g. use = Phi(x, n) -> use = Phi(x, u) -> use = Phi(x, u) - // u = AddI(y, use) u = AddI(y, use) -> u = AddI(use, y) - // current state, After mutation, After commute, - // before mutation before commute canonical loop incr - // - // Alternatively, we may have a region-phi, that is about to turn - // into a loop-phi, and where the AddI recurrence is already closed, - // and the AddI has its inputs sorted by idx. In this case, we also - // need AddNode::commute to canonicalize the AddI, so that the phi - // input is on the left now. - // e.g. use = Phi(region, u) -> use = Phi(loop, u) -> use = Phi(loop, u) - // u = AddI(y, use) u = AddI(y, use) -> u = AddI(use, y) - // current state, loop-phi, After commute, - // region-phi before commute canonical loop incr - if (use->is_Phi() && - use->req() == 3 && - (n == use->in(0) || // possibly: region-phi -> loop-phi - n == use->in(LoopNode::LoopBackControl))) { // possibly: closing AddI recurrence - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->is_Add(); - }); - } - - // AndLNode::Ideal optimizes GraphKit::mark_word_test patterns - // Pattern: AndL(LoadL(x=mark word), y) - // Pattern: AndL(Phi(LoadL(x=mark word), z), y) - if (use->is_Load()) { - for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { - Node* u = use->fast_out(i2); - if (u->Opcode() == Op_AndL) { - // n -> Load -> AndL - worklist.push(u); - } else if (u->is_Phi()) { - // n -> Load -> Phi -> AndL - add_users_to_worklist_if(worklist, u, [](Node* u2) { - return u2->Opcode() == Op_AndL; - }); - } - } - } - - // PhiNode::Ideal can optimize: - // Pattern: Phi(condition, x + y, x) -> x + Phi(condition, y, 0) - if (use_op == Op_AddI) { - add_users_to_worklist_if(worklist, use, [](Node* u) { - return u->is_Phi(); - }); - } - - // AndVNode::Ideal and OrVNode::Ideal have 2-hop operations for - // repeated and/or with the same value: - // Pattern: AndV(AndV(a, b), a) -> AndV(a, b) - if (use_op == Op_AndV || use_op == Op_OrV) { - add_users_to_worklist_if(worklist, use, [&](Node* u) { - return u->Opcode() == use_op; - }); + add_users_to_worklist_if(worklist, use, [=](Node* u) { return u->Opcode() == add_op; }); } // (Phi (ValueType ValueType)) is transformed into (ValueType (Phi ) (Phi )...) // and is applied if there are EncodeP/DecodeN or casts between a Phi and InlineType nodes diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 62fa32c7045..8acdf2b8000 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1081,12 +1081,10 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod if (is_marked_reduction(use1) && is_marked_reduction(use2)) { Node* use1_in2 = use1->in(2); if (use1_in2->is_Phi() || is_marked_reduction(use1_in2)) { - igvn().rehash_node_delayed(use1); use1->swap_edges(1, 2); } Node* use2_in2 = use2->in(2); if (use2_in2->is_Phi() || is_marked_reduction(use2_in2)) { - igvn().rehash_node_delayed(use2); use2->swap_edges(1, 2); } return PairOrderStatus::Ordered; @@ -1102,17 +1100,14 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod if (i1 != i2) { if ((i1 == (3-i2)) && (use2->is_Add() || use2->is_Mul())) { // 2. Commutative: swap edges, and hope the other position matches too. - igvn().rehash_node_delayed(use2); use2->swap_edges(i1, i2); } else if (VectorNode::is_muladds2i(use2) && use1 != use2) { // 3.a/b: MulAddS2I. if (i1 == 5 - i2) { // ((i1 == 3 && i2 == 2) || (i1 == 2 && i2 == 3) || (i1 == 1 && i2 == 4) || (i1 == 4 && i2 == 1)) - igvn().rehash_node_delayed(use2); use2->swap_edges(1, 2); use2->swap_edges(3, 4); } if (i1 == 3 - i2 || i1 == 7 - i2) { // ((i1 == 1 && i2 == 2) || (i1 == 2 && i2 == 1) || (i1 == 3 && i2 == 4) || (i1 == 4 && i2 == 3)) - igvn().rehash_node_delayed(use2); use2->swap_edges(2, 3); use2->swap_edges(1, 4); } @@ -1123,7 +1118,6 @@ SuperWord::PairOrderStatus SuperWord::order_inputs_of_uses_to_match_def_pair(Nod } } else if (i1 == i2 && VectorNode::is_muladds2i(use2) && use1 != use2) { // 3.c: MulAddS2I. - igvn().rehash_node_delayed(use2); use2->swap_edges(1, 3); use2->swap_edges(2, 4); return PairOrderStatus::Unknown; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java index d7b4278ede9..599e95bd6ab 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestReductions.java @@ -118,7 +118,8 @@ public static void main(String[] args) { switch (args[0]) { case "P0" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=0"); } case "P1" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=1"); } - case "P2" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=2"); } + // Note: increasing the node count limit also helps in some cases. + case "P2" -> { framework.addFlags("-XX:+UnlockDiagnosticVMOptions", "-XX:AutoVectorizationOverrideProfitability=2", "-XX:LoopUnrollLimit=1000"); } default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } }; framework.start(); @@ -2115,7 +2116,15 @@ private static long longAddBig() { IRNode.MUL_REDUCTION_VL, "> 0", IRNode.MUL_VL, "> 0"}, applyIfCPUFeatureOr = {"avx512dq", "true", "sve", "true"}, - applyIf = {"AutoVectorizationOverrideProfitability", "> 0"}) + applyIfAnd = {"AutoVectorizationOverrideProfitability", "> 0", + "LoopUnrollLimit", ">= 1000"}) + @IR(failOn = IRNode.LOAD_VECTOR_L, + applyIfCPUFeature = {"avx512dq", "true"}, + applyIfAnd = {"AutoVectorizationOverrideProfitability", "> 0", + "LoopUnrollLimit", "< 1000"}) + // Increasing the body limit seems to help. Filed for investigation: JDK-8370685 + // If you can eliminate this exception for LoopUnrollLimit, please remove + // the flag completely from the test, also the "addFlags" at the top. @IR(failOn = IRNode.LOAD_VECTOR_L, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"}) // MulVL is not implemented on NEON, so we also not have the reduction. From 4691e9c192cc30c4c08d46632f248c463b3bd48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 6 Oct 2026 12:29:20 +0000 Subject: [PATCH 70/98] 8380051: Update commonmark-java to 0.29.0 Reviewed-by: nbenalla --- .../markdown/MarkdownTransformer.java | 35 +- .../ext/gfm/tables/TablesExtension.java | 21 +- .../gfm/tables/internal/TableBlockParser.java | 6 +- .../internal/TableHtmlNodeRenderer.java | 12 +- .../internal/TableMarkdownNodeRenderer.java | 3 +- .../tables/internal/TableNodeRenderer.java | 16 +- .../TableTextContentNodeRenderer.java | 35 +- .../commonmark/internal/BlockQuoteParser.java | 1 + .../commonmark/internal/BlockStartImpl.java | 14 + .../org/commonmark/internal/Bracket.java | 39 +- ...renceDefinitions.java => Definitions.java} | 35 +- .../internal/DocumentBlockParser.java | 5 - .../commonmark/internal/DocumentParser.java | 191 ++++--- .../commonmark/internal/HeadingParser.java | 6 +- .../internal/InlineParserContextImpl.java | 44 +- .../commonmark/internal/InlineParserImpl.java | 500 +++++++++++++----- .../LinkReferenceDefinitionParser.java | 29 +- .../commonmark/internal/ListBlockParser.java | 2 +- .../commonmark/internal/ParagraphParser.java | 22 +- .../internal/inline/AutolinkInlineParser.java | 16 +- .../inline/BackslashInlineParser.java | 15 +- .../inline/BackticksInlineParser.java | 17 +- .../internal/inline/CoreLinkProcessor.java | 69 +++ .../internal/inline/EntityInlineParser.java | 22 +- .../internal/inline/HtmlInlineParser.java | 20 +- .../internal/inline/LinkResultImpl.java | 78 +++ .../internal/inline/ParsedInlineImpl.java | 5 +- .../commonmark/internal/inline/Scanner.java | 313 ----------- .../internal/renderer/NodeRendererMap.java | 23 +- .../renderer/text/BulletListHolder.java | 48 -- .../internal/renderer/text/ListHolder.java | 59 --- .../internal/util/AsciiMatcher.java | 83 --- .../commonmark/internal/util/CharMatcher.java | 38 -- .../commonmark/internal/util/Escaping.java | 61 +-- .../internal/util/Html5Entities.java | 2 +- .../commonmark/internal/util/LineReader.java | 181 +++++++ .../internal/org/commonmark/node/Block.java | 1 + .../org/commonmark/node/BlockQuote.java | 10 + .../org/commonmark/node/BulletList.java | 12 + .../internal/org/commonmark/node/Code.java | 12 + .../org/commonmark/node/CustomBlock.java | 3 + .../org/commonmark/node/CustomNode.java | 3 + .../org/commonmark/node/DefinitionMap.java | 99 ++++ .../org/commonmark/node/Document.java | 3 + .../org/commonmark/node/Emphasis.java | 8 + .../org/commonmark/node/FencedCodeBlock.java | 14 +- .../org/commonmark/node/HardLineBreak.java | 10 + .../internal/org/commonmark/node/Heading.java | 12 + .../org/commonmark/node/HtmlBlock.java | 2 +- .../org/commonmark/node/HtmlInline.java | 2 +- .../internal/org/commonmark/node/Image.java | 8 + .../commonmark/node/IndentedCodeBlock.java | 12 + .../internal/org/commonmark/node/Link.java | 5 +- .../node/LinkReferenceDefinition.java | 4 +- .../org/commonmark/node/ListBlock.java | 5 +- .../org/commonmark/node/ListItem.java | 23 + .../internal/org/commonmark/node/Node.java | 12 +- .../org/commonmark/node/OrderedList.java | 12 + .../org/commonmark/node/Paragraph.java | 2 + .../org/commonmark/node/SoftLineBreak.java | 9 + .../org/commonmark/node/SourceSpan.java | 70 ++- .../org/commonmark/node/SourceSpans.java | 7 +- .../org/commonmark/node/StrongEmphasis.java | 8 + .../internal/org/commonmark/node/Text.java | 10 + .../org/commonmark/node/ThematicBreak.java | 12 + .../parser/InlineParserContext.java | 37 +- .../parser/InlineParserFactory.java | 4 + .../org/commonmark/parser/Parser.java | 146 +++-- .../org/commonmark/parser/SourceLine.java | 12 +- .../beta}/InlineContentParser.java | 17 +- .../beta/InlineContentParserFactory.java} | 44 +- .../beta}/InlineParserState.java | 9 +- .../org/commonmark/parser/beta/LinkInfo.java | 101 ++++ .../commonmark/parser/beta/LinkProcessor.java | 72 +++ .../commonmark/parser/beta/LinkResult.java | 82 +++ .../inline => parser/beta}/ParsedInline.java | 26 +- .../org/commonmark/parser/beta/Scanner.java | 2 +- .../parser/block/AbstractBlockParser.java | 8 + .../commonmark/parser/block/BlockParser.java | 9 + .../commonmark/parser/block/BlockStart.java | 41 ++ .../parser/block/MatchedBlockParser.java | 3 +- .../parser/delimiter/DelimiterProcessor.java | 2 + .../org/commonmark/renderer/NodeRenderer.java | 16 + .../renderer/html/CoreHtmlNodeRenderer.java | 25 +- .../renderer/html/DefaultUrlSanitizer.java | 9 +- .../html/HtmlNodeRendererContext.java | 9 +- .../renderer/html/HtmlRenderer.java | 76 +-- .../commonmark/renderer/html/HtmlWriter.java | 20 +- .../markdown/CoreMarkdownNodeRenderer.java | 32 +- .../renderer/markdown/MarkdownRenderer.java | 12 +- .../text/CoreTextContentNodeRenderer.java | 196 ++++--- .../text/LineBreakRendering.java} | 27 +- .../text/TextContentNodeRendererContext.java | 7 + .../renderer/text/TextContentRenderer.java | 48 +- .../renderer/text/TextContentWriter.java | 108 +++- src/jdk.internal.md/share/legal/commonmark.md | 4 +- 96 files changed, 2412 insertions(+), 1248 deletions(-) rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/{LinkReferenceDefinitions.java => Definitions.java} (63%) create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/CoreLinkProcessor.java create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/LinkResultImpl.java delete mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java delete mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/BulletListHolder.java delete mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/ListHolder.java delete mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/AsciiMatcher.java delete mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/CharMatcher.java create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/LineReader.java create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/DefinitionMap.java rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/{internal/inline => parser/beta}/InlineContentParser.java (61%) rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/{internal/renderer/text/OrderedListHolder.java => parser/beta/InlineContentParserFactory.java} (61%) rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/{internal/inline => parser/beta}/InlineParserState.java (88%) create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkInfo.java create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkProcessor.java create mode 100644 src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkResult.java rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/{internal/inline => parser/beta}/ParsedInline.java (73%) rename src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/{internal/inline/Position.java => renderer/text/LineBreakRendering.java} (74%) diff --git a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java index ca0bd253e30..f6eb13e1bf8 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.regex.MatchResult; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -57,6 +58,8 @@ import jdk.internal.org.commonmark.parser.InlineParserContext; import jdk.internal.org.commonmark.parser.InlineParserFactory; import jdk.internal.org.commonmark.parser.Parser; +import jdk.internal.org.commonmark.parser.beta.InlineContentParserFactory; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import static com.sun.tools.javac.util.Position.NOPOS; @@ -542,10 +545,32 @@ public List getCustomDelimiterProcessors() { return inlineParserContext.getCustomDelimiterProcessors(); } + @Override + @Deprecated + public LinkReferenceDefinition getLinkReferenceDefinition(String label) { + return getDefinition(LinkReferenceDefinition.class, label); + } + + @Override + public List getCustomInlineContentParserFactories() { + return inlineParserContext.getCustomInlineContentParserFactories(); + } + + @Override + public List getCustomLinkProcessors() { + return inlineParserContext.getCustomLinkProcessors(); + } + + @Override + public Set getCustomLinkMarkers() { + return inlineParserContext.getCustomLinkMarkers(); + } + /** * {@inheritDoc} * - * If the given label does not match any explicitly defined + * If {@code type} indicates a link reference lookup and + * the given label does not match any explicitly defined * link reference definition, but does match a reference * to a program element, a synthetic link reference definition * is returned, with the label prefixed by the {@code autorefScheme}. @@ -554,7 +579,7 @@ public List getCustomDelimiterProcessors() { * @return the link reference definition for the label, or {@code null} */ @Override - public LinkReferenceDefinition getLinkReferenceDefinition(String label) { + public D getDefinition(Class type, String label) { // In CommonMark, square brackets characters need to be escaped within a link label. // See https://spec.commonmark.org/0.30/#link-label // Unescaped square bracket characters are not allowed inside the opening @@ -563,9 +588,9 @@ public LinkReferenceDefinition getLinkReferenceDefinition(String label) { // so we remove them before creating the autoref URL. // Note that the characters always appear together as a pair in API references. var l = label.replace("\\[\\]", "[]"); - var d = inlineParserContext.getLinkReferenceDefinition(l); - return d == null && isReference(l) - ? new LinkReferenceDefinition("", autorefScheme + l, "") + var d = inlineParserContext.getDefinition(type, label); + return d == null && type == LinkReferenceDefinition.class && isReference(l) + ? type.cast(new LinkReferenceDefinition("", autorefScheme + l, "")) : d; } }); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/TablesExtension.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/TablesExtension.java index 63cd4f8e2e5..bbefec96784 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/TablesExtension.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/TablesExtension.java @@ -39,17 +39,12 @@ import jdk.internal.org.commonmark.ext.gfm.tables.internal.TableTextContentNodeRenderer; import jdk.internal.org.commonmark.parser.Parser; import jdk.internal.org.commonmark.renderer.NodeRenderer; -import jdk.internal.org.commonmark.renderer.html.HtmlNodeRendererContext; -import jdk.internal.org.commonmark.renderer.html.HtmlNodeRendererFactory; import jdk.internal.org.commonmark.renderer.html.HtmlRenderer; import jdk.internal.org.commonmark.renderer.markdown.MarkdownNodeRendererContext; import jdk.internal.org.commonmark.renderer.markdown.MarkdownNodeRendererFactory; import jdk.internal.org.commonmark.renderer.markdown.MarkdownRenderer; -import jdk.internal.org.commonmark.renderer.text.TextContentNodeRendererContext; -import jdk.internal.org.commonmark.renderer.text.TextContentNodeRendererFactory; import jdk.internal.org.commonmark.renderer.text.TextContentRenderer; -import java.util.Collections; import java.util.Set; /** @@ -82,22 +77,12 @@ public void extend(Parser.Builder parserBuilder) { @Override public void extend(HtmlRenderer.Builder rendererBuilder) { - rendererBuilder.nodeRendererFactory(new HtmlNodeRendererFactory() { - @Override - public NodeRenderer create(HtmlNodeRendererContext context) { - return new TableHtmlNodeRenderer(context); - } - }); + rendererBuilder.nodeRendererFactory(TableHtmlNodeRenderer::new); } @Override public void extend(TextContentRenderer.Builder rendererBuilder) { - rendererBuilder.nodeRendererFactory(new TextContentNodeRendererFactory() { - @Override - public NodeRenderer create(TextContentNodeRendererContext context) { - return new TableTextContentNodeRenderer(context); - } - }); + rendererBuilder.nodeRendererFactory(TableTextContentNodeRenderer::new); } @Override @@ -110,7 +95,7 @@ public NodeRenderer create(MarkdownNodeRendererContext context) { @Override public Set getSpecialCharacters() { - return Collections.singleton('|'); + return Set.of('|'); } }); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java index aca4db8fc1c..724345d7ec2 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableBlockParser.java @@ -306,17 +306,17 @@ public static class Factory extends AbstractBlockParserFactory { @Override public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { List paragraphLines = matchedBlockParser.getParagraphLines().getLines(); - if (paragraphLines.size() == 1 && Characters.find('|', paragraphLines.get(0).getContent(), 0) != -1) { + if (!paragraphLines.isEmpty() && Characters.find('|', paragraphLines.get(paragraphLines.size() - 1).getContent(), 0) != -1) { SourceLine line = state.getLine(); SourceLine separatorLine = line.substring(state.getIndex(), line.getContent().length()); List columns = parseSeparator(separatorLine.getContent()); if (columns != null && !columns.isEmpty()) { - SourceLine paragraph = paragraphLines.get(0); + SourceLine paragraph = paragraphLines.get(paragraphLines.size() - 1); List headerCells = split(paragraph); if (columns.size() >= headerCells.size()) { return BlockStart.of(new TableBlockParser(columns, paragraph)) .atIndex(state.getIndex()) - .replaceActiveBlockParser(); + .replaceParagraphLines(1); } } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java index 7c9f74fec43..c9939070ebe 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableHtmlNodeRenderer.java @@ -37,7 +37,6 @@ import jdk.internal.org.commonmark.renderer.html.HtmlNodeRendererContext; import jdk.internal.org.commonmark.renderer.html.HtmlWriter; -import java.util.Collections; import java.util.Map; public class TableHtmlNodeRenderer extends TableNodeRenderer { @@ -50,6 +49,7 @@ public TableHtmlNodeRenderer(HtmlNodeRendererContext context) { this.context = context; } + @Override protected void renderBlock(TableBlock tableBlock) { htmlWriter.line(); htmlWriter.tag("table", getAttributes(tableBlock, "table")); @@ -58,6 +58,7 @@ protected void renderBlock(TableBlock tableBlock) { htmlWriter.line(); } + @Override protected void renderHead(TableHead tableHead) { htmlWriter.line(); htmlWriter.tag("thead", getAttributes(tableHead, "thead")); @@ -66,6 +67,7 @@ protected void renderHead(TableHead tableHead) { htmlWriter.line(); } + @Override protected void renderBody(TableBody tableBody) { htmlWriter.line(); htmlWriter.tag("tbody", getAttributes(tableBody, "tbody")); @@ -74,6 +76,7 @@ protected void renderBody(TableBody tableBody) { htmlWriter.line(); } + @Override protected void renderRow(TableRow tableRow) { htmlWriter.line(); htmlWriter.tag("tr", getAttributes(tableRow, "tr")); @@ -82,6 +85,7 @@ protected void renderRow(TableRow tableRow) { htmlWriter.line(); } + @Override protected void renderCell(TableCell tableCell) { String tagName = tableCell.isHeader() ? "th" : "td"; htmlWriter.line(); @@ -92,14 +96,14 @@ protected void renderCell(TableCell tableCell) { } private Map getAttributes(Node node, String tagName) { - return context.extendAttributes(node, tagName, Collections.emptyMap()); + return context.extendAttributes(node, tagName, Map.of()); } private Map getCellAttributes(TableCell tableCell, String tagName) { if (tableCell.getAlignment() != null) { - return context.extendAttributes(tableCell, tagName, Collections.singletonMap("align", getAlignValue(tableCell.getAlignment()))); + return context.extendAttributes(tableCell, tagName, Map.of("align", getAlignValue(tableCell.getAlignment()))); } else { - return context.extendAttributes(tableCell, tagName, Collections.emptyMap()); + return context.extendAttributes(tableCell, tagName, Map.of()); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java index 00712579a5b..e30801dfc1f 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableMarkdownNodeRenderer.java @@ -34,7 +34,6 @@ import jdk.internal.org.commonmark.ext.gfm.tables.*; import jdk.internal.org.commonmark.node.Node; -import jdk.internal.org.commonmark.renderer.NodeRenderer; import jdk.internal.org.commonmark.renderer.markdown.MarkdownNodeRendererContext; import jdk.internal.org.commonmark.renderer.markdown.MarkdownWriter; import jdk.internal.org.commonmark.text.AsciiMatcher; @@ -45,7 +44,7 @@ /** * The Table node renderer that is needed for rendering GFM tables (GitHub Flavored Markdown) to text content. */ -public class TableMarkdownNodeRenderer extends TableNodeRenderer implements NodeRenderer { +public class TableMarkdownNodeRenderer extends TableNodeRenderer { private final MarkdownWriter writer; private final MarkdownNodeRendererContext context; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java index c7b6beabb0d..f4e93e540ad 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableNodeRenderer.java @@ -32,29 +32,23 @@ package jdk.internal.org.commonmark.ext.gfm.tables.internal; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import jdk.internal.org.commonmark.ext.gfm.tables.TableBlock; -import jdk.internal.org.commonmark.ext.gfm.tables.TableBody; -import jdk.internal.org.commonmark.ext.gfm.tables.TableCell; -import jdk.internal.org.commonmark.ext.gfm.tables.TableHead; -import jdk.internal.org.commonmark.ext.gfm.tables.TableRow; +import jdk.internal.org.commonmark.ext.gfm.tables.*; import jdk.internal.org.commonmark.node.Node; import jdk.internal.org.commonmark.renderer.NodeRenderer; +import java.util.Set; + abstract class TableNodeRenderer implements NodeRenderer { @Override public Set> getNodeTypes() { - return new HashSet<>(Arrays.asList( + return Set.of( TableBlock.class, TableHead.class, TableBody.class, TableRow.class, TableCell.class - )); + ); } @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java index 3b618474bce..80d9af6c87e 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/ext/gfm/tables/internal/TableTextContentNodeRenderer.java @@ -54,49 +54,46 @@ public TableTextContentNodeRenderer(TextContentNodeRendererContext context) { this.context = context; } + @Override protected void renderBlock(TableBlock tableBlock) { + // Render rows tight + textContentWriter.pushTight(true); renderChildren(tableBlock); - if (tableBlock.getNext() != null) { - textContentWriter.write("\n"); - } + textContentWriter.popTight(); + textContentWriter.block(); } + @Override protected void renderHead(TableHead tableHead) { renderChildren(tableHead); } + @Override protected void renderBody(TableBody tableBody) { renderChildren(tableBody); } + @Override protected void renderRow(TableRow tableRow) { - textContentWriter.line(); renderChildren(tableRow); - textContentWriter.line(); + textContentWriter.block(); } + @Override protected void renderCell(TableCell tableCell) { renderChildren(tableCell); - textContentWriter.write('|'); - textContentWriter.whitespace(); - } - - private void renderLastCell(TableCell tableCell) { - renderChildren(tableCell); + // For the last cell in row, don't render the delimiter + if (tableCell.getNext() != null) { + textContentWriter.write('|'); + textContentWriter.whitespace(); + } } private void renderChildren(Node parent) { Node node = parent.getFirstChild(); while (node != null) { Node next = node.getNext(); - - // For last cell in row, we dont render the delimiter. - if (node instanceof TableCell && next == null) { - renderLastCell((TableCell) node); - } else { - context.render(node); - } - + context.render(node); node = next; } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java index db6f81976b4..ffa428ebe16 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockQuoteParser.java @@ -78,6 +78,7 @@ private static boolean isMarker(ParserState state, int index) { } public static class Factory extends AbstractBlockParserFactory { + @Override public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) { int nextNonSpace = state.getNextNonSpaceIndex(); if (isMarker(state, nextNonSpace)) { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java index bde28136b82..a81df3bdf4a 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/BlockStartImpl.java @@ -41,6 +41,7 @@ public class BlockStartImpl extends BlockStart { private int newIndex = -1; private int newColumn = -1; private boolean replaceActiveBlockParser = false; + private int replaceParagraphLines = 0; public BlockStartImpl(BlockParser... blockParsers) { this.blockParsers = blockParsers; @@ -62,6 +63,10 @@ public boolean isReplaceActiveBlockParser() { return replaceActiveBlockParser; } + int getReplaceParagraphLines() { + return replaceParagraphLines; + } + @Override public BlockStart atIndex(int newIndex) { this.newIndex = newIndex; @@ -75,9 +80,18 @@ public BlockStart atColumn(int newColumn) { } @Override + @Deprecated public BlockStart replaceActiveBlockParser() { this.replaceActiveBlockParser = true; return this; } + @Override + public BlockStart replaceParagraphLines(int lines) { + if (!(lines >= 1)) { + throw new IllegalArgumentException("Lines must be >= 1"); + } + this.replaceParagraphLines = lines; + return this; + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java index 373d3ac1663..da50234bd39 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Bracket.java @@ -36,26 +36,34 @@ import jdk.internal.org.commonmark.parser.beta.Position; /** - * Opening bracket for links ([) or images (![). + * Opening bracket for links ({@code [}), images ({@code ![}), or links with other markers. */ public class Bracket { - public final Text node; + /** + * The node of a marker such as {@code !} if present, null otherwise. + */ + public final Text markerNode; /** - * The position of the marker for the bracket ([ or ![) + * The position of the marker if present, null otherwise. */ public final Position markerPosition; /** - * The position of the content (after the opening bracket) + * The node of {@code [}. */ - public final Position contentPosition; + public final Text bracketNode; /** - * Whether this is an image or link. + * The position of {@code [}. */ - public final boolean image; + public final Position bracketPosition; + + /** + * The position of the content (after the opening bracket) + */ + public final Position contentPosition; /** * Previous bracket. @@ -73,23 +81,24 @@ public class Bracket { public boolean allowed = true; /** - * Whether there is an unescaped bracket (opening or closing) anywhere after this opening bracket. + * Whether there is an unescaped bracket (opening or closing) after this opening bracket in the text parsed so far. */ public boolean bracketAfter = false; - static public Bracket link(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) { - return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, false); + static public Bracket link(Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) { + return new Bracket(null, null, bracketNode, bracketPosition, contentPosition, previous, previousDelimiter); } - static public Bracket image(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) { - return new Bracket(node, markerPosition, contentPosition, previous, previousDelimiter, true); + static public Bracket withMarker(Text markerNode, Position markerPosition, Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) { + return new Bracket(markerNode, markerPosition, bracketNode, bracketPosition, contentPosition, previous, previousDelimiter); } - private Bracket(Text node, Position markerPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter, boolean image) { - this.node = node; + private Bracket(Text markerNode, Position markerPosition, Text bracketNode, Position bracketPosition, Position contentPosition, Bracket previous, Delimiter previousDelimiter) { + this.markerNode = markerNode; this.markerPosition = markerPosition; + this.bracketNode = bracketNode; + this.bracketPosition = bracketPosition; this.contentPosition = contentPosition; - this.image = image; this.previous = previous; this.previousDelimiter = previousDelimiter; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Definitions.java similarity index 63% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Definitions.java index e1def1de98e..d84187c1d19 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitions.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/Definitions.java @@ -32,28 +32,35 @@ package jdk.internal.org.commonmark.internal; -import jdk.internal.org.commonmark.internal.util.Escaping; -import jdk.internal.org.commonmark.node.LinkReferenceDefinition; +import jdk.internal.org.commonmark.node.DefinitionMap; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.Map; -public class LinkReferenceDefinitions { +public class Definitions { - // LinkedHashMap for determinism and to preserve document order - private final Map definitions = new LinkedHashMap<>(); + private final Map, DefinitionMap> definitionsByType = new HashMap<>(); - public void add(LinkReferenceDefinition definition) { - String normalizedLabel = Escaping.normalizeLabelContent(definition.getLabel()); + public void addDefinitions(DefinitionMap definitionMap) { + var existingMap = getMap(definitionMap.getType()); + if (existingMap == null) { + definitionsByType.put(definitionMap.getType(), definitionMap); + } else { + existingMap.addAll(definitionMap); + } + } - // spec: When there are multiple matching link reference definitions, the first is used - if (!definitions.containsKey(normalizedLabel)) { - definitions.put(normalizedLabel, definition); + public V getDefinition(Class type, String label) { + var definitionMap = getMap(type); + if (definitionMap == null) { + return null; } + return definitionMap.get(label); } - public LinkReferenceDefinition get(String label) { - String normalizedLabel = Escaping.normalizeLabelContent(label); - return definitions.get(normalizedLabel); + @SuppressWarnings("unchecked") + private DefinitionMap getMap(Class type) { + //noinspection unchecked + return (DefinitionMap) definitionsByType.get(type); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java index 8f18feed1a0..74a6dd38ba2 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentBlockParser.java @@ -34,7 +34,6 @@ import jdk.internal.org.commonmark.node.Block; import jdk.internal.org.commonmark.node.Document; -import jdk.internal.org.commonmark.parser.SourceLine; import jdk.internal.org.commonmark.parser.block.AbstractBlockParser; import jdk.internal.org.commonmark.parser.block.BlockContinue; import jdk.internal.org.commonmark.parser.block.ParserState; @@ -63,8 +62,4 @@ public BlockContinue tryContinue(ParserState state) { return BlockContinue.atIndex(state.getIndex()); } - @Override - public void addLine(SourceLine line) { - } - } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java index 34ac293d891..f80a62cc105 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/DocumentParser.java @@ -32,21 +32,26 @@ package jdk.internal.org.commonmark.internal; +import jdk.internal.org.commonmark.internal.util.LineReader; import jdk.internal.org.commonmark.internal.util.Parsing; import jdk.internal.org.commonmark.node.*; -import jdk.internal.org.commonmark.parser.*; +import jdk.internal.org.commonmark.parser.IncludeSourceSpans; +import jdk.internal.org.commonmark.parser.InlineParserFactory; +import jdk.internal.org.commonmark.parser.SourceLine; +import jdk.internal.org.commonmark.parser.SourceLines; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; +import jdk.internal.org.commonmark.parser.beta.InlineContentParserFactory; import jdk.internal.org.commonmark.parser.block.*; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import jdk.internal.org.commonmark.text.Characters; -import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.*; public class DocumentParser implements ParserState { - private static final Set> CORE_FACTORY_TYPES = new LinkedHashSet<>(Arrays.asList( + private static final Set> CORE_FACTORY_TYPES = new LinkedHashSet<>(List.of( BlockQuote.class, Heading.class, FencedCodeBlock.class, @@ -98,20 +103,30 @@ public class DocumentParser implements ParserState { private final List blockParserFactories; private final InlineParserFactory inlineParserFactory; + private final List inlineContentParserFactories; private final List delimiterProcessors; + private final List linkProcessors; + private final Set linkMarkers; private final IncludeSourceSpans includeSourceSpans; + private final int maxOpenBlockParsers; private final DocumentBlockParser documentBlockParser; - private final LinkReferenceDefinitions definitions = new LinkReferenceDefinitions(); + private final Definitions definitions = new Definitions(); private final List openBlockParsers = new ArrayList<>(); private final List allBlockParsers = new ArrayList<>(); public DocumentParser(List blockParserFactories, InlineParserFactory inlineParserFactory, - List delimiterProcessors, IncludeSourceSpans includeSourceSpans) { + List inlineContentParserFactories, List delimiterProcessors, + List linkProcessors, Set linkMarkers, + IncludeSourceSpans includeSourceSpans, int maxOpenBlockParsers) { this.blockParserFactories = blockParserFactories; this.inlineParserFactory = inlineParserFactory; + this.inlineContentParserFactories = inlineContentParserFactories; this.delimiterProcessors = delimiterProcessors; + this.linkProcessors = linkProcessors; + this.linkMarkers = linkMarkers; this.includeSourceSpans = includeSourceSpans; + this.maxOpenBlockParsers = maxOpenBlockParsers; this.documentBlockParser = new DocumentBlockParser(); activateBlockParser(new OpenBlockParser(documentBlockParser, 0)); @@ -122,9 +137,8 @@ public static Set> getDefaultBlockParserTypes() { } public static List calculateBlockParserFactories(List customBlockParserFactories, Set> enabledBlockTypes) { - List list = new ArrayList<>(); // By having the custom factories come first, extensions are able to change behavior of core syntax. - list.addAll(customBlockParserFactories); + List list = new ArrayList<>(customBlockParserFactories); for (Class blockType : enabledBlockTypes) { list.add(NODES_TO_CORE_FACTORIES.get(blockType)); } @@ -147,32 +161,32 @@ public Document parse(String input) { int lineBreak; while ((lineBreak = Characters.findLineBreak(input, lineStart)) != -1) { String line = input.substring(lineStart, lineBreak); - parseLine(line); + parseLine(line, lineStart); if (lineBreak + 1 < input.length() && input.charAt(lineBreak) == '\r' && input.charAt(lineBreak + 1) == '\n') { lineStart = lineBreak + 2; } else { lineStart = lineBreak + 1; } } - if (input.length() > 0 && (lineStart == 0 || lineStart < input.length())) { + if (!input.isEmpty() && (lineStart == 0 || lineStart < input.length())) { String line = input.substring(lineStart); - parseLine(line); + parseLine(line, lineStart); } return finalizeAndProcess(); } public Document parse(Reader input) throws IOException { - BufferedReader bufferedReader; - if (input instanceof BufferedReader) { - bufferedReader = (BufferedReader) input; - } else { - bufferedReader = new BufferedReader(input); - } - + var lineReader = new LineReader(input); + int inputIndex = 0; String line; - while ((line = bufferedReader.readLine()) != null) { - parseLine(line); + while ((line = lineReader.readLine()) != null) { + parseLine(line, inputIndex); + inputIndex += line.length(); + var eol = lineReader.getLineTerminator(); + if (eol != null) { + inputIndex += eol.length(); + } } return finalizeAndProcess(); @@ -217,8 +231,8 @@ public BlockParser getActiveBlockParser() { * Analyze a line of text and update the document appropriately. We parse markdown text by calling this on each * line of input, then finalizing the document. */ - private void parseLine(CharSequence ln) { - setLine(ln); + private void parseLine(String ln, int inputIndex) { + setLine(ln, inputIndex); // For each containing block, try to parse the associated line start. // The document will always match, so we can skip the first block parser and start at 1 matches @@ -290,9 +304,15 @@ private void parseLine(CharSequence ln) { } List replacedSourceSpans = null; - if (blockStart.isReplaceActiveBlockParser()) { - Block replacedBlock = prepareActiveBlockParserForReplacement(); - replacedSourceSpans = replacedBlock.getSourceSpans(); + if (blockStart.getReplaceParagraphLines() >= 1 || blockStart.isReplaceActiveBlockParser()) { + var activeBlockParser = getActiveBlockParser(); + if (activeBlockParser instanceof ParagraphParser) { + var paragraphParser = (ParagraphParser) activeBlockParser; + var lines = blockStart.isReplaceActiveBlockParser() ? Integer.MAX_VALUE : blockStart.getReplaceParagraphLines(); + replacedSourceSpans = replaceParagraphLines(lines, paragraphParser); + } else if (blockStart.isReplaceActiveBlockParser()) { + replacedSourceSpans = prepareActiveBlockParserForReplacement(activeBlockParser); + } } for (BlockParser newBlockParser : blockStart.getBlockParsers()) { @@ -308,7 +328,7 @@ private void parseLine(CharSequence ln) { // What remains at the offset is a text line. Add the text to the // appropriate block. - // First check for a lazy paragraph continuation: + // First check for a lazy continuation line if (!startedNewBlock && !isBlank() && getActiveBlockParser().canHaveLazyContinuationLines()) { openBlockParsers.get(openBlockParsers.size() - 1).sourceIndex = lastIndex; @@ -342,16 +362,16 @@ private void parseLine(CharSequence ln) { } } - private void setLine(CharSequence ln) { + private void setLine(String ln, int inputIndex) { lineIndex++; index = 0; column = 0; columnIsInTab = false; - CharSequence lineContent = prepareLine(ln); + String lineContent = prepareLine(ln); SourceSpan sourceSpan = null; if (includeSourceSpans != IncludeSourceSpans.NONE) { - sourceSpan = SourceSpan.of(lineIndex, 0, lineContent.length()); + sourceSpan = SourceSpan.of(lineIndex, 0, inputIndex, lineContent.length()); } this.line = SourceLine.of(lineContent, sourceSpan); } @@ -450,10 +470,9 @@ private void addLine() { content = line.getContent().subSequence(index, line.getContent().length()); } SourceSpan sourceSpan = null; - if (includeSourceSpans == IncludeSourceSpans.BLOCKS_AND_INLINES) { - // Note that if we're in a partially-consumed tab, the length here corresponds to the content but not to the - // actual source length. That sounds like a problem, but I haven't found a test case where it matters (yet). - sourceSpan = SourceSpan.of(lineIndex, index, content.length()); + if (includeSourceSpans == IncludeSourceSpans.BLOCKS_AND_INLINES && index < line.getSourceSpan().getLength()) { + // Note that if we're in a partially-consumed tab the length of the source span and the content don't match. + sourceSpan = line.getSourceSpan().subSpan(index); } getActiveBlockParser().addLine(SourceLine.of(content, sourceSpan)); addSourceSpans(); @@ -461,19 +480,24 @@ private void addLine() { private void addSourceSpans() { if (includeSourceSpans != IncludeSourceSpans.NONE) { - // Don't add source spans for Document itself (it would get the whole source text) + // Don't add source spans for Document itself (it would get the whole source text), so start at 1, not 0 for (int i = 1; i < openBlockParsers.size(); i++) { - OpenBlockParser openBlockParser = openBlockParsers.get(i); - int blockIndex = openBlockParser.sourceIndex; + var openBlockParser = openBlockParsers.get(i); + // In case of a lazy continuation line, the index is less than where the block parser would expect the + // contents to start, so let's use whichever is smaller. + int blockIndex = Math.min(openBlockParser.sourceIndex, index); int length = line.getContent().length() - blockIndex; if (length != 0) { - openBlockParser.blockParser.addSourceSpan(SourceSpan.of(lineIndex, blockIndex, length)); + openBlockParser.blockParser.addSourceSpan(line.getSourceSpan().subSpan(blockIndex)); } } } } private BlockStartImpl findBlockStart(BlockParser blockParser) { + if (openBlockParsers.size() > maxOpenBlockParsers) { + return null; + } MatchedBlockParser matchedBlockParser = new MatchedBlockParserImpl(blockParser); for (BlockParserFactory blockParserFactory : blockParserFactories) { BlockStart result = blockParserFactory.tryStart(this, matchedBlockParser); @@ -484,35 +508,14 @@ private BlockStartImpl findBlockStart(BlockParser blockParser) { return null; } - /** - * Finalize a block. Close it and do any necessary postprocessing, e.g. setting the content of blocks and - * collecting link reference definitions from paragraphs. - */ - private void finalize(BlockParser blockParser) { - if (blockParser instanceof ParagraphParser) { - addDefinitionsFrom((ParagraphParser) blockParser); - } - - blockParser.closeBlock(); - } - - private void addDefinitionsFrom(ParagraphParser paragraphParser) { - for (LinkReferenceDefinition definition : paragraphParser.getDefinitions()) { - // Add nodes into document before paragraph. - paragraphParser.getBlock().insertBefore(definition); - - definitions.add(definition); - } - } - /** * Walk through a block & children recursively, parsing string content into inline content where appropriate. */ private void processInlines() { - InlineParserContextImpl context = new InlineParserContextImpl(delimiterProcessors, definitions); - InlineParser inlineParser = inlineParserFactory.create(context); + var context = new InlineParserContextImpl(inlineContentParserFactories, delimiterProcessors, linkProcessors, linkMarkers, definitions); + var inlineParser = inlineParserFactory.create(context); - for (BlockParser blockParser : allBlockParsers) { + for (var blockParser : allBlockParsers) { blockParser.parseInlines(inlineParser); } } @@ -538,24 +541,23 @@ private OpenBlockParser deactivateBlockParser() { return openBlockParsers.remove(openBlockParsers.size() - 1); } - private Block prepareActiveBlockParserForReplacement() { - // Note that we don't want to parse inlines, as it's getting replaced. - BlockParser old = deactivateBlockParser().blockParser; + private List replaceParagraphLines(int lines, ParagraphParser paragraphParser) { + // Remove lines from paragraph as the new block is using them. + // If all lines are used, this also unlinks the Paragraph block. + var sourceSpans = paragraphParser.removeLines(lines); + // Close the paragraph block parser, which will finalize it. + closeBlockParsers(1); + return sourceSpans; + } - if (old instanceof ParagraphParser) { - ParagraphParser paragraphParser = (ParagraphParser) old; - // Collect any link reference definitions. Note that replacing the active block parser is done after a - // block parser got the current paragraph content using MatchedBlockParser#getContentString. In case the - // paragraph started with link reference definitions, we parse and strip them before the block parser gets - // the content. We want to keep them. - // If no replacement happens, we collect the definitions as part of finalizing paragraph blocks. - addDefinitionsFrom(paragraphParser); - } + private List prepareActiveBlockParserForReplacement(BlockParser blockParser) { + // Note that we don't want to parse inlines here, as it's getting replaced. + deactivateBlockParser(); // Do this so that source positions are calculated, which we will carry over to the replacing block. - old.closeBlock(); - old.getBlock().unlink(); - return old.getBlock(); + blockParser.closeBlock(); + blockParser.getBlock().unlink(); + return blockParser.getBlock().getSourceSpans(); } private Document finalizeAndProcess() { @@ -576,31 +578,28 @@ private void closeBlockParsers(int count) { } /** - * Prepares the input line replacing {@code \0} + * Finalize a block. Close it and do any necessary postprocessing, e.g. setting the content of blocks and + * collecting link reference definitions from paragraphs. */ - private static CharSequence prepareLine(CharSequence line) { - // Avoid building a new string in the majority of cases (no \0) - StringBuilder sb = null; - int length = line.length(); - for (int i = 0; i < length; i++) { - char c = line.charAt(i); - if (c == '\0') { - if (sb == null) { - sb = new StringBuilder(length); - sb.append(line, 0, i); - } - sb.append('\uFFFD'); - } else { - if (sb != null) { - sb.append(c); - } - } + private void finalize(BlockParser blockParser) { + addDefinitionsFrom(blockParser); + blockParser.closeBlock(); + } + + private void addDefinitionsFrom(BlockParser blockParser) { + for (var definitionMap : blockParser.getDefinitions()) { + definitions.addDefinitions(definitionMap); } + } - if (sb != null) { - return sb.toString(); - } else { + /** + * Prepares the input line replacing {@code \0} + */ + private static String prepareLine(String line) { + if (line.indexOf('\0') == -1) { return line; + } else { + return line.replace('\0', '\uFFFD'); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java index cea8cb10366..bbd21b0ec92 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/HeadingParser.java @@ -92,7 +92,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar if (!paragraph.isEmpty()) { return BlockStart.of(new HeadingParser(setextHeadingLevel, paragraph)) .atIndex(line.getContent().length()) - .replaceActiveBlockParser(); + .replaceParagraphLines(paragraph.getLines().size()); } } @@ -101,7 +101,7 @@ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockPar } // spec: An ATX heading consists of a string of characters, parsed as inline content, between an opening sequence of - // 1\u20136 unescaped # characters and an optional closing sequence of any number of unescaped # characters. The opening + // 1-6 unescaped # characters and an optional closing sequence of any number of unescaped # characters. The opening // sequence of # characters must be followed by a space or by the end of line. The optional closing sequence of #s // must be preceded by a space and may be followed by spaces only. private static HeadingParser getAtxHeading(SourceLine line) { @@ -172,10 +172,12 @@ private static HeadingParser getAtxHeading(SourceLine line) { if (isSetextHeadingRest(line, index + 1, '=')) { return 1; } + break; case '-': if (isSetextHeadingRest(line, index + 1, '-')) { return 2; } + break; } return 0; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java index dab02aeedd1..d0cb6ac66de 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserContextImpl.java @@ -34,20 +34,36 @@ import jdk.internal.org.commonmark.node.LinkReferenceDefinition; import jdk.internal.org.commonmark.parser.InlineParserContext; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; +import jdk.internal.org.commonmark.parser.beta.InlineContentParserFactory; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import java.util.List; -import java.util.Map; +import java.util.Set; public class InlineParserContextImpl implements InlineParserContext { + private final List inlineContentParserFactories; private final List delimiterProcessors; - private final LinkReferenceDefinitions linkReferenceDefinitions; + private final List linkProcessors; + private final Set linkMarkers; + private final Definitions definitions; - public InlineParserContextImpl(List delimiterProcessors, - LinkReferenceDefinitions linkReferenceDefinitions) { + public InlineParserContextImpl(List inlineContentParserFactories, + List delimiterProcessors, + List linkProcessors, + Set linkMarkers, + Definitions definitions) { + this.inlineContentParserFactories = inlineContentParserFactories; this.delimiterProcessors = delimiterProcessors; - this.linkReferenceDefinitions = linkReferenceDefinitions; + this.linkProcessors = linkProcessors; + this.linkMarkers = linkMarkers; + this.definitions = definitions; + } + + @Override + public List getCustomInlineContentParserFactories() { + return inlineContentParserFactories; } @Override @@ -56,7 +72,23 @@ public List getCustomDelimiterProcessors() { } @Override + public List getCustomLinkProcessors() { + return linkProcessors; + } + + @Override + public Set getCustomLinkMarkers() { + return linkMarkers; + } + + @Override + @Deprecated public LinkReferenceDefinition getLinkReferenceDefinition(String label) { - return linkReferenceDefinitions.get(label); + return definitions.getDefinition(LinkReferenceDefinition.class, label); + } + + @Override + public D getDefinition(Class type, String label) { + return definitions.getDefinition(type, label); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java index 3c0913a3206..65632e43c8d 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/InlineParserImpl.java @@ -39,8 +39,8 @@ import jdk.internal.org.commonmark.parser.InlineParser; import jdk.internal.org.commonmark.parser.InlineParserContext; import jdk.internal.org.commonmark.parser.SourceLines; -import jdk.internal.org.commonmark.parser.beta.Position; import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import jdk.internal.org.commonmark.text.Characters; @@ -48,11 +48,14 @@ public class InlineParserImpl implements InlineParser, InlineParserState { - private final BitSet specialCharacters; - private final Map delimiterProcessors; private final InlineParserContext context; - private final Map> inlineParsers; + private final List inlineContentParserFactories; + private final Map delimiterProcessors; + private final List linkProcessors; + private final BitSet specialCharacters; + private final BitSet linkMarkers; + private Map> inlineParsers; private Scanner scanner; private boolean includeSourceSpans; private int trailingSpaces; @@ -68,46 +71,40 @@ public class InlineParserImpl implements InlineParser, InlineParserState { */ private Bracket lastBracket; - public InlineParserImpl(InlineParserContext inlineParserContext) { - this.delimiterProcessors = calculateDelimiterProcessors(inlineParserContext.getCustomDelimiterProcessors()); - - this.context = inlineParserContext; - this.inlineParsers = new HashMap<>(); - this.inlineParsers.put('\\', Collections.singletonList(new BackslashInlineParser())); - this.inlineParsers.put('`', Collections.singletonList(new BackticksInlineParser())); - this.inlineParsers.put('&', Collections.singletonList(new EntityInlineParser())); - this.inlineParsers.put('<', Arrays.asList(new AutolinkInlineParser(), new HtmlInlineParser())); + public InlineParserImpl(InlineParserContext context) { + this.context = context; + this.inlineContentParserFactories = calculateInlineContentParserFactories(context.getCustomInlineContentParserFactories()); + this.delimiterProcessors = calculateDelimiterProcessors(context.getCustomDelimiterProcessors()); + this.linkProcessors = calculateLinkProcessors(context.getCustomLinkProcessors()); + this.linkMarkers = calculateLinkMarkers(context.getCustomLinkMarkers()); + this.specialCharacters = calculateSpecialCharacters(linkMarkers, this.delimiterProcessors.keySet(), this.inlineContentParserFactories); + } - this.specialCharacters = calculateSpecialCharacters(this.delimiterProcessors.keySet(), inlineParsers.keySet()); + private List calculateInlineContentParserFactories(List customFactories) { + // Custom parsers can override built-in parsers if they want, so make sure they are tried first + var list = new ArrayList<>(customFactories); + list.add(new BackslashInlineParser.Factory()); + list.add(new BackticksInlineParser.Factory()); + list.add(new EntityInlineParser.Factory()); + list.add(new AutolinkInlineParser.Factory()); + list.add(new HtmlInlineParser.Factory()); + return list; } - public static BitSet calculateSpecialCharacters(Set delimiterCharacters, Set characters) { - BitSet bitSet = new BitSet(); - for (Character c : delimiterCharacters) { - bitSet.set(c); - } - for (Character c : characters) { - bitSet.set(c); - } - bitSet.set('['); - bitSet.set(']'); - bitSet.set('!'); - bitSet.set('\n'); - return bitSet; + private List calculateLinkProcessors(List linkProcessors) { + // Custom link processors can override the built-in behavior, so make sure they are tried first + var list = new ArrayList<>(linkProcessors); + list.add(new CoreLinkProcessor()); + return list; } - public static Map calculateDelimiterProcessors(List delimiterProcessors) { - Map map = new HashMap<>(); - addDelimiterProcessors(Arrays.asList(new AsteriskDelimiterProcessor(), new UnderscoreDelimiterProcessor()), map); + private static Map calculateDelimiterProcessors(List delimiterProcessors) { + var map = new HashMap(); + addDelimiterProcessors(List.of(new AsteriskDelimiterProcessor(), new UnderscoreDelimiterProcessor()), map); addDelimiterProcessors(delimiterProcessors, map); return map; } - @Override - public Scanner scanner() { - return scanner; - } - private static void addDelimiterProcessors(Iterable delimiterProcessors, Map map) { for (DelimiterProcessor delimiterProcessor : delimiterProcessors) { char opening = delimiterProcessor.getOpeningCharacter(); @@ -141,6 +138,50 @@ private static void addDelimiterProcessorForChar(char delimiterChar, DelimiterPr } } + private static BitSet calculateLinkMarkers(Set linkMarkers) { + var bitSet = new BitSet(); + for (var c : linkMarkers) { + bitSet.set(c); + } + bitSet.set('!'); + return bitSet; + } + + private static BitSet calculateSpecialCharacters(BitSet linkMarkers, + Set delimiterCharacters, + List inlineContentParserFactories) { + BitSet bitSet = (BitSet) linkMarkers.clone(); + for (Character c : delimiterCharacters) { + bitSet.set(c); + } + for (var factory : inlineContentParserFactories) { + for (var c : factory.getTriggerCharacters()) { + bitSet.set(c); + } + } + bitSet.set('['); + bitSet.set(']'); + bitSet.set('!'); + bitSet.set('\n'); + return bitSet; + } + + private Map> createInlineContentParsers() { + var map = new HashMap>(); + for (var factory : inlineContentParserFactories) { + var parser = factory.create(); + for (var c : factory.getTriggerCharacters()) { + map.computeIfAbsent(c, k -> new ArrayList<>()).add(parser); + } + } + return map; + } + + @Override + public Scanner scanner() { + return scanner; + } + /** * Parse content in block into inline children, appending them to the block node. */ @@ -149,14 +190,13 @@ public void parse(SourceLines lines, Node block) { reset(lines); while (true) { - List nodes = parseInline(); - if (nodes != null) { - for (Node node : nodes) { - block.appendChild(node); - } - } else { + var nodes = parseInline(); + if (nodes == null) { break; } + for (Node node : nodes) { + block.appendChild(node); + } } processDelimiters(null); @@ -169,6 +209,7 @@ void reset(SourceLines lines) { this.trailingSpaces = 0; this.lastDelimiter = null; this.lastBracket = null; + this.inlineParsers = createInlineContentParsers(); } private Text text(SourceLines sourceLines) { @@ -187,20 +228,28 @@ private List parseInline() { switch (c) { case '[': - return Collections.singletonList(parseOpenBracket()); - case '!': - return Collections.singletonList(parseBang()); + return List.of(parseOpenBracket()); case ']': - return Collections.singletonList(parseCloseBracket()); + return List.of(parseCloseBracket()); case '\n': - return Collections.singletonList(parseLineBreak()); + return List.of(parseLineBreak()); case Scanner.END: return null; } + if (linkMarkers.get(c)) { + var markerPosition = scanner.position(); + var nodes = parseLinkMarker(); + if (nodes != null) { + return nodes; + } + // Reset and try other things (e.g. inline parsers below) + scanner.setPosition(markerPosition); + } + // No inline parser, delimiter or other special handling. if (!specialCharacters.get(c)) { - return Collections.singletonList(parseText()); + return List.of(parseText()); } List inlineParsers = this.inlineParsers.get(c); @@ -215,7 +264,7 @@ private List parseInline() { if (includeSourceSpans && node.getSourceSpans().isEmpty()) { node.setSourceSpans(scanner.getSource(position, scanner.position()).getSourceSpans()); } - return Collections.singletonList(node); + return List.of(node); } else { // Reset position scanner.setPosition(position); @@ -232,7 +281,7 @@ private List parseInline() { } // If we get here, even for a special/delimiter character, we will just treat it as text. - return Collections.singletonList(parseText()); + return List.of(parseText()); } /** @@ -272,21 +321,23 @@ private Node parseOpenBracket() { } /** - * If next character is [, and ! delimiter to delimiter stack and add a text node to block's children. - * Otherwise just add a text node. + * If next character is {@code [}, add a bracket to the stack. + * Otherwise, return null. */ - private Node parseBang() { - Position start = scanner.position(); + private List parseLinkMarker() { + var markerPosition = scanner.position(); scanner.next(); + var bracketPosition = scanner.position(); if (scanner.next('[')) { - Position contentPosition = scanner.position(); - Text node = text(scanner.getSource(start, contentPosition)); + var contentPosition = scanner.position(); + var bangNode = text(scanner.getSource(markerPosition, bracketPosition)); + var bracketNode = text(scanner.getSource(bracketPosition, contentPosition)); // Add entry to stack for this opener - addBracket(Bracket.image(node, start, contentPosition, lastBracket, lastDelimiter)); - return node; + addBracket(Bracket.withMarker(bangNode, markerPosition, bracketNode, bracketPosition, contentPosition, lastBracket, lastDelimiter)); + return List.of(bangNode, bracketNode); } else { - return text(scanner.getSource(start, scanner.position())); + return null; } } @@ -307,107 +358,158 @@ private Node parseCloseBracket() { } if (!opener.allowed) { - // Matching opener but it's not allowed, just return a literal. + // Matching opener, but it's not allowed, just return a literal. removeLastBracket(); return text(scanner.getSource(beforeClose, afterClose)); } - // Check to see if we have a link/image - String dest = null; - String title = null; + var linkOrImage = parseLinkOrImage(opener, beforeClose); + if (linkOrImage != null) { + return linkOrImage; + } + scanner.setPosition(afterClose); - // Maybe a inline link like `[foo](/uri "title")` - if (scanner.next('(')) { - scanner.whitespace(); - dest = parseLinkDestination(scanner); - if (dest == null) { - scanner.setPosition(afterClose); - } else { - int whitespace = scanner.whitespace(); - // title needs a whitespace before - if (whitespace >= 1) { - title = parseLinkTitle(scanner); - scanner.whitespace(); - } - if (!scanner.next(')')) { - // Don't have a closing `)`, so it's not a destination and title -> reset. - // Note that something like `[foo](` could be valid, `(` will just be text. - scanner.setPosition(afterClose); - dest = null; - title = null; - } - } + // Nothing parsed, just parse the bracket as text and continue + removeLastBracket(); + return text(scanner.getSource(beforeClose, afterClose)); + } + + private Node parseLinkOrImage(Bracket opener, Position beforeClose) { + var linkInfo = parseLinkInfo(opener, beforeClose); + if (linkInfo == null) { + return null; } + var processorStartPosition = scanner.position(); - // Maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]`. - // Note that even `[foo](` could be a valid link if there's a reference, which is why this is not just an `else` - // here. - if (dest == null) { - // See if there's a link label like `[bar]` or `[]` - String ref = parseLinkLabel(scanner); - if (ref == null) { - scanner.setPosition(afterClose); - } - if ((ref == null || ref.isEmpty()) && !opener.bracketAfter) { - // If the second label is empty `[foo][]` or missing `[foo]`, then the first label is the reference. - // But it can only be a reference when there's no (unescaped) bracket in it. - // If there is, we don't even need to try to look up the reference. This is an optimization. - ref = scanner.getSource(opener.contentPosition, beforeClose).getContent(); + for (var linkProcessor : linkProcessors) { + var linkResult = linkProcessor.process(linkInfo, scanner, context); + if (!(linkResult instanceof LinkResultImpl)) { + // Reset position in case the processor used the scanner, and it didn't work out. + scanner.setPosition(processorStartPosition); + continue; } - if (ref != null) { - LinkReferenceDefinition definition = context.getLinkReferenceDefinition(ref); - if (definition != null) { - dest = definition.getDestination(); - title = definition.getTitle(); - } + var result = (LinkResultImpl) linkResult; + var node = result.getNode(); + var position = result.getPosition(); + var includeMarker = result.isIncludeMarker(); + + switch (result.getType()) { + case WRAP: + scanner.setPosition(position); + return wrapBracket(opener, node, includeMarker); + case REPLACE: + scanner.setPosition(position); + return replaceBracket(opener, node, includeMarker); } } - if (dest != null) { - // If we got here, we have a link or image - Node linkOrImage = opener.image ? new Image(dest, title) : new Link(dest, title); + return null; + } - // Add all nodes between the opening bracket and now (closing bracket) as child nodes of the link - Node node = opener.node.getNext(); - while (node != null) { - Node next = node.getNext(); - linkOrImage.appendChild(node); - node = next; - } + private LinkInfo parseLinkInfo(Bracket opener, Position beforeClose) { + // Check to see if we have a link (or image, with a ! in front). The different types: + // - Inline: `[foo](/uri)` or with optional title `[foo](/uri "title")` + // - Reference links + // - Full: `[foo][bar]` (foo is the text and bar is the label that needs to match a reference) + // - Collapsed: `[foo][]` (foo is both the text and label) + // - Shortcut: `[foo]` (foo is both the text and label) + + // Starting position is after the closing `]` + var afterClose = scanner.position(); + + // Maybe an inline link/image + var destinationTitle = parseInlineDestinationTitle(scanner); + if (destinationTitle != null) { + var text = scanner.getSource(opener.contentPosition, beforeClose).getContent(); + return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, null, destinationTitle.destination, destinationTitle.title, afterClose); + } + // Not an inline link/image, rewind back to after `]`. + scanner.setPosition(afterClose); + + // Maybe a reference link/image like `[foo][bar]`, `[foo][]` or `[foo]`. + // Note that even `[foo](` could be a valid link if foo is a reference, which is why we try this even if the `(` + // failed to be parsed as an inline link/image before. + + // See if there's a link label like `[bar]` or `[]` + var label = parseLinkLabel(scanner); + if (label == null) { + // No label, rewind back + scanner.setPosition(afterClose); + } + var textIsReference = label == null || label.isEmpty(); + if (opener.bracketAfter && textIsReference && opener.markerNode == null) { + // In case of shortcut or collapsed links, the text is used as the reference. But the reference is not allowed to + // contain an unescaped bracket, so if that's the case we don't need to continue. This is an optimization. + return null; + } - if (includeSourceSpans) { - linkOrImage.setSourceSpans(scanner.getSource(opener.markerPosition, scanner.position()).getSourceSpans()); - } + var text = scanner.getSource(opener.contentPosition, beforeClose).getContent(); + return new LinkInfoImpl(opener.markerNode, opener.bracketNode, text, label, null, null, afterClose); + } - // Process delimiters such as emphasis inside link/image - processDelimiters(opener.previousDelimiter); - mergeChildTextNodes(linkOrImage); - // We don't need the corresponding text node anymore, we turned it into a link/image node - opener.node.unlink(); - removeLastBracket(); + private Node wrapBracket(Bracket opener, Node wrapperNode, boolean includeMarker) { + // Add all nodes between the opening bracket and now (closing bracket) as child nodes of the link + Node n = opener.bracketNode.getNext(); + while (n != null) { + Node next = n.getNext(); + wrapperNode.appendChild(n); + n = next; + } - // Links within links are not allowed. We found this link, so there can be no other link around it. - if (!opener.image) { - Bracket bracket = lastBracket; - while (bracket != null) { - if (!bracket.image) { - // Disallow link opener. It will still get matched, but will not result in a link. - bracket.allowed = false; - } - bracket = bracket.previous; - } - } + if (includeSourceSpans) { + var startPosition = includeMarker && opener.markerPosition != null ? opener.markerPosition : opener.bracketPosition; + wrapperNode.setSourceSpans(scanner.getSource(startPosition, scanner.position()).getSourceSpans()); + } - return linkOrImage; + // Process delimiters such as emphasis inside link/image + processDelimiters(opener.previousDelimiter); + mergeChildTextNodes(wrapperNode); + // We don't need the corresponding text node anymore, we turned it into a link/image node + if (includeMarker && opener.markerNode != null) { + opener.markerNode.unlink(); + } + opener.bracketNode.unlink(); + removeLastBracket(); - } else { - // No link or image, parse just the bracket as text and continue - removeLastBracket(); + // Links within links are not allowed. We found this link, so there can be no other links around it. + if (opener.markerNode == null) { + disallowPreviousLinks(); + } - scanner.setPosition(afterClose); - return text(scanner.getSource(beforeClose, afterClose)); + return wrapperNode; + } + + private Node replaceBracket(Bracket opener, Node node, boolean includeMarker) { + // Remove delimiters (but keep text nodes) + while (lastDelimiter != null && lastDelimiter != opener.previousDelimiter) { + removeDelimiterKeepNode(lastDelimiter); + } + + if (includeSourceSpans) { + var startPosition = includeMarker && opener.markerPosition != null ? opener.markerPosition : opener.bracketPosition; + node.setSourceSpans(scanner.getSource(startPosition, scanner.position()).getSourceSpans()); } + + removeLastBracket(); + + // Remove nodes that we added since the opener, because we're replacing them + Node n = includeMarker && opener.markerNode != null ? opener.markerNode : opener.bracketNode; + while (n != null) { + var next = n.getNext(); + n.unlink(); + n = next; + } + + // Links within links are not allowed. We found this link, so there can be no other links around it. + // Note that this makes any syntax like `[foo]` behave the same as built-in links, which is probably a good + // default (it works for footnotes). It might be useful for a `LinkProcessor` to be able to specify the + // behavior; something we could add to `LinkResult` in the future if requested. + if (opener.markerNode == null || !includeMarker) { + disallowPreviousLinks(); + } + + return node; } private void addBracket(Bracket bracket) { @@ -421,10 +523,50 @@ private void removeLastBracket() { lastBracket = lastBracket.previous; } + private void disallowPreviousLinks() { + Bracket bracket = lastBracket; + while (bracket != null) { + if (bracket.markerNode == null) { + // Disallow link opener. It will still get matched, but will not result in a link. + bracket.allowed = false; + } + bracket = bracket.previous; + } + } + + /** + * Try to parse the destination and an optional title for an inline link/image. + */ + private static DestinationTitle parseInlineDestinationTitle(Scanner scanner) { + if (!scanner.next('(')) { + return null; + } + + scanner.whitespace(); + String dest = parseLinkDestination(scanner); + if (dest == null) { + return null; + } + + String title = null; + int whitespace = scanner.whitespace(); + // title needs a whitespace before + if (whitespace >= 1) { + title = parseLinkTitle(scanner); + scanner.whitespace(); + } + if (!scanner.next(')')) { + // Don't have a closing `)`, so it's not a destination and title. + // Note that something like `[foo](` could still be valid later, `(` will just be text. + return null; + } + return new DestinationTitle(dest, title); + } + /** * Attempt to parse link destination, returning the string or null if no match. */ - private String parseLinkDestination(Scanner scanner) { + private static String parseLinkDestination(Scanner scanner) { char delimiter = scanner.peek(); Position start = scanner.position(); if (!LinkScanner.scanLinkDestination(scanner)) { @@ -446,7 +588,7 @@ private String parseLinkDestination(Scanner scanner) { /** * Attempt to parse link title (sans quotes), returning the string or null if no match. */ - private String parseLinkTitle(Scanner scanner) { + private static String parseLinkTitle(Scanner scanner) { Position start = scanner.position(); if (!LinkScanner.scanLinkTitle(scanner)) { return null; @@ -461,7 +603,7 @@ private String parseLinkTitle(Scanner scanner) { /** * Attempt to parse a link label, returning the label between the brackets or null. */ - String parseLinkLabel(Scanner scanner) { + static String parseLinkLabel(Scanner scanner) { if (!scanner.next('[')) { return null; } @@ -488,7 +630,9 @@ String parseLinkLabel(Scanner scanner) { private Node parseLineBreak() { scanner.next(); - if (trailingSpaces >= 2) { + var hard = trailingSpaces >= 2; + trailingSpaces = 0; + if (hard) { return new HardLineBreak(); } else { return new SoftLineBreak(); @@ -784,4 +928,74 @@ private static class DelimiterData { this.canClose = canClose; } } + + /** + * A destination and optional title for a link or image. + */ + private static class DestinationTitle { + final String destination; + final String title; + + public DestinationTitle(String destination, String title) { + this.destination = destination; + this.title = title; + } + } + + private static class LinkInfoImpl implements LinkInfo { + + private final Text marker; + private final Text openingBracket; + private final String text; + private final String label; + private final String destination; + private final String title; + private final Position afterTextBracket; + + private LinkInfoImpl(Text marker, Text openingBracket, String text, String label, + String destination, String title, Position afterTextBracket) { + this.marker = marker; + this.openingBracket = openingBracket; + this.text = text; + this.label = label; + this.destination = destination; + this.title = title; + this.afterTextBracket = afterTextBracket; + } + + @Override + public Text marker() { + return marker; + } + + @Override + public Text openingBracket() { + return openingBracket; + } + + @Override + public String text() { + return text; + } + + @Override + public String label() { + return label; + } + + @Override + public String destination() { + return destination; + } + + @Override + public String title() { + return title; + } + + @Override + public Position afterTextBracket() { + return afterTextBracket; + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java index 35db88cc2ba..b7921b69a5f 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/LinkReferenceDefinitionParser.java @@ -42,12 +42,13 @@ import jdk.internal.org.commonmark.parser.beta.Scanner; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * Parser for link reference definitions at the beginning of a paragraph. * - * @see Link reference definitions + * @see Link reference definitions */ public class LinkReferenceDefinitionParser { @@ -102,6 +103,9 @@ public void parse(SourceLine line) { // Parsing failed, which means we fall back to treating text as a paragraph. if (!success) { state = State.PARAGRAPH; + // If parsing of the title part failed, we still have a valid reference that we can add, and we need to + // do it before the source span for this line is added. + finishReference(); return; } } @@ -131,6 +135,14 @@ State getState() { return state; } + List removeLines(int lines) { + var removedSpans = Collections.unmodifiableList(new ArrayList<>( + sourceSpans.subList(Math.max(sourceSpans.size() - lines, 0), sourceSpans.size()))); + removeLast(lines, paragraphLines); + removeLast(lines, sourceSpans); + return removedSpans; + } + private boolean startDefinition(Scanner scanner) { // Finish any outstanding references now. We don't do this earlier because we need addSourceSpan to have been // called before we do it. @@ -250,7 +262,8 @@ private boolean startTitle(Scanner scanner) { private boolean title(Scanner scanner) { Position start = scanner.position(); if (!LinkScanner.scanLinkTitleContent(scanner, titleDelimiter)) { - // Invalid title, stop + // Invalid title, stop. Title collected so far must not be used. + title = null; return false; } @@ -267,6 +280,8 @@ private boolean title(Scanner scanner) { scanner.whitespace(); if (scanner.hasNext()) { // spec: No further non-whitespace characters may occur on the line. + // Title collected so far must not be used. + title = null; return false; } referenceValid = true; @@ -295,6 +310,16 @@ private void finishReference() { title = null; } + private static void removeLast(int n, List list) { + if (n >= list.size()) { + list.clear(); + } else { + for (int i = 0; i < n; i++) { + list.remove(list.size() - 1); + } + } + } + enum State { // Looking for the start of a definition, i.e. `[` START_DEFINITION, diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java index 89a3c2332cb..793bc08cfd9 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ListBlockParser.java @@ -160,7 +160,7 @@ private static ListMarkerData parseListMarker(CharSequence line, int index) { } } - // spec: An ordered list marker is a sequence of 1\u20139 arabic digits (0-9), followed by either a `.` character or a + // spec: An ordered list marker is a sequence of 1-9 arabic digits (0-9), followed by either a `.` character or a // `)` character. private static ListMarkerData parseOrderedList(CharSequence line, int index) { int digits = 0; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java index 7cbc28e2cb2..1850c72315d 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/ParagraphParser.java @@ -32,10 +32,7 @@ package jdk.internal.org.commonmark.internal; -import jdk.internal.org.commonmark.node.Block; -import jdk.internal.org.commonmark.node.LinkReferenceDefinition; -import jdk.internal.org.commonmark.node.Paragraph; -import jdk.internal.org.commonmark.node.SourceSpan; +import jdk.internal.org.commonmark.node.*; import jdk.internal.org.commonmark.parser.InlineParser; import jdk.internal.org.commonmark.parser.SourceLine; import jdk.internal.org.commonmark.parser.SourceLines; @@ -81,8 +78,21 @@ public void addSourceSpan(SourceSpan sourceSpan) { linkReferenceDefinitionParser.addSourceSpan(sourceSpan); } + @Override + public List> getDefinitions() { + var map = new DefinitionMap<>(LinkReferenceDefinition.class); + for (var def : linkReferenceDefinitionParser.getDefinitions()) { + map.putIfAbsent(def.getLabel(), def); + } + return List.of(map); + } + @Override public void closeBlock() { + for (var def : linkReferenceDefinitionParser.getDefinitions()) { + block.insertBefore(def); + } + if (linkReferenceDefinitionParser.getParagraphLines().isEmpty()) { block.unlink(); } else { @@ -102,7 +112,7 @@ public SourceLines getParagraphLines() { return linkReferenceDefinitionParser.getParagraphLines(); } - public List getDefinitions() { - return linkReferenceDefinitionParser.getDefinitions(); + public List removeLines(int lines) { + return linkReferenceDefinitionParser.removeLines(lines); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java index 28ce45d9c4c..98a8af2cfa7 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/AutolinkInlineParser.java @@ -35,9 +35,9 @@ import jdk.internal.org.commonmark.node.Link; import jdk.internal.org.commonmark.node.Text; import jdk.internal.org.commonmark.parser.SourceLines; -import jdk.internal.org.commonmark.parser.beta.Position; -import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; +import java.util.Set; import java.util.regex.Pattern; /** @@ -78,4 +78,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) { } return ParsedInline.none(); } + + public static class Factory implements InlineContentParserFactory { + @Override + public Set getTriggerCharacters() { + return Set.of('<'); + } + + @Override + public InlineContentParser create() { + return new AutolinkInlineParser(); + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java index e18d337ad8a..4170d9cd0ae 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackslashInlineParser.java @@ -35,8 +35,9 @@ import jdk.internal.org.commonmark.internal.util.Escaping; import jdk.internal.org.commonmark.node.HardLineBreak; import jdk.internal.org.commonmark.node.Text; -import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; +import java.util.Set; import java.util.regex.Pattern; /** @@ -64,4 +65,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) { return ParsedInline.of(new Text("\\"), scanner.position()); } } + + public static class Factory implements InlineContentParserFactory { + @Override + public Set getTriggerCharacters() { + return Set.of('\\'); + } + + @Override + public InlineContentParser create() { + return new BackslashInlineParser(); + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java index 06b80ab3ebd..fe97556d305 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/BackticksInlineParser.java @@ -35,10 +35,11 @@ import jdk.internal.org.commonmark.node.Code; import jdk.internal.org.commonmark.node.Text; import jdk.internal.org.commonmark.parser.SourceLines; -import jdk.internal.org.commonmark.parser.beta.Position; -import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; import jdk.internal.org.commonmark.text.Characters; +import java.util.Set; + /** * Attempt to parse backticks, returning either a backtick code span or a literal sequence of backticks. */ @@ -79,4 +80,16 @@ public ParsedInline tryParse(InlineParserState inlineParserState) { Text text = new Text(source.getContent()); return ParsedInline.of(text, afterOpening); } + + public static class Factory implements InlineContentParserFactory { + @Override + public Set getTriggerCharacters() { + return Set.of('`'); + } + + @Override + public InlineContentParser create() { + return new BackticksInlineParser(); + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/CoreLinkProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/CoreLinkProcessor.java new file mode 100644 index 00000000000..2f9b17a6b1c --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/CoreLinkProcessor.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.internal.inline; + +import jdk.internal.org.commonmark.node.Image; +import jdk.internal.org.commonmark.node.Link; +import jdk.internal.org.commonmark.node.LinkReferenceDefinition; +import jdk.internal.org.commonmark.parser.InlineParserContext; +import jdk.internal.org.commonmark.parser.beta.LinkInfo; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; +import jdk.internal.org.commonmark.parser.beta.LinkResult; +import jdk.internal.org.commonmark.parser.beta.Scanner; + +public class CoreLinkProcessor implements LinkProcessor { + + @Override + public LinkResult process(LinkInfo linkInfo, Scanner scanner, InlineParserContext context) { + if (linkInfo.destination() != null) { + // Inline link + return process(linkInfo, scanner, linkInfo.destination(), linkInfo.title()); + } + + var label = linkInfo.label(); + var ref = label != null && !label.isEmpty() ? label : linkInfo.text(); + var def = context.getDefinition(LinkReferenceDefinition.class, ref); + if (def != null) { + // Reference link + return process(linkInfo, scanner, def.getDestination(), def.getTitle()); + } + return LinkResult.none(); + } + + private static LinkResult process(LinkInfo linkInfo, Scanner scanner, String destination, String title) { + if (linkInfo.marker() != null && linkInfo.marker().getLiteral().equals("!")) { + return LinkResult.wrapTextIn(new Image(destination, title), scanner.position()).includeMarker(); + } + return LinkResult.wrapTextIn(new Link(destination, title), scanner.position()); + } +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java index cf4e3d2004b..e8972e31d3d 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/EntityInlineParser.java @@ -32,14 +32,15 @@ package jdk.internal.org.commonmark.internal.inline; -import jdk.internal.org.commonmark.text.AsciiMatcher; import jdk.internal.org.commonmark.internal.util.Html5Entities; import jdk.internal.org.commonmark.node.Text; -import jdk.internal.org.commonmark.parser.beta.Position; -import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; +import jdk.internal.org.commonmark.text.AsciiMatcher; + +import java.util.Set; /** - * Attempts to parse a HTML entity or numeric character reference. + * Attempts to parse an HTML entity or numeric character reference. */ public class EntityInlineParser implements InlineContentParser { @@ -84,4 +85,17 @@ private ParsedInline entity(Scanner scanner, Position start) { String text = scanner.getSource(start, scanner.position()).getContent(); return ParsedInline.of(new Text(Html5Entities.entityToString(text)), scanner.position()); } + + public static class Factory implements InlineContentParserFactory { + + @Override + public Set getTriggerCharacters() { + return Set.of('&'); + } + + @Override + public InlineContentParser create() { + return new EntityInlineParser(); + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java index 88f4baa2275..be0c7e7346c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/HtmlInlineParser.java @@ -32,10 +32,11 @@ package jdk.internal.org.commonmark.internal.inline; -import jdk.internal.org.commonmark.text.AsciiMatcher; import jdk.internal.org.commonmark.node.HtmlInline; -import jdk.internal.org.commonmark.parser.beta.Position; -import jdk.internal.org.commonmark.parser.beta.Scanner; +import jdk.internal.org.commonmark.parser.beta.*; +import jdk.internal.org.commonmark.text.AsciiMatcher; + +import java.util.Set; /** * Attempt to parse inline HTML. @@ -232,4 +233,17 @@ private static boolean tryDeclaration(Scanner scanner) { } return false; } + + public static class Factory implements InlineContentParserFactory { + + @Override + public Set getTriggerCharacters() { + return Set.of('<'); + } + + @Override + public InlineContentParser create() { + return new HtmlInlineParser(); + } + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/LinkResultImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/LinkResultImpl.java new file mode 100644 index 00000000000..62b87198004 --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/LinkResultImpl.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.internal.inline; + +import jdk.internal.org.commonmark.node.Node; +import jdk.internal.org.commonmark.parser.beta.LinkResult; +import jdk.internal.org.commonmark.parser.beta.Position; + +public class LinkResultImpl implements LinkResult { + @Override + public LinkResult includeMarker() { + includeMarker = true; + return this; + } + + public enum Type { + WRAP, + REPLACE + } + + private final Type type; + private final Node node; + private final Position position; + + private boolean includeMarker = false; + + public LinkResultImpl(Type type, Node node, Position position) { + this.type = type; + this.node = node; + this.position = position; + } + + public Type getType() { + return type; + } + + public Node getNode() { + return node; + } + + public Position getPosition() { + return position; + } + + public boolean isIncludeMarker() { + return includeMarker; + } +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java index 973149a83b4..ebdb0401066 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInlineImpl.java @@ -33,13 +33,14 @@ package jdk.internal.org.commonmark.internal.inline; import jdk.internal.org.commonmark.node.Node; +import jdk.internal.org.commonmark.parser.beta.ParsedInline; import jdk.internal.org.commonmark.parser.beta.Position; -public class ParsedInlineImpl extends ParsedInline { +public class ParsedInlineImpl implements ParsedInline { private final Node node; private final Position position; - ParsedInlineImpl(Node node, Position position) { + public ParsedInlineImpl(Node node, Position position) { this.node = node; this.position = position; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java deleted file mode 100644 index a32d0564f7d..00000000000 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Scanner.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, a notice that is now available elsewhere in this distribution - * accompanied the original version of this file, and, per its terms, - * should not be removed. - */ - -package jdk.internal.org.commonmark.internal.inline; - -import jdk.internal.org.commonmark.internal.util.CharMatcher; -import jdk.internal.org.commonmark.node.SourceSpan; -import jdk.internal.org.commonmark.parser.SourceLine; -import jdk.internal.org.commonmark.parser.SourceLines; - -import java.util.List; - -public class Scanner { - - /** - * Character representing the end of input source (or outside of the text in case of the "previous" methods). - *

        - * Note that we can use NULL to represent this because CommonMark does not allow those in the input (we replace them - * in the beginning of parsing). - */ - public static final char END = '\0'; - - // Lines without newlines at the end. The scanner will yield `\n` between lines because they're significant for - // parsing and the final output. There is no `\n` after the last line. - private final List lines; - // Which line we're at. - private int lineIndex; - // The index within the line. If index == length(), we pretend that there's a `\n` and only advance after we yield - // that. - private int index; - - // Current line or "" if at the end of the lines (using "" instead of null saves a null check) - private SourceLine line = SourceLine.of("", null); - private int lineLength = 0; - - Scanner(List lines, int lineIndex, int index) { - this.lines = lines; - this.lineIndex = lineIndex; - this.index = index; - if (!lines.isEmpty()) { - checkPosition(lineIndex, index); - setLine(lines.get(lineIndex)); - } - } - - public static Scanner of(SourceLines lines) { - return new Scanner(lines.getLines(), 0, 0); - } - - public char peek() { - if (index < lineLength) { - return line.getContent().charAt(index); - } else { - if (lineIndex < lines.size() - 1) { - return '\n'; - } else { - // Don't return newline for end of last line - return END; - } - } - } - - public int peekCodePoint() { - if (index < lineLength) { - char c = line.getContent().charAt(index); - if (Character.isHighSurrogate(c) && index + 1 < lineLength) { - char low = line.getContent().charAt(index + 1); - if (Character.isLowSurrogate(low)) { - return Character.toCodePoint(c, low); - } - } - return c; - } else { - if (lineIndex < lines.size() - 1) { - return '\n'; - } else { - // Don't return newline for end of last line - return END; - } - } - } - - public int peekPreviousCodePoint() { - if (index > 0) { - int prev = index - 1; - char c = line.getContent().charAt(prev); - if (Character.isLowSurrogate(c) && prev > 0) { - char high = line.getContent().charAt(prev - 1); - if (Character.isHighSurrogate(high)) { - return Character.toCodePoint(high, c); - } - } - return c; - } else { - if (lineIndex > 0) { - return '\n'; - } else { - return END; - } - } - } - - public boolean hasNext() { - if (index < lineLength) { - return true; - } else { - // No newline at end of last line - return lineIndex < lines.size() - 1; - } - } - - public void next() { - index++; - if (index > lineLength) { - lineIndex++; - if (lineIndex < lines.size()) { - setLine(lines.get(lineIndex)); - } else { - setLine(SourceLine.of("", null)); - } - index = 0; - } - } - - /** - * Check if the specified char is next and advance the position. - * - * @param c the char to check (including newline characters) - * @return true if matched and position was advanced, false otherwise - */ - public boolean next(char c) { - if (peek() == c) { - next(); - return true; - } else { - return false; - } - } - - /** - * Check if we have the specified content on the line and advanced the position. Note that if you want to match - * newline characters, use {@link #next(char)}. - * - * @param content the text content to match on a single line (excluding newline characters) - * @return true if matched and position was advanced, false otherwise - */ - public boolean next(String content) { - if (index < lineLength && index + content.length() <= lineLength) { - // Can't use startsWith because it's not available on CharSequence - for (int i = 0; i < content.length(); i++) { - if (line.getContent().charAt(index + i) != content.charAt(i)) { - return false; - } - } - index += content.length(); - return true; - } else { - return false; - } - } - - public int matchMultiple(char c) { - int count = 0; - while (peek() == c) { - count++; - next(); - } - return count; - } - - public int match(CharMatcher matcher) { - int count = 0; - while (matcher.matches(peek())) { - count++; - next(); - } - return count; - } - - public int whitespace() { - int count = 0; - while (true) { - switch (peek()) { - case ' ': - case '\t': - case '\n': - case '\u000B': - case '\f': - case '\r': - count++; - next(); - break; - default: - return count; - } - } - } - - public int find(char c) { - int count = 0; - while (true) { - char cur = peek(); - if (cur == Scanner.END) { - return -1; - } else if (cur == c) { - return count; - } - count++; - next(); - } - } - - public int find(CharMatcher matcher) { - int count = 0; - while (true) { - char c = peek(); - if (c == END) { - return -1; - } else if (matcher.matches(c)) { - return count; - } - count++; - next(); - } - } - - // Don't expose the int index, because it would be good if we could switch input to a List of lines later - // instead of one contiguous String. - public Position position() { - return new Position(lineIndex, index); - } - - public void setPosition(Position position) { - checkPosition(position.lineIndex, position.index); - this.lineIndex = position.lineIndex; - this.index = position.index; - setLine(lines.get(this.lineIndex)); - } - - // For cases where the caller appends the result to a StringBuilder, we could offer another method to avoid some - // unnecessary copying. - public SourceLines getSource(Position begin, Position end) { - if (begin.lineIndex == end.lineIndex) { - // Shortcut for common case of text from a single line - SourceLine line = lines.get(begin.lineIndex); - CharSequence newContent = line.getContent().subSequence(begin.index, end.index); - SourceSpan newSourceSpan = null; - SourceSpan sourceSpan = line.getSourceSpan(); - if (sourceSpan != null) { - newSourceSpan = SourceSpan.of(sourceSpan.getLineIndex(), sourceSpan.getColumnIndex() + begin.index, newContent.length()); - } - return SourceLines.of(SourceLine.of(newContent, newSourceSpan)); - } else { - SourceLines sourceLines = SourceLines.empty(); - - SourceLine firstLine = lines.get(begin.lineIndex); - sourceLines.addLine(firstLine.substring(begin.index, firstLine.getContent().length())); - - // Lines between begin and end (we are appending the full line) - for (int line = begin.lineIndex + 1; line < end.lineIndex; line++) { - sourceLines.addLine(lines.get(line)); - } - - SourceLine lastLine = lines.get(end.lineIndex); - sourceLines.addLine(lastLine.substring(0, end.index)); - return sourceLines; - } - } - - private void setLine(SourceLine line) { - this.line = line; - this.lineLength = line.getContent().length(); - } - - private void checkPosition(int lineIndex, int index) { - if (lineIndex < 0 || lineIndex >= lines.size()) { - throw new IllegalArgumentException("Line index " + lineIndex + " out of range, number of lines: " + lines.size()); - } - SourceLine line = lines.get(lineIndex); - if (index < 0 || index > line.getContent().length()) { - throw new IllegalArgumentException("Index " + index + " out of range, line length: " + line.getContent().length()); - } - } -} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/NodeRendererMap.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/NodeRendererMap.java index 523609f21de..651dda18432 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/NodeRendererMap.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/NodeRendererMap.java @@ -35,24 +35,39 @@ import jdk.internal.org.commonmark.node.Node; import jdk.internal.org.commonmark.renderer.NodeRenderer; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class NodeRendererMap { + private final List nodeRenderers = new ArrayList<>(); private final Map, NodeRenderer> renderers = new HashMap<>(32); + /** + * Set the renderer for each {@link NodeRenderer#getNodeTypes()}, unless there was already a renderer set (first wins). + */ public void add(NodeRenderer nodeRenderer) { - for (Class nodeType : nodeRenderer.getNodeTypes()) { - // Overwrite existing renderer - renderers.put(nodeType, nodeRenderer); + nodeRenderers.add(nodeRenderer); + for (var nodeType : nodeRenderer.getNodeTypes()) { + // The first node renderer for a node type "wins". + renderers.putIfAbsent(nodeType, nodeRenderer); } } public void render(Node node) { - NodeRenderer nodeRenderer = renderers.get(node.getClass()); + var nodeRenderer = renderers.get(node.getClass()); if (nodeRenderer != null) { nodeRenderer.render(node); } } + + public void beforeRoot(Node node) { + nodeRenderers.forEach(r -> r.beforeRoot(node)); + } + + public void afterRoot(Node node) { + nodeRenderers.forEach(r -> r.afterRoot(node)); + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/BulletListHolder.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/BulletListHolder.java deleted file mode 100644 index 154bad58278..00000000000 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/BulletListHolder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, a notice that is now available elsewhere in this distribution - * accompanied the original version of this file, and, per its terms, - * should not be removed. - */ - -package jdk.internal.org.commonmark.internal.renderer.text; - -import jdk.internal.org.commonmark.node.BulletList; - -public class BulletListHolder extends ListHolder { - private final String marker; - - public BulletListHolder(ListHolder parent, BulletList list) { - super(parent); - marker = list.getMarker(); - } - - public String getMarker() { - return marker; - } -} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/ListHolder.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/ListHolder.java deleted file mode 100644 index 6334f4cffad..00000000000 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/ListHolder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, a notice that is now available elsewhere in this distribution - * accompanied the original version of this file, and, per its terms, - * should not be removed. - */ - -package jdk.internal.org.commonmark.internal.renderer.text; - -public abstract class ListHolder { - private static final String INDENT_DEFAULT = " "; - private static final String INDENT_EMPTY = ""; - - private final ListHolder parent; - private final String indent; - - ListHolder(ListHolder parent) { - this.parent = parent; - - if (parent != null) { - indent = parent.indent + INDENT_DEFAULT; - } else { - indent = INDENT_EMPTY; - } - } - - public ListHolder getParent() { - return parent; - } - - public String getIndent() { - return indent; - } -} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/AsciiMatcher.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/AsciiMatcher.java deleted file mode 100644 index 915c3078b7a..00000000000 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/AsciiMatcher.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, a notice that is now available elsewhere in this distribution - * accompanied the original version of this file, and, per its terms, - * should not be removed. - */ - -package jdk.internal.org.commonmark.internal.util; - -import java.util.BitSet; - -public class AsciiMatcher implements CharMatcher { - private final BitSet set; - - private AsciiMatcher(Builder builder) { - this.set = builder.set; - } - - @Override - public boolean matches(char c) { - return set.get(c); - } - - public Builder newBuilder() { - return new Builder((BitSet) set.clone()); - } - - public static Builder builder() { - return new Builder(new BitSet()); - } - - public static class Builder { - private final BitSet set; - - private Builder(BitSet set) { - this.set = set; - } - - public Builder c(char c) { - if (c > 127) { - throw new IllegalArgumentException("Can only match ASCII characters"); - } - set.set(c); - return this; - } - - public Builder range(char from, char toInclusive) { - for (char c = from; c <= toInclusive; c++) { - c(c); - } - return this; - } - - public AsciiMatcher build() { - return new AsciiMatcher(this); - } - } -} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/CharMatcher.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/CharMatcher.java deleted file mode 100644 index e74cb314ff3..00000000000 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/CharMatcher.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, a notice that is now available elsewhere in this distribution - * accompanied the original version of this file, and, per its terms, - * should not be removed. - */ - -package jdk.internal.org.commonmark.internal.util; - -public interface CharMatcher { - - boolean matches(char c); -} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Escaping.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Escaping.java index 939a4c69860..cf19c1ded9b 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Escaping.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Escaping.java @@ -32,14 +32,14 @@ package jdk.internal.org.commonmark.internal.util; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Escaping { - public static final String ESCAPABLE = "[!\"#$%&\'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~-]"; + public static final String ESCAPABLE = "[!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~-]"; public static final String ENTITY = "&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});"; @@ -57,36 +57,30 @@ public class Escaping { private static final Pattern WHITESPACE = Pattern.compile("[ \t\r\n]+"); - private static final Replacer UNESCAPE_REPLACER = new Replacer() { - @Override - public void replace(String input, StringBuilder sb) { - if (input.charAt(0) == '\\') { - sb.append(input, 1, input.length()); - } else { - sb.append(Html5Entities.entityToString(input)); - } + private static final Replacer UNESCAPE_REPLACER = (input, sb) -> { + if (input.charAt(0) == '\\') { + sb.append(input, 1, input.length()); + } else { + sb.append(Html5Entities.entityToString(input)); } }; - private static final Replacer URI_REPLACER = new Replacer() { - @Override - public void replace(String input, StringBuilder sb) { - if (input.startsWith("%")) { - if (input.length() == 3) { - // Already percent-encoded, preserve - sb.append(input); - } else { - // %25 is the percent-encoding for % - sb.append("%25"); - sb.append(input, 1, input.length()); - } + private static final Replacer URI_REPLACER = (input, sb) -> { + if (input.startsWith("%")) { + if (input.length() == 3) { + // Already percent-encoded, preserve + sb.append(input); } else { - byte[] bytes = input.getBytes(Charset.forName("UTF-8")); - for (byte b : bytes) { - sb.append('%'); - sb.append(HEX_DIGITS[(b >> 4) & 0xF]); - sb.append(HEX_DIGITS[b & 0xF]); - } + // %25 is the percent-encoding for % + sb.append("%25"); + sb.append(input, 1, input.length()); + } + } else { + byte[] bytes = input.getBytes(StandardCharsets.UTF_8); + for (byte b : bytes) { + sb.append('%'); + sb.append(HEX_DIGITS[(b >> 4) & 0xF]); + sb.append(HEX_DIGITS[b & 0xF]); } } }; @@ -95,7 +89,6 @@ public static String escapeHtml(String input) { // Avoid building a new string in the majority of cases (nothing to escape) StringBuilder sb = null; - loop: for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); String replacement; @@ -116,7 +109,7 @@ public static String escapeHtml(String input) { if (sb != null) { sb.append(c); } - continue loop; + continue; } if (sb == null) { sb = new StringBuilder(); @@ -146,11 +139,11 @@ public static String percentEncodeUrl(String s) { public static String normalizeLabelContent(String input) { String trimmed = input.trim(); - // This is necessary to correctly case fold "\u1e9e" to "SS": - // "\u1e9e".toLowerCase(Locale.ROOT) -> "\u00df" - // "\u00df".toUpperCase(Locale.ROOT) -> "SS" + // This is necessary to correctly case fold "\u1E9E" (LATIN CAPITAL LETTER SHARP S) to "SS": + // "\u1E9E".toLowerCase(Locale.ROOT) -> "\u00DF" (LATIN SMALL LETTER SHARP S) + // "\u00DF".toUpperCase(Locale.ROOT) -> "SS" // Note that doing upper first (or only upper without lower) wouldn't work because: - // "\u1e9e".toUpperCase(Locale.ROOT) -> "\u1e9e" + // "\u1E9E".toUpperCase(Locale.ROOT) -> "\u1E9E" String caseFolded = trimmed.toLowerCase(Locale.ROOT).toUpperCase(Locale.ROOT); return WHITESPACE.matcher(caseFolded).replaceAll(" "); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Html5Entities.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Html5Entities.java index 6dcb66c058d..c2fda445d40 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Html5Entities.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/Html5Entities.java @@ -86,7 +86,7 @@ private static Map readEntities() { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, charset))) { String line; while ((line = bufferedReader.readLine()) != null) { - if (line.length() == 0) { + if (line.isEmpty()) { continue; } int equal = line.indexOf("="); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/LineReader.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/LineReader.java new file mode 100644 index 00000000000..55fa445deae --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/util/LineReader.java @@ -0,0 +1,181 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.internal.util; + +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; + +/** + * Reads lines from a reader like {@link java.io.BufferedReader} but also returns the line terminators. + *

        + * Line terminators can be either a line feed {@code "\n"}, carriage return {@code "\r"}, or a carriage return followed + * by a line feed {@code "\r\n"}. Call {@link #getLineTerminator()} after {@link #readLine()} to obtain the + * corresponding line terminator. If a stream has a line at the end without a terminator, {@link #getLineTerminator()} + * returns {@code null}. + */ +public class LineReader implements Closeable { + + // Same as java.io.BufferedReader + static final int CHAR_BUFFER_SIZE = 8192; + static final int EXPECTED_LINE_LENGTH = 80; + + private Reader reader; + private char[] cbuf; + + private int position = 0; + private int limit = 0; + + private String lineTerminator = null; + + public LineReader(Reader reader) { + this.reader = reader; + this.cbuf = new char[CHAR_BUFFER_SIZE]; + } + + /** + * Read a line of text. + * + * @return the line, or {@code null} when the end of the stream has been reached and no more lines can be read + */ + public String readLine() throws IOException { + StringBuilder sb = null; + boolean cr = false; + + while (true) { + if (position >= limit) { + fill(); + } + + if (cr) { + // We saw a CR before, check if we have CR LF or just CR. + if (position < limit && cbuf[position] == '\n') { + position++; + return line(sb.toString(), "\r\n"); + } else { + return line(sb.toString(), "\r"); + } + } + + if (position >= limit) { + // End of stream, return either the last line without terminator or null for end. + return line(sb != null ? sb.toString() : null, null); + } + + int start = position; + int i = position; + for (; i < limit; i++) { + char c = cbuf[i]; + if (c == '\n') { + position = i + 1; + return line(finish(sb, start, i), "\n"); + } else if (c == '\r') { + if (i + 1 < limit) { + // We know what the next character is, so we can check now whether we have + // a CR LF or just a CR and return. + if (cbuf[i + 1] == '\n') { + position = i + 2; + return line(finish(sb, start, i), "\r\n"); + } else { + position = i + 1; + return line(finish(sb, start, i), "\r"); + } + } else { + // We don't know what the next character is yet, check on next iteration. + cr = true; + position = i + 1; + break; + } + } + } + + if (position < i) { + position = i; + } + + // Haven't found a finished line yet, copy the data from the buffer so that we can fill + // the buffer again. + if (sb == null) { + sb = new StringBuilder(EXPECTED_LINE_LENGTH); + } + sb.append(cbuf, start, i - start); + } + } + + /** + * Return the line terminator of the last read line from {@link #readLine()}. + * + * @return {@code "\n"}, {@code "\r"}, {@code "\r\n"}, or {@code null} + */ + public String getLineTerminator() { + return lineTerminator; + } + + @Override + public void close() throws IOException { + if (reader == null) { + return; + } + try { + reader.close(); + } finally { + reader = null; + cbuf = null; + } + } + + private void fill() throws IOException { + int read; + do { + read = reader.read(cbuf, 0, cbuf.length); + } while (read == 0); + if (read > 0) { + limit = read; + position = 0; + } + } + + private String line(String line, String lineTerminator) { + this.lineTerminator = lineTerminator; + return line; + } + + private String finish(StringBuilder sb, int start, int end) { + int len = end - start; + if (sb == null) { + return new String(cbuf, start, len); + } else { + return sb.append(cbuf, start, len).toString(); + } + } +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java index 57603fa5fdd..13e54a25a59 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Block.java @@ -37,6 +37,7 @@ */ public abstract class Block extends Node { + @Override public Block getParent() { return (Block) super.getParent(); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java index 8a5f1d20c07..74a1adb34b7 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BlockQuote.java @@ -32,6 +32,16 @@ package jdk.internal.org.commonmark.node; +/** + * A block quote, e.g.: + *

        + * > Some quoted text
        + * 
        + *

        + * Note that child nodes are themselves blocks, e.g. {@link Paragraph}, {@link ListBlock} etc. + * + * @see CommonMark Spec + */ public class BlockQuote extends Block { @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java index 75ab053da3f..a6a85024bc6 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/BulletList.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * A bullet list, e.g.: + *

        + * - One
        + * - Two
        + * - Three
        + * 
        + *

        + * The children are {@link ListItem} blocks, which contain other blocks (or nested lists). + * + * @see CommonMark Spec: List items + */ public class BulletList extends ListBlock { private String marker; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java index b64ddefa1f1..f2a8697f0b8 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Code.java @@ -32,6 +32,14 @@ package jdk.internal.org.commonmark.node; +/** + * Inline code span, e.g.: + *

        + * Some `inline code`
        + * 
        + * + * @see CommonMark Spec + */ public class Code extends Node { private String literal; @@ -48,6 +56,10 @@ public void accept(Visitor visitor) { visitor.visit(this); } + /** + * @return the literal text in the code span (note that it's not necessarily the raw text between tildes, + * e.g. when spaces are stripped) + */ public String getLiteral() { return literal; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java index 08038848ada..df7b72bf72e 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomBlock.java @@ -32,6 +32,9 @@ package jdk.internal.org.commonmark.node; +/** + * A block that extensions can subclass to define custom blocks (not part of the core specification). + */ public abstract class CustomBlock extends Block { @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java index 733df320afc..a573eae4cf9 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/CustomNode.java @@ -32,6 +32,9 @@ package jdk.internal.org.commonmark.node; +/** + * A node that extensions can subclass to define custom nodes (not part of the core specification). + */ public abstract class CustomNode extends Node { @Override public void accept(Visitor visitor) { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/DefinitionMap.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/DefinitionMap.java new file mode 100644 index 00000000000..2b40e725869 --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/DefinitionMap.java @@ -0,0 +1,99 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.node; + +import jdk.internal.org.commonmark.internal.util.Escaping; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * A map that can be used to store and look up reference definitions by a label. The labels are case-insensitive and + * normalized, the same way as for {@link LinkReferenceDefinition} nodes. + * + * @param the type of value + */ +public class DefinitionMap { + + private final Class type; + // LinkedHashMap for determinism and to preserve document order + private final Map definitions = new LinkedHashMap<>(); + + public DefinitionMap(Class type) { + this.type = type; + } + + public Class getType() { + return type; + } + + public void addAll(DefinitionMap that) { + for (var entry : that.definitions.entrySet()) { + // Note that keys are already normalized, so we can add them directly + definitions.putIfAbsent(entry.getKey(), entry.getValue()); + } + } + + /** + * Store a new definition unless one is already in the map. If there is no definition for that label yet, return null. + * Otherwise, return the existing definition. + *

        + * The label is normalized by the definition map before storing. + */ + public D putIfAbsent(String label, D definition) { + String normalizedLabel = Escaping.normalizeLabelContent(label); + + // spec: When there are multiple matching link reference definitions, the first is used + return definitions.putIfAbsent(normalizedLabel, definition); + } + + /** + * Look up a definition by label. The label is normalized by the definition map before lookup. + * + * @return the value or null + */ + public D get(String label) { + String normalizedLabel = Escaping.normalizeLabelContent(label); + return definitions.get(normalizedLabel); + } + + public Set keySet() { + return definitions.keySet(); + } + + public Collection values() { + return definitions.values(); + } +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java index 390728318ec..f15546713e0 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Document.java @@ -32,6 +32,9 @@ package jdk.internal.org.commonmark.node; +/** + * The root block of a document, containing the top-level blocks. + */ public class Document extends Block { @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java index 86b09f4f2f6..7dd42799362 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Emphasis.java @@ -32,6 +32,14 @@ package jdk.internal.org.commonmark.node; +/** + * Emphasis, e.g.: + *

        + * Some *emphasis* or _emphasis_
        + * 
        + * + * @see CommonMark Spec: Emphasis and strong emphasis + */ public class Emphasis extends Node implements Delimited { private String delimiter; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java index d78b3e4292a..0bcba0db209 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/FencedCodeBlock.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * A fenced code block, e.g.: + *
        + * ```
        + * foo
        + * bar
        + * ```
        + * 
        + *

        + * + * @see CommonMark Spec + */ public class FencedCodeBlock extends Block { private String fenceCharacter; @@ -99,7 +111,7 @@ public void setFenceIndent(int fenceIndent) { } /** - * @see CommonMark spec + * @see CommonMark spec */ public String getInfo() { return info; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java index 5c85504a8fb..86e0ac493ca 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HardLineBreak.java @@ -32,6 +32,16 @@ package jdk.internal.org.commonmark.node; +/** + * A hard line break, e.g.: + *

        + * line\
        + * break
        + * 
        + *

        + * + * @see CommonMark Spec + */ public class HardLineBreak extends Node { @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java index 49a8225fca1..f008a8bac8c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Heading.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * A heading, e.g.: + *

        + * First heading
        + * =============
        + *
        + * ## Another heading
        + * 
        + * + * @see CommonMark Spec: ATX headings + * @see CommonMark Spec: Setext headings + */ public class Heading extends Block { private int level; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java index 94380c084e1..a7c55719e9a 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlBlock.java @@ -35,7 +35,7 @@ /** * HTML block * - * @see CommonMark Spec + * @see CommonMark Spec */ public class HtmlBlock extends Block { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java index 7402606ac83..cf8e5dbed62 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/HtmlInline.java @@ -35,7 +35,7 @@ /** * Inline HTML element. * - * @see CommonMark Spec + * @see CommonMark Spec */ public class HtmlInline extends Node { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java index 36a3f2f7221..ceb4c50e6ec 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Image.java @@ -32,6 +32,14 @@ package jdk.internal.org.commonmark.node; +/** + * An image, e.g.: + *
        + * ![foo](/url "title")
        + * 
        + * + * @see CommonMark Spec + */ public class Image extends Node { private String destination; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java index d3b4cec41d8..140f22e44ad 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/IndentedCodeBlock.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * An indented code block, e.g.: + *
        
        + * Code follows:
        + *
        + *     foo
        + *     bar
        + * 
        + *

        + * + * @see CommonMark Spec + */ public class IndentedCodeBlock extends Block { private String literal; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java index 448c0b063db..c4be61cf62f 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Link.java @@ -50,7 +50,7 @@ * Note that the text in the link can contain inline formatting, so it could also contain an {@link Image} or * {@link Emphasis}, etc. * - * @see CommonMark Spec for links + * @see CommonMark Spec */ public class Link extends Node { @@ -78,6 +78,9 @@ public void setDestination(String destination) { this.destination = destination; } + /** + * @return the title or null + */ public String getTitle() { return title; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java index 9f3e7107e78..555493b4def 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/LinkReferenceDefinition.java @@ -41,9 +41,9 @@ * They can be referenced anywhere else in the document to produce a link using [foo]. The definitions * themselves are usually not rendered in the final output. * - * @see Link reference definitions + * @see CommonMark Spec */ -public class LinkReferenceDefinition extends Node { +public class LinkReferenceDefinition extends Block { private String label; private String destination; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java index 67cccde093a..fa1f4b93642 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListBlock.java @@ -32,13 +32,16 @@ package jdk.internal.org.commonmark.node; +/** + * A list block like {@link BulletList} or {@link OrderedList}. + */ public abstract class ListBlock extends Block { private boolean tight; /** * @return whether this list is tight or loose - * @see CommonMark Spec for tight lists + * @see CommonMark Spec for tight lists */ public boolean isTight() { return tight; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java index 6e1a5d15fb3..8230217a2e2 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ListItem.java @@ -32,6 +32,15 @@ package jdk.internal.org.commonmark.node; +/** + * A child of a {@link ListBlock}, containing other blocks (e.g. {@link Paragraph}, other lists, etc). + *

        + * Note that a list item can't directly contain {@link Text}, it needs to be: + * {@link ListItem} : {@link Paragraph} : {@link Text}. + * If you want a list that is rendered tightly, create a list with {@link ListBlock#setTight(boolean)}. + * + * @see CommonMark Spec: List items + */ public class ListItem extends Block { private Integer markerIndent; @@ -84,4 +93,18 @@ public Integer getContentIndent() { public void setContentIndent(Integer contentIndent) { this.contentIndent = contentIndent; } + + /** + * @deprecated list items should only contain block nodes; if you're trying to create a list that is rendered + * without paragraphs, use {@link ListBlock#setTight(boolean)} instead. + */ + @Override + @Deprecated + public void appendChild(Node child) { + super.appendChild(child); + } + + public void appendChild(Block child) { + super.appendChild(child); + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java index 1607ec2ef5f..821d1b35207 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Node.java @@ -82,11 +82,10 @@ public void appendChild(Node child) { if (this.lastChild != null) { this.lastChild.next = child; child.prev = this.lastChild; - this.lastChild = child; } else { this.firstChild = child; - this.lastChild = child; } + this.lastChild = child; } public void prependChild(Node child) { @@ -118,6 +117,9 @@ public void unlink() { this.prev = null; } + /** + * Inserts the {@code sibling} node after {@code this} node. + */ public void insertAfter(Node sibling) { sibling.unlink(); sibling.next = this.next; @@ -132,6 +134,9 @@ public void insertAfter(Node sibling) { } } + /** + * Inserts the {@code sibling} node before {@code this} node. + */ public void insertBefore(Node sibling) { sibling.unlink(); sibling.prev = this.prev; @@ -146,13 +151,12 @@ public void insertBefore(Node sibling) { } } - /** * @return the source spans of this node if included by the parser, an empty list otherwise * @since 0.16.0 */ public List getSourceSpans() { - return sourceSpans != null ? Collections.unmodifiableList(sourceSpans) : Collections.emptyList(); + return sourceSpans != null ? Collections.unmodifiableList(sourceSpans) : List.of(); } /** diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java index ebee8422f08..38087dadd95 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/OrderedList.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * An ordered list, e.g.: + *

        
        + * 1. One
        + * 2. Two
        + * 3. Three
        + * 
        + *

        + * The children are {@link ListItem} blocks, which contain other blocks (or nested lists). + * + * @see CommonMark Spec: List items + */ public class OrderedList extends ListBlock { private String markerDelimiter; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java index 32168c0251e..5f6ed28487c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Paragraph.java @@ -34,6 +34,8 @@ /** * A paragraph block, contains inline nodes such as {@link Text} + * + * @see CommonMark Spec */ public class Paragraph extends Block { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java index 1fedc5c899b..ab63f57c8d3 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SoftLineBreak.java @@ -32,6 +32,15 @@ package jdk.internal.org.commonmark.node; +/** + * A soft line break (as opposed to a {@link HardLineBreak}), e.g. between: + *

        + * foo
        + * bar
        + * 
        + * + * @see CommonMark Spec + */ public class SoftLineBreak extends Node { @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java index b816a8bf437..dfa909a211e 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpan.java @@ -59,32 +59,64 @@ public class SourceSpan { private final int lineIndex; private final int columnIndex; + private final int inputIndex; private final int length; + public static SourceSpan of(int line, int col, int input, int length) { + return new SourceSpan(line, col, input, length); + } + + /** + * @deprecated Use {{@link #of(int, int, int, int)}} instead to also specify input index. Using the deprecated one + * will set {@link #inputIndex} to 0. + */ + @Deprecated public static SourceSpan of(int lineIndex, int columnIndex, int length) { - return new SourceSpan(lineIndex, columnIndex, length); + return of(lineIndex, columnIndex, 0, length); } - private SourceSpan(int lineIndex, int columnIndex, int length) { + private SourceSpan(int lineIndex, int columnIndex, int inputIndex, int length) { + if (lineIndex < 0) { + throw new IllegalArgumentException("lineIndex " + lineIndex + " must be >= 0"); + } + if (columnIndex < 0) { + throw new IllegalArgumentException("columnIndex " + columnIndex + " must be >= 0"); + } + if (inputIndex < 0) { + throw new IllegalArgumentException("inputIndex " + inputIndex + " must be >= 0"); + } + if (length < 0) { + throw new IllegalArgumentException("length " + length + " must be >= 0"); + } this.lineIndex = lineIndex; this.columnIndex = columnIndex; + this.inputIndex = inputIndex; this.length = length; } /** - * @return 0-based index of line in source + * @return 0-based line index, e.g. 0 for first line, 1 for the second line, etc */ public int getLineIndex() { return lineIndex; } /** - * @return 0-based index of column (character on line) in source + * @return 0-based index of column (character on line) in source, e.g. 0 for the first character of a line, 1 for + * the second character, etc */ public int getColumnIndex() { return columnIndex; } + /** + * @return 0-based index in whole input + * @since 0.24.0 + */ + public int getInputIndex() { + return inputIndex; + } + /** * @return length of the span in characters */ @@ -92,6 +124,32 @@ public int getLength() { return length; } + public SourceSpan subSpan(int beginIndex) { + return subSpan(beginIndex, length); + } + + public SourceSpan subSpan(int beginIndex, int endIndex) { + if (beginIndex < 0) { + throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " + must be >= 0"); + } + if (beginIndex > length) { + throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " must be <= length " + length); + } + if (endIndex < 0) { + throw new IndexOutOfBoundsException("endIndex " + endIndex + " + must be >= 0"); + } + if (endIndex > length) { + throw new IndexOutOfBoundsException("endIndex " + endIndex + " must be <= length " + length); + } + if (beginIndex > endIndex) { + throw new IndexOutOfBoundsException("beginIndex " + beginIndex + " must be <= endIndex " + endIndex); + } + if (beginIndex == 0 && endIndex == length) { + return this; + } + return new SourceSpan(lineIndex, columnIndex + beginIndex, inputIndex + beginIndex, endIndex - beginIndex); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -103,12 +161,13 @@ public boolean equals(Object o) { SourceSpan that = (SourceSpan) o; return lineIndex == that.lineIndex && columnIndex == that.columnIndex && + inputIndex == that.inputIndex && length == that.length; } @Override public int hashCode() { - return Objects.hash(lineIndex, columnIndex, length); + return Objects.hash(lineIndex, columnIndex, inputIndex, length); } @Override @@ -116,6 +175,7 @@ public String toString() { return "SourceSpan{" + "line=" + lineIndex + ", column=" + columnIndex + + ", input=" + inputIndex + ", length=" + length + "}"; } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java index 4a35a89840d..787771bcc08 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/SourceSpans.java @@ -33,7 +33,6 @@ package jdk.internal.org.commonmark.node; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -50,7 +49,7 @@ public static SourceSpans empty() { } public List getSourceSpans() { - return sourceSpans != null ? sourceSpans : Collections.emptyList(); + return sourceSpans != null ? sourceSpans : List.of(); } public void addAllFrom(Iterable nodes) { @@ -74,8 +73,8 @@ public void addAll(List other) { int lastIndex = sourceSpans.size() - 1; SourceSpan a = sourceSpans.get(lastIndex); SourceSpan b = other.get(0); - if (a.getLineIndex() == b.getLineIndex() && a.getColumnIndex() + a.getLength() == b.getColumnIndex()) { - sourceSpans.set(lastIndex, SourceSpan.of(a.getLineIndex(), a.getColumnIndex(), a.getLength() + b.getLength())); + if (a.getInputIndex() + a.getLength() == b.getInputIndex()) { + sourceSpans.set(lastIndex, SourceSpan.of(a.getLineIndex(), a.getColumnIndex(), a.getInputIndex(), a.getLength() + b.getLength())); sourceSpans.addAll(other.subList(1, other.size())); } else { sourceSpans.addAll(other); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java index 1efed6d9929..38c07352c52 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/StrongEmphasis.java @@ -32,6 +32,14 @@ package jdk.internal.org.commonmark.node; +/** + * Strong emphasis, e.g.: + *
        
        + * Some **strong emphasis** or __strong emphasis__
        + * 
        + * + * @see CommonMark Spec: Emphasis and strong emphasis + */ public class StrongEmphasis extends Node implements Delimited { private String delimiter; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java index 976134d1dd0..15612771147 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/Text.java @@ -32,6 +32,16 @@ package jdk.internal.org.commonmark.node; +/** + * A text node, e.g. in: + *
        + * foo *bar*
        + * 
        + *

        + * The foo is a text node, and the bar inside the emphasis is also a text node. + * + * @see CommonMark Spec + */ public class Text extends Node { private String literal; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java index 8be13266a33..2cfee131236 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/node/ThematicBreak.java @@ -32,6 +32,18 @@ package jdk.internal.org.commonmark.node; +/** + * A thematic break, e.g. between text: + *

        + * Some text
        + *
        + * ___
        + *
        + * Some other text.
        + * 
        + * + * @see CommonMark Spec + */ public class ThematicBreak extends Block { private String literal; diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java index b28faff22be..e89e69866fa 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserContext.java @@ -33,9 +33,12 @@ package jdk.internal.org.commonmark.parser; import jdk.internal.org.commonmark.node.LinkReferenceDefinition; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; +import jdk.internal.org.commonmark.parser.beta.InlineContentParserFactory; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import java.util.List; +import java.util.Set; /** * Context for inline parsing. @@ -43,17 +46,47 @@ public interface InlineParserContext { /** - * @return custom delimiter processors that have been configured with {@link Parser.Builder#customDelimiterProcessor(DelimiterProcessor)} + * @return custom inline content parsers that have been configured with + * {@link Parser.Builder#customInlineContentParserFactory(InlineContentParserFactory)} + */ + List getCustomInlineContentParserFactories(); + + /** + * @return custom delimiter processors that have been configured with + * {@link Parser.Builder#customDelimiterProcessor(DelimiterProcessor)} */ List getCustomDelimiterProcessors(); + /** + * @return custom link processors that have been configured with {@link Parser.Builder#linkProcessor}. + */ + List getCustomLinkProcessors(); + + /** + * @return custom link markers that have been configured with {@link Parser.Builder#linkMarker}. + */ + Set getCustomLinkMarkers(); + /** * Look up a {@link LinkReferenceDefinition} for a given label. *

        - * Note that the label is not normalized yet; implementations are responsible for normalizing before lookup. + * Note that the passed in label does not need to be normalized; implementations are responsible for doing the + * normalization before lookup. * * @param label the link label to look up * @return the definition if one exists, {@code null} otherwise + * @deprecated use {@link #getDefinition} with {@link LinkReferenceDefinition} instead */ + @Deprecated LinkReferenceDefinition getLinkReferenceDefinition(String label); + + /** + * Look up a definition of a type for a given label. + *

        + * Note that the passed in label does not need to be normalized; implementations are responsible for doing the + * normalization before lookup. + * + * @return the definition if one exists, null otherwise + */ + D getDefinition(Class type, String label); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java index 6fb0860f48e..79351ff990e 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/InlineParserFactory.java @@ -36,5 +36,9 @@ * Factory for custom inline parser. */ public interface InlineParserFactory { + + /** + * Create an {@link InlineParser} to use for parsing inlines. This is called once per parsed document. + */ InlineParser create(InlineParserContext inlineParserContext); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java index 40712b97873..938c5a2273c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/Parser.java @@ -33,19 +33,21 @@ package jdk.internal.org.commonmark.parser; import jdk.internal.org.commonmark.Extension; +import jdk.internal.org.commonmark.internal.Definitions; import jdk.internal.org.commonmark.internal.DocumentParser; import jdk.internal.org.commonmark.internal.InlineParserContextImpl; import jdk.internal.org.commonmark.internal.InlineParserImpl; -import jdk.internal.org.commonmark.internal.LinkReferenceDefinitions; import jdk.internal.org.commonmark.node.*; +import jdk.internal.org.commonmark.parser.beta.LinkInfo; +import jdk.internal.org.commonmark.parser.beta.LinkProcessor; +import jdk.internal.org.commonmark.parser.beta.InlineContentParserFactory; +import jdk.internal.org.commonmark.parser.beta.LinkResult; import jdk.internal.org.commonmark.parser.block.BlockParserFactory; import jdk.internal.org.commonmark.parser.delimiter.DelimiterProcessor; import java.io.IOException; import java.io.Reader; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; /** @@ -60,21 +62,31 @@ public class Parser { private final List blockParserFactories; + private final List inlineContentParserFactories; private final List delimiterProcessors; + private final List linkProcessors; + private final Set linkMarkers; private final InlineParserFactory inlineParserFactory; private final List postProcessors; private final IncludeSourceSpans includeSourceSpans; + private final int maxOpenBlockParsers; private Parser(Builder builder) { this.blockParserFactories = DocumentParser.calculateBlockParserFactories(builder.blockParserFactories, builder.enabledBlockTypes); this.inlineParserFactory = builder.getInlineParserFactory(); this.postProcessors = builder.postProcessors; + this.inlineContentParserFactories = builder.inlineContentParserFactories; this.delimiterProcessors = builder.delimiterProcessors; + this.linkProcessors = builder.linkProcessors; + this.linkMarkers = builder.linkMarkers; this.includeSourceSpans = builder.includeSourceSpans; + this.maxOpenBlockParsers = builder.maxOpenBlockParsers; // Try to construct an inline parser. Invalid configuration might result in an exception, which we want to // detect as soon as possible. - this.inlineParserFactory.create(new InlineParserContextImpl(delimiterProcessors, new LinkReferenceDefinitions())); + var context = new InlineParserContextImpl( + inlineContentParserFactories, delimiterProcessors, linkProcessors, linkMarkers, new Definitions()); + this.inlineParserFactory.create(context); } /** @@ -95,9 +107,7 @@ public static Builder builder() { * @return the root node */ public Node parse(String input) { - if (input == null) { - throw new NullPointerException("input must not be null"); - } + Objects.requireNonNull(input, "input must not be null"); DocumentParser documentParser = createDocumentParser(); Node document = documentParser.parse(input); return postProcess(document); @@ -122,17 +132,15 @@ public Node parse(String input) { * @throws IOException when reading throws an exception */ public Node parseReader(Reader input) throws IOException { - if (input == null) { - throw new NullPointerException("input must not be null"); - } - + Objects.requireNonNull(input, "input must not be null"); DocumentParser documentParser = createDocumentParser(); Node document = documentParser.parse(input); return postProcess(document); } private DocumentParser createDocumentParser() { - return new DocumentParser(blockParserFactories, inlineParserFactory, delimiterProcessors, includeSourceSpans); + return new DocumentParser(blockParserFactories, inlineParserFactory, inlineContentParserFactories, + delimiterProcessors, linkProcessors, linkMarkers, includeSourceSpans, maxOpenBlockParsers); } private Node postProcess(Node document) { @@ -147,11 +155,15 @@ private Node postProcess(Node document) { */ public static class Builder { private final List blockParserFactories = new ArrayList<>(); + private final List inlineContentParserFactories = new ArrayList<>(); private final List delimiterProcessors = new ArrayList<>(); + private final List linkProcessors = new ArrayList<>(); private final List postProcessors = new ArrayList<>(); + private final Set linkMarkers = new HashSet<>(); private Set> enabledBlockTypes = DocumentParser.getDefaultBlockParserTypes(); private InlineParserFactory inlineParserFactory; private IncludeSourceSpans includeSourceSpans = IncludeSourceSpans.NONE; + private int maxOpenBlockParsers = Integer.MAX_VALUE; /** * @return the configured {@link Parser} @@ -165,9 +177,7 @@ public Parser build() { * @return {@code this} */ public Builder extensions(Iterable extensions) { - if (extensions == null) { - throw new NullPointerException("extensions must not be null"); - } + Objects.requireNonNull(extensions, "extensions must not be null"); for (Extension extension : extensions) { if (extension instanceof ParserExtension) { ParserExtension parserExtension = (ParserExtension) extension; @@ -196,25 +206,23 @@ public Builder extensions(Iterable extensions) { * E.g., to only parse headings and lists: *

                  *     {@code
        -         *     Parser.builder().enabledBlockTypes(new HashSet<>(Arrays.asList(Heading.class, ListBlock.class)));
        +         *     Parser.builder().enabledBlockTypes(Set.of(Heading.class, ListBlock.class));
                  *     }
                  * 
        * * @param enabledBlockTypes A list of block nodes the parser will parse. - * If this list is empty, the parser will not recognize any CommonMark core features. + * If this list is empty, the parser will not recognize any CommonMark core features. * @return {@code this} */ public Builder enabledBlockTypes(Set> enabledBlockTypes) { - if (enabledBlockTypes == null) { - throw new NullPointerException("enabledBlockTypes must not be null"); - } + Objects.requireNonNull(enabledBlockTypes, "enabledBlockTypes must not be null"); DocumentParser.checkEnabledBlockTypes(enabledBlockTypes); this.enabledBlockTypes = enabledBlockTypes; return this; } /** - * Whether to calculate {@link org.commonmark.node.SourceSpan} for {@link Node}. + * Whether to calculate source positions for parsed {@link Node Nodes}, see {@link Node#getSourceSpans()}. *

        * By default, source spans are disabled. * @@ -228,7 +236,28 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) { } /** - * Adds a custom block parser factory. + * Limit how many block parsers may be open at once while parsing. + *

        + * Once the limit is reached, additional block starts are treated as plain text instead of + * creating deeper nested block structure. + *

        + * The document root parser is not counted. The default is unlimited, so callers that keep + * using {@code Parser.builder().build()} preserve behavior. + * + * @param maxOpenBlockParsers maximum number of open non-document block parsers, must be + * zero or greater + * @return {@code this} + */ + public Builder maxOpenBlockParsers(int maxOpenBlockParsers) { + if (maxOpenBlockParsers < 0) { + throw new IllegalArgumentException("maxOpenBlockParsers must be >= 0"); + } + this.maxOpenBlockParsers = maxOpenBlockParsers; + return this; + } + + /** + * Add a custom block parser factory. *

        * Note that custom factories are applied before the built-in factories. This is so that * extensions can change how some syntax is parsed that would otherwise be handled by built-in factories. @@ -238,35 +267,78 @@ public Builder includeSourceSpans(IncludeSourceSpans includeSourceSpans) { * @return {@code this} */ public Builder customBlockParserFactory(BlockParserFactory blockParserFactory) { - if (blockParserFactory == null) { - throw new NullPointerException("blockParserFactory must not be null"); - } + Objects.requireNonNull(blockParserFactory, "blockParserFactory must not be null"); blockParserFactories.add(blockParserFactory); return this; } /** - * Adds a custom delimiter processor. + * Add a factory for a custom inline content parser, for extending inline parsing or overriding built-in parsing. + *

        + * Note that parsers are triggered based on a special character as specified by + * {@link InlineContentParserFactory#getTriggerCharacters()}. It is possible to register multiple parsers for the same + * character, or even for some built-in special character such as {@code `}. The custom parsers are tried first + * in order in which they are registered, and then the built-in ones. + */ + public Builder customInlineContentParserFactory(InlineContentParserFactory inlineContentParserFactory) { + Objects.requireNonNull(inlineContentParserFactory, "inlineContentParser must not be null"); + inlineContentParserFactories.add(inlineContentParserFactory); + return this; + } + + /** + * Add a custom delimiter processor for inline parsing. *

        * Note that multiple delimiter processors with the same characters can be added, as long as they have a * different minimum length. In that case, the processor with the shortest matching length is used. Adding more * than one delimiter processor with the same character and minimum length is invalid. + *

        + * If you want more control over how parsing is done, you might want to use + * {@link #customInlineContentParserFactory} instead. * * @param delimiterProcessor a delimiter processor implementation * @return {@code this} */ public Builder customDelimiterProcessor(DelimiterProcessor delimiterProcessor) { - if (delimiterProcessor == null) { - throw new NullPointerException("delimiterProcessor must not be null"); - } + Objects.requireNonNull(delimiterProcessor, "delimiterProcessor must not be null"); delimiterProcessors.add(delimiterProcessor); return this; } + /** + * Add a custom link/image processor for inline parsing. + *

        + * Multiple link processors can be added, and will be tried in order in which they were added. If no link + * processor applies, the normal behavior applies. That means these can override built-in link parsing. + * + * @param linkProcessor a link processor implementation + * @return {@code this} + */ + public Builder linkProcessor(LinkProcessor linkProcessor) { + Objects.requireNonNull(linkProcessor, "linkProcessor must not be null"); + linkProcessors.add(linkProcessor); + return this; + } + + /** + * Add a custom link marker for link processing. A link marker is a character like {@code !} which, if it + * appears before the {@code [} of a link, changes the meaning of the link. + *

        + * If a link marker followed by a valid link is parsed, the {@link org.commonmark.parser.beta.LinkInfo} + * that is passed to {@link LinkProcessor} will have its {@link LinkInfo#marker()} set. A link processor should + * check the {@link Text#getLiteral()} and then do any processing, and will probably want to use {@link LinkResult#includeMarker()}. + * + * @param linkMarker a link marker character + * @return {@code this} + */ + public Builder linkMarker(Character linkMarker) { + Objects.requireNonNull(linkMarker, "linkMarker must not be null"); + linkMarkers.add(linkMarker); + return this; + } + public Builder postProcessor(PostProcessor postProcessor) { - if (postProcessor == null) { - throw new NullPointerException("postProcessor must not be null"); - } + Objects.requireNonNull(postProcessor, "postProcessor must not be null"); postProcessors.add(postProcessor); return this; } @@ -297,13 +369,9 @@ public Builder inlineParserFactory(InlineParserFactory inlineParserFactory) { private InlineParserFactory getInlineParserFactory() { if (inlineParserFactory != null) { return inlineParserFactory; + } else { + return InlineParserImpl::new; } - return new InlineParserFactory() { - @Override - public InlineParser create(InlineParserContext inlineParserContext) { - return new InlineParserImpl(inlineParserContext); - } - }; } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java index 4008e6bece0..0672d7dfbb5 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/SourceLine.java @@ -34,6 +34,8 @@ import jdk.internal.org.commonmark.node.SourceSpan; +import java.util.Objects; + /** * A line or part of a line from the input source. * @@ -49,10 +51,7 @@ public static SourceLine of(CharSequence content, SourceSpan sourceSpan) { } private SourceLine(CharSequence content, SourceSpan sourceSpan) { - if (content == null) { - throw new NullPointerException("content must not be null"); - } - this.content = content; + this.content = Objects.requireNonNull(content, "content must not be null"); this.sourceSpan = sourceSpan; } @@ -68,10 +67,11 @@ public SourceLine substring(int beginIndex, int endIndex) { CharSequence newContent = content.subSequence(beginIndex, endIndex); SourceSpan newSourceSpan = null; if (sourceSpan != null) { - int columnIndex = sourceSpan.getColumnIndex() + beginIndex; int length = endIndex - beginIndex; if (length != 0) { - newSourceSpan = SourceSpan.of(sourceSpan.getLineIndex(), columnIndex, length); + int columnIndex = sourceSpan.getColumnIndex() + beginIndex; + int inputIndex = sourceSpan.getInputIndex() + beginIndex; + newSourceSpan = SourceSpan.of(sourceSpan.getLineIndex(), columnIndex, inputIndex, length); } } return SourceLine.of(newContent, newSourceSpan); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParser.java similarity index 61% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParser.java index 66feff21784..bc8c5f58771 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineContentParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParser.java @@ -30,9 +30,24 @@ * should not be removed. */ -package jdk.internal.org.commonmark.internal.inline; +package jdk.internal.org.commonmark.parser.beta; +/** + * Parser for a type of inline content. Registered via a {@link InlineContentParserFactory} and created by its + * {@link InlineContentParserFactory#create() create} method. The lifetime of this is tied to each inline content + * snippet that is parsed, as a new instance is created for each. + */ public interface InlineContentParser { + /** + * Try to parse inline content starting from the current position. Note that the character at the current position + * is one of {@link InlineContentParserFactory#getTriggerCharacters()} of the factory that created this parser. + *

        + * For a given inline content snippet that is being parsed, this method can be called multiple times: each time a + * trigger character is encountered. + * + * @param inlineParserState the current state of the inline parser + * @return the result of parsing; can indicate that this parser is not interested, or that parsing was successful + */ ParsedInline tryParse(InlineParserState inlineParserState); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/OrderedListHolder.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParserFactory.java similarity index 61% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/OrderedListHolder.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParserFactory.java index 114c4dfeb29..cd56e1fb185 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/renderer/text/OrderedListHolder.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineContentParserFactory.java @@ -30,29 +30,27 @@ * should not be removed. */ -package jdk.internal.org.commonmark.internal.renderer.text; +package jdk.internal.org.commonmark.parser.beta; -import jdk.internal.org.commonmark.node.OrderedList; +import java.util.Set; -public class OrderedListHolder extends ListHolder { - private final String delimiter; - private int counter; - - public OrderedListHolder(ListHolder parent, OrderedList list) { - super(parent); - delimiter = list.getMarkerDelimiter() != null ? list.getMarkerDelimiter() : "."; - counter = list.getMarkerStartNumber() != null ? list.getMarkerStartNumber() : 1; - } - - public String getDelimiter() { - return delimiter; - } - - public int getCounter() { - return counter; - } - - public void increaseCounter() { - counter++; - } +/** + * A factory for extending inline content parsing. + *

        + * See {@link org.commonmark.parser.Parser.Builder#customInlineContentParserFactory} for how to register it. + */ +public interface InlineContentParserFactory { + + /** + * An inline content parser needs to have a special "trigger" character which activates it. When this character is + * encountered during inline parsing, {@link InlineContentParser#tryParse} is called with the current parser state. + * It can also register for more than one trigger character. + */ + Set getTriggerCharacters(); + + /** + * Create an {@link InlineContentParser} that will do the parsing. Create is called once per text snippet of inline + * content inside block structures, and then called each time a trigger character is encountered. + */ + InlineContentParser create(); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineParserState.java similarity index 88% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineParserState.java index 6890364fca0..e4efdf7f3d6 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/InlineParserState.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/InlineParserState.java @@ -30,16 +30,13 @@ * should not be removed. */ -package jdk.internal.org.commonmark.internal.inline; - -import jdk.internal.org.commonmark.parser.beta.Position; -import jdk.internal.org.commonmark.parser.beta.Scanner; +package jdk.internal.org.commonmark.parser.beta; public interface InlineParserState { /** - * Return a scanner for the input for the current position (on the character that the inline parser registered - * interest for). + * Return a scanner for the input for the current position (on the trigger character that the inline parser was + * added for). *

        * Note that this always returns the same instance, if you want to backtrack you need to use * {@link Scanner#position()} and {@link Scanner#setPosition(Position)}. diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkInfo.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkInfo.java new file mode 100644 index 00000000000..366347e2040 --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkInfo.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.parser.beta; + +import jdk.internal.org.commonmark.node.Text; + +/** + * A parsed link/image. There are different types of links. + *

        + * Inline links: + *

        + * [text](destination)
        + * [text](destination "title")
        + * 
        + *

        + * Reference links, which have different subtypes. Full:: + *

        + * [text][label]
        + * 
        + * Collapsed (label is ""): + *
        + * [text][]
        + * 
        + * Shortcut (label is null): + *
        + * [text]
        + * 
        + * Images use the same syntax as links but with a {@code !} {@link #marker()} front, e.g. {@code ![text](destination)}. + */ +public interface LinkInfo { + + /** + * The marker if present, or null. A marker is e.g. {@code !} for an image, or a custom marker as specified in + * {@link org.commonmark.parser.Parser.Builder#linkMarker}. + */ + Text marker(); + + /** + * The text node of the opening bracket {@code [}. + */ + Text openingBracket(); + + /** + * The text between the first brackets, e.g. `foo` in `[foo][bar]`. + */ + String text(); + + /** + * The label, or null for inline links or for shortcut links (in which case {@link #text()} should be used as the label). + */ + String label(); + + /** + * The destination if available, e.g. in `[foo](destination)`, or null + */ + String destination(); + + /** + * The title if available, e.g. in `[foo](destination "title")`, or null + */ + String title(); + + /** + * The position after the closing text bracket, e.g.: + *
        +     * [foo][bar]
        +     *      ^
        +     * 
        + */ + Position afterTextBracket(); +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkProcessor.java new file mode 100644 index 00000000000..dc0e38e02e3 --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkProcessor.java @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.parser.beta; + +import jdk.internal.org.commonmark.parser.InlineParserContext; + +/** + * An interface to decide how links/images are handled. + *

        + * Implementations need to be registered with a parser via {@link org.commonmark.parser.Parser.Builder#linkProcessor}. + * Then, when inline parsing is run, each parsed link/image is passed to the processor. This includes links like these: + *

        + *

        
        + * [text](destination)
        + * [text]
        + * [text][]
        + * [text][label]
        + * 
        + * And images: + *
        
        + * ![text](destination)
        + * ![text]
        + * ![text][]
        + * ![text][label]
        + * 
        + * See {@link LinkInfo} for accessing various parts of the parsed link/image. + *

        + * The processor can then inspect the link/image and decide what to do with it by returning the appropriate + * {@link LinkResult}. If it returns {@link LinkResult#none()}, the next registered processor is tried. If none of them + * apply, the link is handled as it normally would. + */ +public interface LinkProcessor { + + /** + * @param linkInfo information about the parsed link/image + * @param scanner the scanner at the current position after the parsed link/image + * @param context context for inline parsing + * @return what to do with the link/image, e.g. do nothing (try the next processor), wrap the text in a node, or + * replace the link/image with a node + */ + LinkResult process(LinkInfo linkInfo, Scanner scanner, InlineParserContext context); +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkResult.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkResult.java new file mode 100644 index 00000000000..137d25b50d6 --- /dev/null +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/LinkResult.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, a notice that is now available elsewhere in this distribution + * accompanied the original version of this file, and, per its terms, + * should not be removed. + */ + +package jdk.internal.org.commonmark.parser.beta; + +import jdk.internal.org.commonmark.internal.inline.LinkResultImpl; +import jdk.internal.org.commonmark.node.Node; + +/** + * What to do with a link/image processed by {@link LinkProcessor}. + */ +public interface LinkResult { + /** + * Link not handled by processor. + */ + static LinkResult none() { + return null; + } + + /** + * Wrap the link text in a node. This is the normal behavior for links, e.g. for this: + *

        
        +     * [my *text*](destination)
        +     * 
        + * The text is {@code my *text*}, a text node and emphasis. The text is wrapped in a + * {@link org.commonmark.node.Link} node, which means the text is added as child nodes to it. + * + * @param node the node to which the link text nodes will be added as child nodes + * @param position the position to continue parsing from + */ + static LinkResult wrapTextIn(Node node, Position position) { + return new LinkResultImpl(LinkResultImpl.Type.WRAP, node, position); + } + + /** + * Replace the link with a node. E.g. for this: + *
        
        +     * [^foo]
        +     * 
        + * The processor could decide to create a {@code FootnoteReference} node instead which replaces the link. + * + * @param node the node to replace the link with + * @param position the position to continue parsing from + */ + static LinkResult replaceWith(Node node, Position position) { + return new LinkResultImpl(LinkResultImpl.Type.REPLACE, node, position); + } + + /** + * If a {@link LinkInfo#marker()} is present, include it in processing (i.e. treat it the same way as the brackets). + */ + LinkResult includeMarker(); +} diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/ParsedInline.java similarity index 73% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/ParsedInline.java index 2aed7db83d0..1ccb269ed02 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/ParsedInline.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/ParsedInline.java @@ -30,27 +30,27 @@ * should not be removed. */ -package jdk.internal.org.commonmark.internal.inline; +package jdk.internal.org.commonmark.parser.beta; +import jdk.internal.org.commonmark.internal.inline.ParsedInlineImpl; import jdk.internal.org.commonmark.node.Node; -import jdk.internal.org.commonmark.parser.beta.Position; -public abstract class ParsedInline { +import java.util.Objects; - protected ParsedInline() { - } +/** + * The result of a single inline parser. Use the static methods to create instances. + *

        + * This interface is not intended to be implemented by clients. + */ +public interface ParsedInline { - public static ParsedInline none() { + static ParsedInline none() { return null; } - public static ParsedInline of(Node node, Position position) { - if (node == null) { - throw new NullPointerException("node must not be null"); - } - if (position == null) { - throw new NullPointerException("position must not be null"); - } + static ParsedInline of(Node node, Position position) { + Objects.requireNonNull(node, "node must not be null"); + Objects.requireNonNull(position, "position must not be null"); return new ParsedInlineImpl(node, position); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/Scanner.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/Scanner.java index e6b1dfaa347..58878194c28 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/Scanner.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/beta/Scanner.java @@ -276,7 +276,7 @@ public SourceLines getSource(Position begin, Position end) { SourceSpan newSourceSpan = null; SourceSpan sourceSpan = line.getSourceSpan(); if (sourceSpan != null) { - newSourceSpan = SourceSpan.of(sourceSpan.getLineIndex(), sourceSpan.getColumnIndex() + begin.index, newContent.length()); + newSourceSpan = sourceSpan.subSpan(begin.index, end.index); } return SourceLines.of(SourceLine.of(newContent, newSourceSpan)); } else { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java index 26cd6220daf..550762a2b64 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/AbstractBlockParser.java @@ -33,10 +33,13 @@ package jdk.internal.org.commonmark.parser.block; import jdk.internal.org.commonmark.node.Block; +import jdk.internal.org.commonmark.node.DefinitionMap; import jdk.internal.org.commonmark.node.SourceSpan; import jdk.internal.org.commonmark.parser.InlineParser; import jdk.internal.org.commonmark.parser.SourceLine; +import java.util.List; + public abstract class AbstractBlockParser implements BlockParser { @Override @@ -63,6 +66,11 @@ public void addSourceSpan(SourceSpan sourceSpan) { getBlock().addSourceSpan(sourceSpan); } + @Override + public List> getDefinitions() { + return List.of(); + } + @Override public void closeBlock() { } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java index e893e70b0ef..1cae1035e7d 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockParser.java @@ -33,10 +33,13 @@ package jdk.internal.org.commonmark.parser.block; import jdk.internal.org.commonmark.node.Block; +import jdk.internal.org.commonmark.node.DefinitionMap; import jdk.internal.org.commonmark.node.SourceSpan; import jdk.internal.org.commonmark.parser.InlineParser; import jdk.internal.org.commonmark.parser.SourceLine; +import java.util.List; + /** * Parser for a specific block node. *

        @@ -81,6 +84,12 @@ public interface BlockParser { */ void addSourceSpan(SourceSpan sourceSpan); + /** + * Return definitions parsed by this parser. The definitions returned here can later be accessed during inline + * parsing via {@link org.commonmark.parser.InlineParserContext#getDefinition}. + */ + List> getDefinitions(); + void closeBlock(); void parseInlines(InlineParser inlineParser); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java index a927abe968b..3217b6c7038 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/BlockStart.java @@ -42,18 +42,59 @@ public abstract class BlockStart { protected BlockStart() { } + /** + * Result for when there is no block start. + */ public static BlockStart none() { return null; } + /** + * Start block(s) with the specified parser(s). + */ public static BlockStart of(BlockParser... blockParsers) { return new BlockStartImpl(blockParsers); } + /** + * Continue parsing at the specified index. + * + * @param newIndex the new index, see {@link ParserState#getIndex()} + */ public abstract BlockStart atIndex(int newIndex); + /** + * Continue parsing at the specified column (for tab handling). + * + * @param newColumn the new column, see {@link ParserState#getColumn()} + */ public abstract BlockStart atColumn(int newColumn); + /** + * @deprecated use {@link #replaceParagraphLines(int)} instead; please raise an issue if that doesn't work for you + * for some reason. + */ + @Deprecated public abstract BlockStart replaceActiveBlockParser(); + /** + * Replace a number of lines from the current paragraph (as returned by + * {@link MatchedBlockParser#getParagraphLines()}) with the new block. + *

        + * This is useful for parsing blocks that start with normal paragraphs and only have special marker syntax in later + * lines, e.g. in this: + *

        +     * Foo
        +     * ===
        +     * 
        + * The Foo line is initially parsed as a normal paragraph, then === is parsed as a heading + * marker, replacing the 1 paragraph line before. The end result is a single Heading block. + *

        + * Note that source spans from the replaced lines are automatically added to the new block. + * + * @param lines the number of lines to replace (at least 1); use {@link Integer#MAX_VALUE} to replace the whole + * paragraph + */ + public abstract BlockStart replaceParagraphLines(int lines); + } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java index 76a9669d069..469e6511b9b 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/block/MatchedBlockParser.java @@ -44,7 +44,8 @@ public interface MatchedBlockParser { BlockParser getMatchedBlockParser(); /** - * Returns the current paragraph lines if the matched block is a paragraph. + * Returns the current paragraph lines if the matched block is a paragraph. If you want to use some or all of the + * lines for starting a new block instead, use {@link BlockStart#replaceParagraphLines(int)}. * * @return paragraph content or an empty list */ diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java index 592f6a5e433..1d77debf70e 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/parser/delimiter/DelimiterProcessor.java @@ -38,6 +38,8 @@ * Custom delimiter processor for additional delimiters besides {@code _} and {@code *}. *

        * Note that implementations of this need to be thread-safe, the same instance may be used by multiple parsers. + * + * @see org.commonmark.parser.beta.InlineContentParserFactory */ public interface DelimiterProcessor { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java index 00d6c185920..59f4f468bdd 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/NodeRenderer.java @@ -52,4 +52,20 @@ public interface NodeRenderer { * @param node the node to render, will be an instance of one of {@link #getNodeTypes()} */ void render(Node node); + + /** + * Called before the root node is rendered, to do any initial processing at the start. + * + * @param rootNode the root (top-level) node + */ + default void beforeRoot(Node rootNode) { + } + + /** + * Called after the root node is rendered, to do any final processing at the end. + * + * @param rootNode the root (top-level) node + */ + default void afterRoot(Node rootNode) { + } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index 646cc7413ac..240106ab074 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -35,7 +35,9 @@ import jdk.internal.org.commonmark.node.*; import jdk.internal.org.commonmark.renderer.NodeRenderer; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; /** * The node renderer that renders all the core nodes (comes last in the order of node renderers). @@ -52,7 +54,7 @@ public CoreHtmlNodeRenderer(HtmlNodeRendererContext context) { @Override public Set> getNodeTypes() { - return new HashSet<>(Arrays.asList( + return Set.of( Document.class, Heading.class, Paragraph.class, @@ -73,7 +75,7 @@ public Set> getNodeTypes() { HtmlInline.class, SoftLineBreak.class, HardLineBreak.class - )); + ); } @Override @@ -99,13 +101,15 @@ public void visit(Heading heading) { @Override public void visit(Paragraph paragraph) { - boolean inTightList = isInTightList(paragraph); - if (!inTightList) { + boolean omitP = isInTightList(paragraph) || // + (context.shouldOmitSingleParagraphP() && paragraph.getParent() instanceof Document && // + paragraph.getPrevious() == null && paragraph.getNext() == null); + if (!omitP) { html.line(); html.tag("p", getAttrs(paragraph, "p")); } visitChildren(paragraph); - if (!inTightList) { + if (!omitP) { html.tag("/p"); html.line(); } @@ -167,7 +171,7 @@ public void visit(ThematicBreak thematicBreak) { @Override public void visit(IndentedCodeBlock indentedCodeBlock) { - renderCodeBlock(indentedCodeBlock.getLiteral(), indentedCodeBlock, Collections.emptyMap()); + renderCodeBlock(indentedCodeBlock.getLiteral(), indentedCodeBlock, Map.of()); } @Override @@ -319,7 +323,7 @@ private boolean isInTightList(Paragraph paragraph) { } private Map getAttrs(Node node, String tagName) { - return getAttrs(node, tagName, Collections.emptyMap()); + return getAttrs(node, tagName, Map.of()); } private Map getAttrs(Node node, String tagName, Map defaultAttributes) { @@ -339,6 +343,11 @@ public void visit(Text text) { sb.append(text.getLiteral()); } + @Override + public void visit(Code code) { + sb.append(code.getLiteral()); + } + @Override public void visit(SoftLineBreak softLineBreak) { sb.append('\n'); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java index 16e094afe16..ce98c93be77 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/DefaultUrlSanitizer.java @@ -32,14 +32,11 @@ package jdk.internal.org.commonmark.renderer.html; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * - * Allows http, https and mailto protocols for url. + * Allows http, https, mailto, and data protocols for url. * Also allows protocol relative urls, and relative urls. * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java */ @@ -47,7 +44,7 @@ public class DefaultUrlSanitizer implements UrlSanitizer { private Set protocols; public DefaultUrlSanitizer() { - this(Arrays.asList("http", "https", "mailto")); + this(List.of("http", "https", "mailto", "data")); } public DefaultUrlSanitizer(Collection protocols) { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java index 78dc295cb21..bbfc33ab8b7 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlNodeRendererContext.java @@ -49,8 +49,8 @@ public interface HtmlNodeRendererContext { /** * Let extensions modify the HTML tag attributes. * - * @param node the node for which the attributes are applied - * @param tagName the HTML tag name that these attributes are for (e.g. {@code h1}, {@code pre}, {@code code}). + * @param node the node for which the attributes are applied + * @param tagName the HTML tag name that these attributes are for (e.g. {@code h1}, {@code pre}, {@code code}). * @param attributes the attributes that were calculated by the renderer * @return the extended attributes with added/updated/removed entries */ @@ -79,6 +79,11 @@ public interface HtmlNodeRendererContext { */ boolean shouldEscapeHtml(); + /** + * @return whether documents that only contain a single paragraph should be rendered without the {@code

        } tag + */ + boolean shouldOmitSingleParagraphP(); + /** * @return true if the {@link UrlSanitizer} should be used. * @since 0.14.0 diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java index d4b5214211a..b720bb3ef92 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlRenderer.java @@ -36,13 +36,9 @@ import jdk.internal.org.commonmark.internal.renderer.NodeRendererMap; import jdk.internal.org.commonmark.internal.util.Escaping; import jdk.internal.org.commonmark.node.*; -import jdk.internal.org.commonmark.renderer.NodeRenderer; import jdk.internal.org.commonmark.renderer.Renderer; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Renders a tree of nodes to HTML. @@ -57,29 +53,26 @@ public class HtmlRenderer implements Renderer { private final String softbreak; private final boolean escapeHtml; + private final boolean percentEncodeUrls; + private final boolean omitSingleParagraphP; private final boolean sanitizeUrls; private final UrlSanitizer urlSanitizer; - private final boolean percentEncodeUrls; private final List attributeProviderFactories; private final List nodeRendererFactories; private HtmlRenderer(Builder builder) { this.softbreak = builder.softbreak; this.escapeHtml = builder.escapeHtml; - this.sanitizeUrls = builder.sanitizeUrls; this.percentEncodeUrls = builder.percentEncodeUrls; + this.omitSingleParagraphP = builder.omitSingleParagraphP; + this.sanitizeUrls = builder.sanitizeUrls; this.urlSanitizer = builder.urlSanitizer; this.attributeProviderFactories = new ArrayList<>(builder.attributeProviderFactories); this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1); this.nodeRendererFactories.addAll(builder.nodeRendererFactories); // Add as last. This means clients can override the rendering of core nodes if they want. - this.nodeRendererFactories.add(new HtmlNodeRendererFactory() { - @Override - public NodeRenderer create(HtmlNodeRendererContext context) { - return new CoreHtmlNodeRenderer(context); - } - }); + this.nodeRendererFactories.add(CoreHtmlNodeRenderer::new); } /** @@ -93,18 +86,16 @@ public static Builder builder() { @Override public void render(Node node, Appendable output) { - if (node == null) { - throw new NullPointerException("node must not be null"); - } + Objects.requireNonNull(node, "node must not be null"); RendererContext context = new RendererContext(new HtmlWriter(output)); + context.beforeRoot(node); context.render(node); + context.afterRoot(node); } @Override public String render(Node node) { - if (node == null) { - throw new NullPointerException("node must not be null"); - } + Objects.requireNonNull(node, "node must not be null"); StringBuilder sb = new StringBuilder(); render(node, sb); return sb.toString(); @@ -120,6 +111,7 @@ public static class Builder { private boolean sanitizeUrls = false; private UrlSanitizer urlSanitizer = new DefaultUrlSanitizer(); private boolean percentEncodeUrls = false; + private boolean omitSingleParagraphP = false; private List attributeProviderFactories = new ArrayList<>(); private List nodeRendererFactories = new ArrayList<>(); @@ -203,6 +195,17 @@ public Builder percentEncodeUrls(boolean percentEncodeUrls) { return this; } + /** + * Whether documents that only contain a single paragraph should be rendered without the {@code

        } tag. Set to + * {@code true} to render without the tag; the default of {@code false} always renders the tag. + * + * @return {@code this} + */ + public Builder omitSingleParagraphP(boolean omitSingleParagraphP) { + this.omitSingleParagraphP = omitSingleParagraphP; + return this; + } + /** * Add a factory for an attribute provider for adding/changing HTML attributes to the rendered tags. * @@ -210,9 +213,7 @@ public Builder percentEncodeUrls(boolean percentEncodeUrls) { * @return {@code this} */ public Builder attributeProviderFactory(AttributeProviderFactory attributeProviderFactory) { - if (attributeProviderFactory == null) { - throw new NullPointerException("attributeProviderFactory must not be null"); - } + Objects.requireNonNull(attributeProviderFactory, "attributeProviderFactory must not be null"); this.attributeProviderFactories.add(attributeProviderFactory); return this; } @@ -228,9 +229,7 @@ public Builder attributeProviderFactory(AttributeProviderFactory attributeProvid * @return {@code this} */ public Builder nodeRendererFactory(HtmlNodeRendererFactory nodeRendererFactory) { - if (nodeRendererFactory == null) { - throw new NullPointerException("nodeRendererFactory must not be null"); - } + Objects.requireNonNull(nodeRendererFactory, "nodeRendererFactory must not be null"); this.nodeRendererFactories.add(nodeRendererFactory); return this; } @@ -240,9 +239,7 @@ public Builder nodeRendererFactory(HtmlNodeRendererFactory nodeRendererFactory) * @return {@code this} */ public Builder extensions(Iterable extensions) { - if (extensions == null) { - throw new NullPointerException("extensions must not be null"); - } + Objects.requireNonNull(extensions, "extensions must not be null"); for (Extension extension : extensions) { if (extension instanceof HtmlRendererExtension) { HtmlRendererExtension htmlRendererExtension = (HtmlRendererExtension) extension; @@ -270,15 +267,13 @@ private RendererContext(HtmlWriter htmlWriter) { this.htmlWriter = htmlWriter; attributeProviders = new ArrayList<>(attributeProviderFactories.size()); - for (AttributeProviderFactory attributeProviderFactory : attributeProviderFactories) { + for (var attributeProviderFactory : attributeProviderFactories) { attributeProviders.add(attributeProviderFactory.create(this)); } - // The first node renderer for a node type "wins". - for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) { - HtmlNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i); - NodeRenderer nodeRenderer = nodeRendererFactory.create(this); - nodeRendererMap.add(nodeRenderer); + for (var factory : nodeRendererFactories) { + var renderer = factory.create(this); + nodeRendererMap.add(renderer); } } @@ -287,6 +282,11 @@ public boolean shouldEscapeHtml() { return escapeHtml; } + @Override + public boolean shouldOmitSingleParagraphP() { + return omitSingleParagraphP; + } + @Override public boolean shouldSanitizeUrls() { return sanitizeUrls; @@ -328,6 +328,14 @@ public void render(Node node) { nodeRendererMap.render(node); } + public void beforeRoot(Node node) { + nodeRendererMap.beforeRoot(node); + } + + public void afterRoot(Node node) { + nodeRendererMap.afterRoot(node); + } + private void setCustomAttributes(Node node, String tagName, Map attrs) { for (AttributeProvider attributeProvider : attributeProviders) { attributeProvider.setAttributes(node, tagName, attrs); diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java index 341c428a3ee..be9ee8e9ae8 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/html/HtmlWriter.java @@ -35,20 +35,18 @@ import jdk.internal.org.commonmark.internal.util.Escaping; import java.io.IOException; -import java.util.Collections; import java.util.Map; +import java.util.Objects; public class HtmlWriter { - private static final Map NO_ATTRIBUTES = Collections.emptyMap(); + private static final Map NO_ATTRIBUTES = Map.of(); private final Appendable buffer; private char lastChar = 0; public HtmlWriter(Appendable out) { - if (out == null) { - throw new NullPointerException("out must not be null"); - } + Objects.requireNonNull(out, "out must not be null"); this.buffer = out; } @@ -72,12 +70,14 @@ public void tag(String name, Map attrs, boolean voidElement) { append("<"); append(name); if (attrs != null && !attrs.isEmpty()) { - for (Map.Entry attrib : attrs.entrySet()) { + for (var attr : attrs.entrySet()) { append(" "); - append(Escaping.escapeHtml(attrib.getKey())); - append("=\""); - append(Escaping.escapeHtml(attrib.getValue())); - append("\""); + append(Escaping.escapeHtml(attr.getKey())); + if (attr.getValue() != null) { + append("=\""); + append(Escaping.escapeHtml(attr.getValue())); + append("\""); + } } } if (voidElement) { diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java index 4d543ea1174..f68140252f5 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java @@ -32,14 +32,12 @@ package jdk.internal.org.commonmark.renderer.markdown; -import jdk.internal.org.commonmark.text.AsciiMatcher; import jdk.internal.org.commonmark.node.*; -import jdk.internal.org.commonmark.text.CharMatcher; import jdk.internal.org.commonmark.renderer.NodeRenderer; +import jdk.internal.org.commonmark.text.AsciiMatcher; +import jdk.internal.org.commonmark.text.CharMatcher; import jdk.internal.org.commonmark.text.Characters; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; @@ -83,7 +81,7 @@ public CoreMarkdownNodeRenderer(MarkdownNodeRendererContext context) { @Override public Set> getNodeTypes() { - return new HashSet<>(Arrays.asList( + return Set.of( BlockQuote.class, BulletList.class, Code.class, @@ -104,7 +102,7 @@ public Set> getNodeTypes() { StrongEmphasis.class, Text.class, ThematicBreak.class - )); + ); } @Override @@ -121,8 +119,12 @@ public void visit(Document document) { @Override public void visit(ThematicBreak thematicBreak) { - // Let's use ___ as it doesn't introduce ambiguity with * or - list item markers - writer.raw("___"); + String literal = thematicBreak.getLiteral(); + if (literal == null) { + // Let's use ___ as it doesn't introduce ambiguity with * or - list item markers + literal = "___"; + } + writer.raw(literal); writer.block(); } @@ -287,7 +289,7 @@ public void visit(ListItem listItem) { throw new IllegalStateException("Unknown list holder type: " + listHolder); } Integer contentIndent = listItem.getContentIndent(); - String spaces = contentIndent != null ? repeat(" ", contentIndent - marker.length()) : " "; + String spaces = contentIndent != null ? repeat(" ", Math.max(contentIndent - marker.length(), 1)) : " "; writer.writePrefix(marker); writer.writePrefix(spaces); writer.pushPrefix(repeat(" ", marker.length() + spaces.length())); @@ -400,9 +402,11 @@ public void visit(Text text) { break; } case '=': { - // Would be ambiguous with a Setext heading, escape - writer.raw("\\="); - literal = literal.substring(1); + // Would be ambiguous with a Setext heading, escape unless it's the first line in the block + if (text.getPrevious() != null) { + writer.raw("\\="); + literal = literal.substring(1); + } break; } case '0': @@ -502,9 +506,9 @@ private static List getLines(String literal) { if (parts[parts.length - 1].isEmpty()) { // But we don't want the last empty string, as "\n" is used as a line terminator (not a separator), // so return without the last element. - return Arrays.asList(parts).subList(0, parts.length - 1); + return List.of(parts).subList(0, parts.length - 1); } else { - return Arrays.asList(parts); + return List.of(parts); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/MarkdownRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/MarkdownRenderer.java index 344a176d0d5..e1a66e63a23 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/MarkdownRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/markdown/MarkdownRenderer.java @@ -70,7 +70,7 @@ public NodeRenderer create(MarkdownNodeRendererContext context) { @Override public Set getSpecialCharacters() { - return Collections.emptySet(); + return Set.of(); } }); } @@ -162,18 +162,16 @@ private class RendererContext implements MarkdownNodeRendererContext { private RendererContext(MarkdownWriter writer) { // Set fields that are used by interface this.writer = writer; - Set escapes = new HashSet(); + Set escapes = new HashSet<>(); for (MarkdownNodeRendererFactory factory : nodeRendererFactories) { escapes.addAll(factory.getSpecialCharacters()); } additionalTextEscapes = Collections.unmodifiableSet(escapes); - // The first node renderer for a node type "wins". - for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) { - MarkdownNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i); + for (var factory : nodeRendererFactories) { // Pass in this as context here, which uses the fields set above - NodeRenderer nodeRenderer = nodeRendererFactory.create(this); - nodeRendererMap.add(nodeRenderer); + var renderer = factory.create(this); + nodeRendererMap.add(renderer); } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java index 1321d5ad785..fc9e144855c 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/CoreTextContentNodeRenderer.java @@ -34,12 +34,7 @@ import jdk.internal.org.commonmark.node.*; import jdk.internal.org.commonmark.renderer.NodeRenderer; -import jdk.internal.org.commonmark.internal.renderer.text.BulletListHolder; -import jdk.internal.org.commonmark.internal.renderer.text.ListHolder; -import jdk.internal.org.commonmark.internal.renderer.text.OrderedListHolder; -import java.util.Arrays; -import java.util.HashSet; import java.util.Set; /** @@ -59,7 +54,7 @@ public CoreTextContentNodeRenderer(TextContentNodeRendererContext context) { @Override public Set> getNodeTypes() { - return new HashSet<>(Arrays.asList( + return Set.of( Document.class, Heading.class, Paragraph.class, @@ -80,7 +75,7 @@ public Set> getNodeTypes() { HtmlInline.class, SoftLineBreak.class, HardLineBreak.class - )); + ); } @Override @@ -96,26 +91,24 @@ public void visit(Document document) { @Override public void visit(BlockQuote blockQuote) { - textContent.write('\u00ab'); + // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + textContent.write('\u00AB'); visitChildren(blockQuote); - textContent.write('\u00bb'); + textContent.resetBlock(); + // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + textContent.write('\u00BB'); - writeEndOfLineIfNeeded(blockQuote, null); + textContent.block(); } @Override public void visit(BulletList bulletList) { - if (listHolder != null) { - writeEndOfLine(); - } + textContent.pushTight(bulletList.isTight()); listHolder = new BulletListHolder(listHolder, bulletList); visitChildren(bulletList); - writeEndOfLineIfNeeded(bulletList, null); - if (listHolder.getParent() != null) { - listHolder = listHolder.getParent(); - } else { - listHolder = null; - } + textContent.popTight(); + textContent.block(); + listHolder = listHolder.getParent(); } @Override @@ -127,31 +120,40 @@ public void visit(Code code) { @Override public void visit(FencedCodeBlock fencedCodeBlock) { - if (context.stripNewlines()) { - textContent.writeStripped(fencedCodeBlock.getLiteral()); - writeEndOfLineIfNeeded(fencedCodeBlock, null); + var literal = stripTrailingNewline(fencedCodeBlock.getLiteral()); + if (stripNewlines()) { + textContent.writeStripped(literal); } else { - textContent.write(fencedCodeBlock.getLiteral()); + textContent.write(literal); } + textContent.block(); } @Override public void visit(HardLineBreak hardLineBreak) { - writeEndOfLineIfNeeded(hardLineBreak, null); + if (stripNewlines()) { + textContent.whitespace(); + } else { + textContent.line(); + } } @Override public void visit(Heading heading) { visitChildren(heading); - writeEndOfLineIfNeeded(heading, ':'); + if (stripNewlines()) { + textContent.write(": "); + } else { + textContent.block(); + } } @Override public void visit(ThematicBreak thematicBreak) { - if (!context.stripNewlines()) { + if (!stripNewlines()) { textContent.write("***"); } - writeEndOfLineIfNeeded(thematicBreak, null); + textContent.block(); } @Override @@ -171,12 +173,13 @@ public void visit(Image image) { @Override public void visit(IndentedCodeBlock indentedCodeBlock) { - if (context.stripNewlines()) { - textContent.writeStripped(indentedCodeBlock.getLiteral()); - writeEndOfLineIfNeeded(indentedCodeBlock, null); + var literal = stripTrailingNewline(indentedCodeBlock.getLiteral()); + if (stripNewlines()) { + textContent.writeStripped(literal); } else { - textContent.write(indentedCodeBlock.getLiteral()); + textContent.write(literal); } + textContent.block(); } @Override @@ -187,49 +190,56 @@ public void visit(Link link) { @Override public void visit(ListItem listItem) { if (listHolder != null && listHolder instanceof OrderedListHolder) { - OrderedListHolder orderedListHolder = (OrderedListHolder) listHolder; - String indent = context.stripNewlines() ? "" : orderedListHolder.getIndent(); - textContent.write(indent + orderedListHolder.getCounter() + orderedListHolder.getDelimiter() + " "); + var orderedListHolder = (OrderedListHolder) listHolder; + var marker = orderedListHolder.getCounter() + orderedListHolder.getDelimiter(); + var spaces = " "; + textContent.write(marker); + textContent.write(spaces); + textContent.pushPrefix(repeat(" ", marker.length() + spaces.length())); visitChildren(listItem); - writeEndOfLineIfNeeded(listItem, null); + textContent.block(); + textContent.popPrefix(); orderedListHolder.increaseCounter(); } else if (listHolder != null && listHolder instanceof BulletListHolder) { BulletListHolder bulletListHolder = (BulletListHolder) listHolder; - if (!context.stripNewlines()) { - textContent.write(bulletListHolder.getIndent() + bulletListHolder.getMarker() + " "); + if (!stripNewlines()) { + var marker = bulletListHolder.getMarker(); + var spaces = " "; + textContent.write(marker); + textContent.write(spaces); + textContent.pushPrefix(repeat(" ", marker.length() + spaces.length())); } visitChildren(listItem); - writeEndOfLineIfNeeded(listItem, null); + textContent.block(); + if (!stripNewlines()) { + textContent.popPrefix(); + } } } @Override public void visit(OrderedList orderedList) { - if (listHolder != null) { - writeEndOfLine(); - } + textContent.pushTight(orderedList.isTight()); listHolder = new OrderedListHolder(listHolder, orderedList); visitChildren(orderedList); - writeEndOfLineIfNeeded(orderedList, null); - if (listHolder.getParent() != null) { - listHolder = listHolder.getParent(); - } else { - listHolder = null; - } + textContent.popTight(); + textContent.block(); + listHolder = listHolder.getParent(); } @Override public void visit(Paragraph paragraph) { visitChildren(paragraph); - // Add "end of line" only if its "root paragraph. - if (paragraph.getParent() == null || paragraph.getParent() instanceof Document) { - writeEndOfLineIfNeeded(paragraph, null); - } + textContent.block(); } @Override public void visit(SoftLineBreak softLineBreak) { - writeEndOfLineIfNeeded(softLineBreak, null); + if (stripNewlines()) { + textContent.whitespace(); + } else { + textContent.line(); + } } @Override @@ -248,7 +258,7 @@ protected void visitChildren(Node parent) { } private void writeText(String text) { - if (context.stripNewlines()) { + if (stripNewlines()) { textContent.writeStripped(text); } else { textContent.write(text); @@ -258,7 +268,7 @@ private void writeText(String text) { private void writeLink(Node node, String title, String destination) { boolean hasChild = node.getFirstChild() != null; boolean hasTitle = title != null && !title.equals(destination); - boolean hasDestination = destination != null && !destination.equals(""); + boolean hasDestination = destination != null && !destination.isEmpty(); if (hasChild) { textContent.write('"'); @@ -287,26 +297,72 @@ private void writeLink(Node node, String title, String destination) { } } - private void writeEndOfLineIfNeeded(Node node, Character c) { - if (context.stripNewlines()) { - if (c != null) { - textContent.write(c); - } - if (node.getNext() != null) { - textContent.whitespace(); - } + private boolean stripNewlines() { + return context.lineBreakRendering() == LineBreakRendering.STRIP; + } + + private static String stripTrailingNewline(String s) { + if (s.endsWith("\n")) { + return s.substring(0, s.length() - 1); } else { - if (node.getNext() != null) { - textContent.line(); - } + return s; } } - private void writeEndOfLine() { - if (context.stripNewlines()) { - textContent.whitespace(); - } else { - textContent.line(); + // Keep for Android compat (String.repeat only available on Android 12 and later) + private static String repeat(String s, int count) { + var sb = new StringBuilder(s.length() * count); + for (int i = 0; i < count; i++) { + sb.append(s); + } + return sb.toString(); + } + + private static class BulletListHolder extends ListHolder { + private final String marker; + + public BulletListHolder(ListHolder parent, BulletList list) { + super(parent); + marker = list.getMarker(); + } + + public String getMarker() { + return marker; + } + } + + private abstract static class ListHolder { + private final ListHolder parent; + + ListHolder(ListHolder parent) { + this.parent = parent; + } + + public ListHolder getParent() { + return parent; + } + } + + private static class OrderedListHolder extends ListHolder { + private final String delimiter; + private int counter; + + public OrderedListHolder(ListHolder parent, OrderedList list) { + super(parent); + delimiter = list.getMarkerDelimiter() != null ? list.getMarkerDelimiter() : "."; + counter = list.getMarkerStartNumber() != null ? list.getMarkerStartNumber() : 1; + } + + public String getDelimiter() { + return delimiter; + } + + public int getCounter() { + return counter; + } + + public void increaseCounter() { + counter++; } } } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/LineBreakRendering.java similarity index 74% rename from src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java rename to src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/LineBreakRendering.java index fa3767083e3..7704203cc1a 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/internal/inline/Position.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/LineBreakRendering.java @@ -30,19 +30,22 @@ * should not be removed. */ -package jdk.internal.org.commonmark.internal.inline; +package jdk.internal.org.commonmark.renderer.text; /** - * Position within a {@link Scanner}. This is intentionally kept opaque so as not to expose the internal structure of - * the Scanner. + * Control how line breaks are rendered. */ -public class Position { - - final int lineIndex; - final int index; - - Position(int lineIndex, int index) { - this.lineIndex = lineIndex; - this.index = index; - } +public enum LineBreakRendering { + /** + * Strip all line breaks within blocks and between blocks, resulting in all the text in a single line. + */ + STRIP, + /** + * Use single line breaks between blocks, not a blank line (also render all lists as tight). + */ + COMPACT, + /** + * Separate blocks by a blank line (and respect tight vs loose lists). + */ + SEPARATE_BLOCKS, } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java index c00be3cca41..62fd0e235cf 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentNodeRendererContext.java @@ -36,10 +36,17 @@ public interface TextContentNodeRendererContext { + /** + * Controls how line breaks should be rendered, see {@link LineBreakRendering}. + */ + LineBreakRendering lineBreakRendering(); + /** * @return true for stripping new lines and render text as "single line", * false for keeping all line breaks. + * @deprecated Use {@link #lineBreakRendering()} instead */ + @Deprecated boolean stripNewlines(); /** diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java index 6ac282fd4c7..0c4dede8dc6 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentRenderer.java @@ -35,7 +35,6 @@ import jdk.internal.org.commonmark.Extension; import jdk.internal.org.commonmark.internal.renderer.NodeRendererMap; import jdk.internal.org.commonmark.node.Node; -import jdk.internal.org.commonmark.renderer.NodeRenderer; import jdk.internal.org.commonmark.renderer.Renderer; import java.util.ArrayList; @@ -46,22 +45,17 @@ */ public class TextContentRenderer implements Renderer { - private final boolean stripNewlines; + private final LineBreakRendering lineBreakRendering; private final List nodeRendererFactories; private TextContentRenderer(Builder builder) { - this.stripNewlines = builder.stripNewlines; + this.lineBreakRendering = builder.lineBreakRendering; this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1); this.nodeRendererFactories.addAll(builder.nodeRendererFactories); // Add as last. This means clients can override the rendering of core nodes if they want. - this.nodeRendererFactories.add(new TextContentNodeRendererFactory() { - @Override - public NodeRenderer create(TextContentNodeRendererContext context) { - return new CoreTextContentNodeRenderer(context); - } - }); + this.nodeRendererFactories.add(CoreTextContentNodeRenderer::new); } /** @@ -75,7 +69,7 @@ public static Builder builder() { @Override public void render(Node node, Appendable output) { - RendererContext context = new RendererContext(new TextContentWriter(output)); + RendererContext context = new RendererContext(new TextContentWriter(output, lineBreakRendering)); context.render(node); } @@ -91,8 +85,8 @@ public String render(Node node) { */ public static class Builder { - private boolean stripNewlines = false; private List nodeRendererFactories = new ArrayList<>(); + private LineBreakRendering lineBreakRendering = LineBreakRendering.COMPACT; /** * @return the configured {@link TextContentRenderer} @@ -101,15 +95,29 @@ public TextContentRenderer build() { return new TextContentRenderer(this); } + /** + * Configure how line breaks (newlines) are rendered, see {@link LineBreakRendering}. + * The default is {@link LineBreakRendering#COMPACT}. + * + * @param lineBreakRendering the mode to use + * @return {@code this} + */ + public Builder lineBreakRendering(LineBreakRendering lineBreakRendering) { + this.lineBreakRendering = lineBreakRendering; + return this; + } + /** * Set the value of flag for stripping new lines. * * @param stripNewlines true for stripping new lines and render text as "single line", * false for keeping all line breaks * @return {@code this} + * @deprecated Use {@link #lineBreakRendering(LineBreakRendering)} with {@link LineBreakRendering#STRIP} instead */ + @Deprecated public Builder stripNewlines(boolean stripNewlines) { - this.stripNewlines = stripNewlines; + this.lineBreakRendering = stripNewlines ? LineBreakRendering.STRIP : LineBreakRendering.COMPACT; return this; } @@ -158,17 +166,21 @@ private class RendererContext implements TextContentNodeRendererContext { private RendererContext(TextContentWriter textContentWriter) { this.textContentWriter = textContentWriter; - // The first node renderer for a node type "wins". - for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) { - TextContentNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i); - NodeRenderer nodeRenderer = nodeRendererFactory.create(this); - nodeRendererMap.add(nodeRenderer); + for (var factory : nodeRendererFactories) { + var renderer = factory.create(this); + nodeRendererMap.add(renderer); } } @Override + public LineBreakRendering lineBreakRendering() { + return lineBreakRendering; + } + + @Override + @Deprecated public boolean stripNewlines() { - return stripNewlines; + return lineBreakRendering == LineBreakRendering.STRIP; } @Override diff --git a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java index edbb7ae2839..724a17b43fd 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/org/commonmark/renderer/text/TextContentWriter.java @@ -33,47 +33,141 @@ package jdk.internal.org.commonmark.renderer.text; import java.io.IOException; +import java.util.LinkedList; public class TextContentWriter { private final Appendable buffer; + private final LineBreakRendering lineBreakRendering; + private final LinkedList prefixes = new LinkedList<>(); + private final LinkedList tight = new LinkedList<>(); + + private String blockSeparator = null; private char lastChar; public TextContentWriter(Appendable out) { - buffer = out; + this(out, LineBreakRendering.COMPACT); + } + + public TextContentWriter(Appendable out, LineBreakRendering lineBreakRendering) { + this.buffer = out; + this.lineBreakRendering = lineBreakRendering; } public void whitespace() { if (lastChar != 0 && lastChar != ' ') { - append(' '); + write(' '); } } public void colon() { if (lastChar != 0 && lastChar != ':') { - append(':'); + write(':'); } } public void line() { - if (lastChar != 0 && lastChar != '\n') { - append('\n'); - } + append('\n'); + writePrefixes(); + } + + public void block() { + blockSeparator = lineBreakRendering == LineBreakRendering.STRIP ? " " : // + lineBreakRendering == LineBreakRendering.COMPACT || isTight() ? "\n" : "\n\n"; + } + + public void resetBlock() { + blockSeparator = null; } public void writeStripped(String s) { - append(s.replaceAll("[\\r\\n\\s]+", " ")); + write(s.replaceAll("[\\r\\n\\s]+", " ")); } public void write(String s) { + flushBlockSeparator(); append(s); } public void write(char c) { + flushBlockSeparator(); append(c); } + /** + * Push a prefix onto the top of the stack. All prefixes are written at the beginning of each line, until the + * prefix is popped again. + * + * @param prefix the raw prefix string + */ + public void pushPrefix(String prefix) { + prefixes.addLast(prefix); + } + + /** + * Write a prefix. + * + * @param prefix the raw prefix string to write + */ + public void writePrefix(String prefix) { + write(prefix); + } + + /** + * Remove the last prefix from the top of the stack. + */ + public void popPrefix() { + prefixes.removeLast(); + } + + /** + * Change whether blocks are tight or loose. Loose is the default where blocks are separated by a blank line. Tight + * is where blocks are not separated by a blank line. Tight blocks are used in lists, if there are no blank lines + * within the list. + *

        + * Note that changing this does not affect block separators that have already been enqueued with {@link #block()}, + * only future ones. + */ + public void pushTight(boolean tight) { + this.tight.addLast(tight); + } + + /** + * Remove the last "tight" setting from the top of the stack. + */ + public void popTight() { + this.tight.removeLast(); + } + + private boolean isTight() { + return !tight.isEmpty() && tight.getLast(); + } + + private void writePrefixes() { + for (String prefix : prefixes) { + append(prefix); + } + } + + /** + * If a block separator has been enqueued with {@link #block()} but not yet written, write it now. + */ + private void flushBlockSeparator() { + if (blockSeparator != null) { + if (blockSeparator.equals("\n") || blockSeparator.equals("\n\n")) { + for (int i = 0; i < blockSeparator.length(); i++) { + var sep = blockSeparator.charAt(i); + append(sep); + writePrefixes(); + } + } else { + append(blockSeparator); + } + blockSeparator = null; + } + } + private void append(String s) { try { buffer.append(s); diff --git a/src/jdk.internal.md/share/legal/commonmark.md b/src/jdk.internal.md/share/legal/commonmark.md index 1fd2d7eb01b..35789ee8ccb 100644 --- a/src/jdk.internal.md/share/legal/commonmark.md +++ b/src/jdk.internal.md/share/legal/commonmark.md @@ -1,8 +1,8 @@ -## CommonMark 0.22.0 +## CommonMark 0.29.0 ### CommonMark License ``` -Copyright (c) 2015, Atlassian Pty Ltd +Copyright (c) 2015, Robin Stocker All rights reserved. Redistribution and use in source and binary forms, with or without From 054dbd7b6a64c5fd6c3885e5bd1ade03bc80bc9a Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 6 Oct 2026 16:41:57 +0000 Subject: [PATCH 71/98] 8393537: NMT: Tag the remaining Thread-related allocations appropriately Reviewed-by: dholmes, stefank --- src/hotspot/share/runtime/javaThread.cpp | 2 +- src/hotspot/share/runtime/park.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index 66057c3fc77..f4b0383a1b9 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -2029,7 +2029,7 @@ void JavaThread::pretouch_stack() { // Deferred OopHandle release support. -class OopHandleList : public CHeapObj { +class OopHandleList : public CHeapObj { static const int _count = 4; OopHandle _handles[_count]; OopHandleList* _next; diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp index 338a01bbfb9..9cf29f96e55 100644 --- a/src/hotspot/share/runtime/park.cpp +++ b/src/hotspot/share/runtime/park.cpp @@ -103,7 +103,7 @@ void ParkEvent::Release (ParkEvent * ev) { // although Niagara's hash function should help. void * ParkEvent::operator new (size_t sz) throw() { - return (void *) ((intptr_t (AllocateHeap(sz + 256, mtInternal, CALLER_PC)) + 256) & -256) ; + return (void *) ((intptr_t (AllocateHeap(sz + 256, mtSynchronizer, CALLER_PC)) + 256) & -256); } void ParkEvent::operator delete (void * a) { From f2fd22f7a23505bef47227a97a51bc9e1837111e Mon Sep 17 00:00:00 2001 From: Shawn Emery Date: Tue, 6 Oct 2026 23:32:00 +0000 Subject: [PATCH 72/98] 8392331: Cleanup AArch64 Keccak intrinsics code to be more readable Reviewed-by: aph, adinn --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index e92b463b23e..92f604c507e 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5122,12 +5122,14 @@ class StubGenerator: public StubCodeGenerator { Register state0 = c_rarg0; Register state1 = c_rarg1; - // use r3.r17,r19..r28 to keep a0..a24. + // use r3..r17,r19..r28 to keep a0..a24. + RegSetIterator regs = (RegSet::range(r0, r28) - r18_tls).begin(); // a0..a24 are respective locals from SHA3.java - const Register a[25] = { - r25, r26, r27, r3, r4, r5, r6, r7, rscratch1, rscratch2, r10, r11, r12, - r13, r14, r15, r16, r17, r28, r19, r20, r21, r22, r23, r24 }; - Register tmp0 = r0, tmp1 = r1, tmp2 = r2; + Register a[25]; + Register tmp0 = *regs, tmp1 = *++regs, tmp2 = *++regs; + for (int i = 0; i < 25; i++) { + a[i] = *++regs; + } Label rounds24_loop_0, rounds24_loop_1; @@ -8936,7 +8938,7 @@ class StubGenerator: public StubCodeGenerator { // used to store this data. // When `r18` is not reserved and frame pointers need not be preserved then - // `rfp` and `r18` are aliased to temporaries `tmp3` and `tmp4`, respectively. + // `r18` and `rfp` are aliased to temporaries `tmp3` and `tmp4`, respectively. // All other register mappings are determined by the emitter. // If `r18` or `rfp` cannot be used then 2 of the 31 live register values are @@ -8957,7 +8959,7 @@ class StubGenerator: public StubCodeGenerator { // When `r18` and `rfp` are available then `saved_regs` will be empty. In // this case push/pop will not emit any instructions, introducing no spills. void keccak_round_gpr(bool can_use_fp, bool can_use_r18, Register rc, - const Register a[], Register tmp0, Register tmp1, + Register a[], Register tmp0, Register tmp1, Register tmp2) { __ eor3(tmp1, a[4], a[9], a[14]); __ eor3(tmp0, tmp1, a[19], a[24]); // tmp0 = a4^a9^a14^a19^a24 = c4 @@ -8965,20 +8967,23 @@ class StubGenerator: public StubCodeGenerator { __ eor3(tmp1, tmp2, a[16], a[21]); // tmp1 = a1^a6^a11^a16^a21 = c1 __ rax1(tmp2, tmp0, tmp1); // d0 { - - Register tmp3, tmp4; - RegSet saved_regs; + Register tmp3, tmp4, a4 = a[4], a9 = a[9]; + RegSet saved_regs, tmp_regs; if (can_use_fp && can_use_r18) { - tmp3 = rfp; - tmp4 = r18_tls; + tmp_regs = RegSet::of(r18_tls, rfp); } else { - tmp3 = a[4]; - tmp4 = a[9]; - saved_regs = RegSet::of(tmp3, tmp4); + tmp_regs = RegSet::of(a4, a9); + a[4] = a[9] = noreg; + saved_regs = tmp_regs; } __ push(saved_regs, sp); + RegSetIterator regs = tmp_regs.begin(); + + tmp3 = *regs; + tmp4 = *++regs; + __ eor3(tmp3, a[0], a[5], a[10]); __ eor3(tmp4, tmp3, a[15], a[20]); // tmp4 = a0^a5^a10^a15^a20 = c0 __ eor(a[0], a[0], tmp2); @@ -9008,6 +9013,8 @@ class StubGenerator: public StubCodeGenerator { __ eor(a[12], a[12], tmp2); __ rax1(tmp0, tmp0, tmp4); // d4 __ pop(saved_regs, sp); + a[4] = a4; + a[9] = a9; __ eor(a[17], a[17], tmp2); __ eor(a[22], a[22], tmp2); @@ -9091,12 +9098,14 @@ class StubGenerator: public StubCodeGenerator { Register ofs = c_rarg3; Register limit = c_rarg4; - // use r3.r17,r19..r28 to keep a0..a24. + // use r3..r17,r19..r28 to keep a0..a24. + RegSetIterator regs = (RegSet::range(r0, r28) - r18_tls).begin(); // a0..a24 are respective locals from SHA3.java - const Register a[25] = { - r25, r26, r27, r3, r4, r5, r6, r7, rscratch1, rscratch2, r10, r11, r12, - r13, r14, r15, r16, r17, r28, r19, r20, r21, r22, r23, r24 }; - Register tmp0 = block_size, tmp1 = buf, tmp2 = state, tmp3 = lr; + Register a[25]; + Register tmp1 = *regs, tmp2 = *++regs, tmp0 = *++regs, tmp3 = lr; + for (int i = 0; i < 25; i++) { + a[i] = *++regs; + } Label sha3_loop, rounds24_preloop, loop_body; Label sha3_512_or_sha3_384, shake128; From c460b8a368a820278cc8b73f2f72d334b550a1d0 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Wed, 7 Oct 2026 00:49:47 +0000 Subject: [PATCH 73/98] 8393570: RISC-V: replicate_imm5 passes byte size instead of element count to vsetvli_helper Reviewed-by: fyang, gcao, ayang --- src/hotspot/cpu/riscv/riscv_v.ad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index a4cd5c83871..282b37f0251 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -3029,7 +3029,7 @@ instruct replicate_imm5(vReg dst, immI5 con) %{ format %{ "replicate_imm5 $dst, $con" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); - __ vsetvli_helper(bt, Matcher::vector_length_in_bytes(this)); + __ vsetvli_helper(bt, Matcher::vector_length(this)); __ vmv_v_i(as_VectorRegister($dst$$reg), $con$$constant); %} ins_pipe(pipe_slow); From 413d5e342304627b3fd74116174daca489478e95 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 7 Oct 2026 01:56:14 +0000 Subject: [PATCH 74/98] 8393658: Shenandoah: Disable gc/logging/TestUnifiedLoggingSwitchStress.java test Reviewed-by: wkemper, kdnilsen, pfontanilla, ruili --- .../hotspot/jtreg/gc/logging/TestUnifiedLoggingSwitchStress.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/gc/logging/TestUnifiedLoggingSwitchStress.java b/test/hotspot/jtreg/gc/logging/TestUnifiedLoggingSwitchStress.java index a1604a2b05e..90fae07c251 100644 --- a/test/hotspot/jtreg/gc/logging/TestUnifiedLoggingSwitchStress.java +++ b/test/hotspot/jtreg/gc/logging/TestUnifiedLoggingSwitchStress.java @@ -46,6 +46,7 @@ * @summary Switches gc log level on fly while stressing memory/gc * @requires !vm.flightRecorder * @requires vm.gc != "Z" + * @requires vm.gc != "Shenandoah" * @library /test/lib / * @modules java.management java.base/jdk.internal.misc * From 093af1620073b49ca6b12c828989f14dc0b162f2 Mon Sep 17 00:00:00 2001 From: Harshit Dhiman Date: Wed, 7 Oct 2026 06:02:25 +0000 Subject: [PATCH 75/98] 8388196: [S390] Port zgc to s390 8388197: [S390] Support for Concurrent Thread-Stack Processing Reviewed-by: amitkumar, sroy --- make/autoconf/jvm-features.m4 | 3 +- src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp | 19 +- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 3 +- src/hotspot/cpu/s390/c2_CodeStubs_s390.cpp | 57 + src/hotspot/cpu/s390/downcallLinker_s390.cpp | 2 +- src/hotspot/cpu/s390/gc/z/zAddress_s390.cpp | 108 ++ src/hotspot/cpu/s390/gc/z/zAddress_s390.hpp | 36 + .../cpu/s390/gc/z/zAddress_s390.inline.hpp | 39 + .../s390/gc/z/zBarrierSetAssembler_s390.cpp | 1030 +++++++++++++++++ .../s390/gc/z/zBarrierSetAssembler_s390.hpp | 197 ++++ src/hotspot/cpu/s390/gc/z/zGlobals_s390.hpp | 33 + src/hotspot/cpu/s390/gc/z/z_s390.ad | 216 ++++ src/hotspot/cpu/s390/interp_masm_s390.cpp | 28 +- src/hotspot/cpu/s390/interp_masm_s390.hpp | 2 +- src/hotspot/cpu/s390/jniFastGetField_s390.cpp | 2 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 24 +- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 2 +- src/hotspot/cpu/s390/methodHandles_s390.cpp | 14 +- src/hotspot/cpu/s390/s390.ad | 22 +- src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 2 +- src/hotspot/cpu/s390/stubGenerator_s390.cpp | 21 +- .../templateInterpreterGenerator_s390.cpp | 11 +- src/hotspot/cpu/s390/templateTable_s390.cpp | 10 +- src/hotspot/cpu/s390/vm_version_s390.hpp | 2 + .../linux_s390/gc/z/zSyscall_linux_s390.hpp | 41 + 25 files changed, 1879 insertions(+), 45 deletions(-) create mode 100644 src/hotspot/cpu/s390/c2_CodeStubs_s390.cpp create mode 100644 src/hotspot/cpu/s390/gc/z/zAddress_s390.cpp create mode 100644 src/hotspot/cpu/s390/gc/z/zAddress_s390.hpp create mode 100644 src/hotspot/cpu/s390/gc/z/zAddress_s390.inline.hpp create mode 100644 src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.cpp create mode 100644 src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.hpp create mode 100644 src/hotspot/cpu/s390/gc/z/zGlobals_s390.hpp create mode 100644 src/hotspot/cpu/s390/gc/z/z_s390.ad create mode 100644 src/hotspot/os_cpu/linux_s390/gc/z/zSyscall_linux_s390.hpp diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index 0696c4f1199..99a6d17cdeb 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -313,7 +313,8 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_ZGC], AVAILABLE=false fi elif test "x$OPENJDK_TARGET_CPU" = "xppc64le" || \ - test "x$OPENJDK_TARGET_CPU" = "xriscv64"; then + test "x$OPENJDK_TARGET_CPU" = "xriscv64" || \ + test "x$OPENJDK_TARGET_CPU" = "xs390x"; then if test "x$OPENJDK_TARGET_OS" = "xlinux"; then AC_MSG_RESULT([yes]) else diff --git a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp index 47876fe4243..a80683ca653 100644 --- a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp +++ b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp @@ -41,7 +41,24 @@ #define CHECK_BAILOUT() { if (ce->compilation()->bailed_out()) return; } void C1SafepointPollStub::emit_code(LIR_Assembler* ce) { - ShouldNotReachHere(); + __ bind(_entry); + assert(SharedRuntime::polling_page_return_handler_blob() != nullptr, + "polling page return stub not created yet"); + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + + // Determine saved exception pc using pc relative address computation. + { + Label next_pc; + __ z_larl(Z_R1_scratch, next_pc); + __ bind(next_pc); + } + + int current_offset = __ offset(); + __ add2reg(Z_R1_scratch, safepoint_offset() - current_offset); + __ z_stg(Z_R1_scratch, Address(Z_thread, JavaThread::saved_exception_pc_offset())); + + __ load_const_optimized(Z_R1_scratch, (intptr_t)stub); + __ z_br(Z_R1_scratch); } void RangeCheckStub::emit_code(LIR_Assembler* ce) { diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index dd2342918f2..d7799769ff8 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1246,8 +1246,9 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) { // We need to mark the code position where the load from the safepoint // polling page was emitted as relocInfo::poll_return_type here. + code_stub->set_safepoint_offset(__ offset()); __ relocate(relocInfo::poll_return_type); - __ load_from_polling_page(Z_R1_scratch); + __ safepoint_poll(*code_stub->entry(), Z_R0_scratch, true /* at_return */, true /* in_nmethod */); __ z_br(Z_R14); // Return to caller. } diff --git a/src/hotspot/cpu/s390/c2_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c2_CodeStubs_s390.cpp new file mode 100644 index 00000000000..5e97aadeccc --- /dev/null +++ b/src/hotspot/cpu/s390/c2_CodeStubs_s390.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "opto/c2_MacroAssembler.hpp" +#include "opto/c2_CodeStubs.hpp" +#include "runtime/sharedRuntime.hpp" + +#define __ masm. + +int C2SafepointPollStub::max_size() const { + return 32; +} + +void C2SafepointPollStub::emit(C2_MacroAssembler& masm) { + __ bind(entry()); + assert(SharedRuntime::polling_page_return_handler_blob() != nullptr, + "polling page return stub not created yet"); + address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point(); + + // Determine saved exception pc using pc relative address computation. + { + Label next_pc; + __ z_larl(Z_R1_scratch, next_pc); + __ bind(next_pc); + } + + int current_offset = __ offset(); + // Code size must not depend on offsets. + __ z_agfi(Z_R1_scratch, _safepoint_offset - current_offset); + __ z_stg(Z_R1_scratch, Address(Z_thread, JavaThread::saved_exception_pc_offset())); + + __ load_const_optimized(Z_R1_scratch, (intptr_t)stub); + __ z_br(Z_R1_scratch); +} + +#undef __ diff --git a/src/hotspot/cpu/s390/downcallLinker_s390.cpp b/src/hotspot/cpu/s390/downcallLinker_s390.cpp index 49d7ab0d487..644461e7064 100644 --- a/src/hotspot/cpu/s390/downcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/downcallLinker_s390.cpp @@ -254,7 +254,7 @@ void DowncallLinker::StubGenerator::generate() { __ z_fence(); // Order state change wrt. safepoint poll. } - __ safepoint_poll(L_safepoint_poll_slow_path, tmp); + __ safepoint_poll(L_safepoint_poll_slow_path, tmp, true /* at_return */, false /* in_nmethod */); __ load_and_test_int(tmp, Address(Z_thread, JavaThread::suspend_flags_offset())); __ z_brne(L_safepoint_poll_slow_path); diff --git a/src/hotspot/cpu/s390/gc/z/zAddress_s390.cpp b/src/hotspot/cpu/s390/gc/z/zAddress_s390.cpp new file mode 100644 index 00000000000..7b1b30a8445 --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zAddress_s390.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" +#include "gc/z/zAddress.inline.hpp" +#include "gc/z/zGlobals.hpp" +#include "runtime/globals.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/powerOfTwo.hpp" + +#ifdef LINUX +#include +#endif // LINUX + +// Default value if probing is not implemented for a certain platform +// Max address bit is restricted by implicit assumptions in the code, for instance +// the bit layout of ZForwardingEntry or Partial array entry (see ZMarkStackEntry) in mark stack +static const size_t DEFAULT_MAX_ADDRESS_BIT = 46; +// Minimum value returned, if probing fail +static const size_t MINIMUM_MAX_ADDRESS_BIT = 36; + +static size_t probe_valid_max_address_bit() { +#ifdef LINUX + size_t max_address_bit = 0; + const size_t page_size = os::vm_page_size(); + for (size_t i = DEFAULT_MAX_ADDRESS_BIT; i > MINIMUM_MAX_ADDRESS_BIT; --i) { + const uintptr_t base_addr = ((uintptr_t) 1U) << i; + if (msync((void*)base_addr, page_size, MS_ASYNC) == 0) { + // msync suceeded, the address is valid, and maybe even already mapped. + max_address_bit = i; + break; + } + if (errno != ENOMEM) { + // Some error occured. This should never happen, but msync + // has some undefined behavior, hence ignore this bit. +#ifdef ASSERT + fatal("Received '%s' while probing the address space for the highest valid bit", os::errno_name(errno)); +#else // ASSERT + log_warning_p(gc)("Received '%s' while probing the address space for the highest valid bit", os::errno_name(errno)); +#endif // ASSERT + continue; + } + // Since msync failed with ENOMEM, the page might not be mapped. + // Try to map it, to see if the address is valid. + void* const result_addr = mmap((void*) base_addr, page_size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); + if (result_addr != MAP_FAILED) { + munmap(result_addr, page_size); + } + if ((uintptr_t) result_addr == base_addr) { + // address is valid + max_address_bit = i; + break; + } + } + if (max_address_bit == 0) { + // probing failed, allocate a very high page and take that bit as the maximum + const uintptr_t high_addr = ((uintptr_t) 1U) << DEFAULT_MAX_ADDRESS_BIT; + void* const result_addr = mmap((void*) high_addr, page_size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); + if (result_addr != MAP_FAILED) { + max_address_bit = BitsPerSize_t - count_leading_zeros((size_t) result_addr) - 1; + munmap(result_addr, page_size); + } + } + log_info_p(gc, init)("Probing address space for the highest valid bit: %zu", max_address_bit); + return MAX2(max_address_bit, MINIMUM_MAX_ADDRESS_BIT); +#else // LINUX + return DEFAULT_MAX_ADDRESS_BIT; +#endif // LINUX +} + +size_t ZPlatformAddressOffsetBits() { + static const size_t valid_max_address_offset_bits = probe_valid_max_address_bit() + 1; + const size_t max_address_offset_bits = valid_max_address_offset_bits - 3; + const size_t min_address_offset_bits = max_address_offset_bits - 2; + const size_t address_offset = ZGlobalsPointers::min_address_offset_request(); + const size_t address_offset_bits = log2i_exact(address_offset); + return clamp(address_offset_bits, min_address_offset_bits, max_address_offset_bits); +} + +size_t ZPlatformAddressHeapBaseShift() { + return ZPlatformAddressOffsetBits(); +} + +void ZGlobalsPointers::pd_set_good_masks() { +} diff --git a/src/hotspot/cpu/s390/gc/z/zAddress_s390.hpp b/src/hotspot/cpu/s390/gc/z/zAddress_s390.hpp new file mode 100644 index 00000000000..d5e2ac0da9e --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zAddress_s390.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 CPU_S390_GC_Z_ZADDRESS_S390_HPP +#define CPU_S390_GC_Z_ZADDRESS_S390_HPP + +#include "utilities/globalDefinitions.hpp" + +const size_t ZPointerLoadShift = 16; + +size_t ZPlatformAddressOffsetBits(); +size_t ZPlatformAddressHeapBaseShift(); + +#endif // CPU_S390_GC_Z_ZADDRESS_S390_HPP + diff --git a/src/hotspot/cpu/s390/gc/z/zAddress_s390.inline.hpp b/src/hotspot/cpu/s390/gc/z/zAddress_s390.inline.hpp new file mode 100644 index 00000000000..aef9ad9aaad --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zAddress_s390.inline.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 CPU_S390_GC_Z_ZADDRESS_S390_INLINE_HPP +#define CPU_S390_GC_Z_ZADDRESS_S390_INLINE_HPP + +#include "utilities/globalDefinitions.hpp" + +inline uintptr_t ZPointer::remap_bits(uintptr_t colored) { + return colored & ZPointerRemappedMask; +} + +inline constexpr int ZPointer::load_shift_lookup(uintptr_t value) { + return ZPointerLoadShift; +} + +#endif // CPU_S390_GC_Z_ZADDRESS_S390_INLINE_HPP + diff --git a/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.cpp new file mode 100644 index 00000000000..15e6f7fa4ec --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "asm/macroAssembler.inline.hpp" +#include "gc/z/zAddress.hpp" +#include "gc/z/zBarrier.inline.hpp" +#include "gc/z/zBarrierSet.hpp" +#include "gc/z/zBarrierSetAssembler.hpp" +#include "gc/z/zBarrierSetRuntime.hpp" +#include "gc/z/zThreadLocalData.hpp" +#include "register_s390.hpp" +#include "runtime/jniHandles.hpp" +#include "utilities/globalDefinitions.hpp" +#ifdef COMPILER1 +#include "c1/c1_LIRAssembler.hpp" +#include "c1/c1_MacroAssembler.hpp" +#include "gc/z/c1/zBarrierSetC1.hpp" +#endif // COMPILER1 +#ifdef COMPILER2 +#include "gc/z/c2/zBarrierSetC2.hpp" +#include "opto/output.hpp" +#endif // COMPILER2 + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#undef __ +#define __ masm-> + +class ZRuntimeCallSpill { +private: + MacroAssembler* _masm; + Register _result; + int _nbytes_save; + + void save() { + MacroAssembler* masm = _masm; + + bool preserve_R2 = _result != Z_R2; + _nbytes_save = (13 - (preserve_R2 ? 0 : 1)) * BytesPerWord; + int offset = frame::z_abi_160_size; + + __ push_frame_abi160(_nbytes_save); + __ save_return_pc(); + __ z_stg(Z_R1, offset, Z_SP); offset += 8; + if(preserve_R2) { + __ z_stg(Z_R2, offset, Z_SP); offset += 8; + } + __ z_stmg(Z_R3, Z_R5, offset, Z_SP); offset += 3 * 8; + __ z_std(Z_F0, offset, Z_SP); offset += 8; + __ z_std(Z_F1, offset, Z_SP); offset += 8; + __ z_std(Z_F2, offset, Z_SP); offset += 8; + __ z_std(Z_F3, offset, Z_SP); offset += 8; + __ z_std(Z_F4, offset, Z_SP); offset += 8; + __ z_std(Z_F5, offset, Z_SP); offset += 8; + __ z_std(Z_F6, offset, Z_SP); offset += 8; + __ z_std(Z_F7, offset, Z_SP); + } + + void restore() { + MacroAssembler* masm = _masm; + Register result = Z_RET; + bool restore_R2 = _result != Z_R2; + + if (restore_R2 && _result != noreg) { + __ z_lgr(Z_R0, result); + result = Z_R0; + } + int offset = frame::z_abi_160_size; + + __ restore_return_pc(); + __ z_lg(Z_R1, offset, Z_SP); offset += 8; + if(restore_R2) { + __ z_lg(Z_R2, offset, Z_SP); offset += 8; + } + __ z_lmg(Z_R3, Z_R5, offset, Z_SP); offset += 3 * 8; + __ z_ld(Z_F0, offset, Z_SP); offset += 8; + __ z_ld(Z_F1, offset, Z_SP); offset += 8; + __ z_ld(Z_F2, offset, Z_SP); offset += 8; + __ z_ld(Z_F3, offset, Z_SP); offset += 8; + __ z_ld(Z_F4, offset, Z_SP); offset += 8; + __ z_ld(Z_F5, offset, Z_SP); offset += 8; + __ z_ld(Z_F6, offset, Z_SP); offset += 8; + __ z_ld(Z_F7, offset, Z_SP); + __ pop_frame(); + + if (_result != noreg) { + __ lgr_if_needed(_result, result); + } + } + +public: + ZRuntimeCallSpill(MacroAssembler* masm, Register result) + : _masm(masm), + _result(result) { + save(); + } + + ~ZRuntimeCallSpill() { + restore(); + } +}; + +void ZBarrierSetAssembler::load_at(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + const Address& src, + Register dst, + Register temp1, + Register temp2, + Label *L_handle_null) { + if (!ZBarrierSet::barrier_needed(decorators, type)) { + // Barrier not needed + BarrierSetAssembler::load_at(masm, decorators, type, src, dst, temp1, temp2, L_handle_null); + return; + } + + BLOCK_COMMENT("ZBarrierSetAssembler::load_at {"); + + assert_different_registers(temp1, dst); + + NearLabel done, uncolor; + + // + // Fast Path + // + + // Load address + __ z_lay(temp1, src); + + // Load oop at address + __ z_lg(dst, 0, temp1); + __ z_lgr(Z_R0_scratch, dst); + + const bool on_non_strong = + (decorators & ON_WEAK_OOP_REF) != 0 || + (decorators & ON_PHANTOM_OOP_REF) != 0; + + // Test Address bad mask + if (on_non_strong) { + __ z_ng(Z_R0_scratch, mark_bad_mask_from_thread(Z_thread)); + } else { + __ z_ng(Z_R0_scratch, load_bad_mask_from_thread(Z_thread)); + } + + __ branch_optimized(Assembler::bcondZero, uncolor); + + // + // Slow Path + // + + { + // Call VM + ZRuntimeCallSpill rcs(masm, dst); + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators), dst, temp1); + } + + // Slow-path has already uncolored + if (L_handle_null != nullptr) { + __ z_ltgr(dst, dst); + __ branch_optimized(Assembler::bcondEqual, *L_handle_null); + } + __ branch_optimized(Assembler::bcondAlways, done); + + __ bind(uncolor); + + // Remove the color bits + __ z_srlg(dst, dst, ZPointerLoadShift); + if (L_handle_null != nullptr) { + __ z_ltgr(dst, dst); + __ branch_optimized(Assembler::bcondEqual, *L_handle_null); + } + + __ bind(done); + + BLOCK_COMMENT("} ZBarrierSetAssembler::load_at"); +} + +void ZBarrierSetAssembler::store_barrier_fast(MacroAssembler* masm, + Address ref_addr, + Register rnew_zaddress, + Register rnew_zpointer, + bool in_nmethod, + bool is_atomic, + Label& medium_path, + Label& medium_path_continuation) const { + assert_different_registers(ref_addr.base(), rnew_zpointer); + assert_different_registers(ref_addr.index(), rnew_zpointer); + assert_different_registers(rnew_zaddress, rnew_zpointer); + + if (in_nmethod) { + if (is_atomic) { + // Atomic operations must ensure that the contents of memory are store-good before + // an atomic operation can execute. + // A not relocatable object could have spurious raw null pointers in its fields after + // getting promoted to the old generation. + __ z_llgh(rnew_zpointer, Address(ref_addr.base(), ref_addr.index(), ref_addr.disp() + 0x6)); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_llill(Z_R0_scratch, barrier_Relocation::unpatched); + __ z_clgr(rnew_zpointer, Z_R0_scratch); + __ branch_optimized(Assembler::bcondNotEqual, medium_path); + } else { + // Stores on relocatable objects never need to deal with raw null pointers in fields. + // Raw null pointers may only exist in the young generation, as they get pruned when + // the object is relocated to old. And no pre-write barrier needs to perform any action + // in the young generation. + __ z_llgh(rnew_zpointer, Address(ref_addr.base(), ref_addr.index(), ref_addr.disp() + 0x6)); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreBadBeforeLoad); + __ z_nill(rnew_zpointer, barrier_Relocation::unpatched); + __ branch_optimized(Assembler::bcondNotZero, medium_path); + } + __ bind(medium_path_continuation); + assert_different_registers(rnew_zaddress, rnew_zpointer); + if (rnew_zaddress != noreg) { + // noreg means null, no need to color + __ z_sllg(rnew_zpointer, rnew_zaddress, ZPointerLoadShift); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_oill(rnew_zpointer, barrier_Relocation::unpatched); + } else { + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_llill(rnew_zpointer, barrier_Relocation::unpatched); + } + } else { + assert(!is_atomic, "atomics outside of nmethods not supported"); + __ z_llgh(rnew_zpointer, Address(ref_addr.base(), ref_addr.index(), ref_addr.disp() + 0x6)); + __ z_ng(rnew_zpointer, Address(Z_thread, ZThreadLocalData::store_bad_mask_offset())); + __ branch_optimized(Assembler::bcondNotEqual, medium_path); + __ bind(medium_path_continuation); + if (rnew_zaddress == noreg) { + __ clear_reg(rnew_zpointer, true, false); + } else { + __ z_sllg(rnew_zpointer, rnew_zaddress, ZPointerLoadShift); + } + __ z_og(rnew_zpointer, Address(Z_thread, ZThreadLocalData::store_good_mask_offset())); + } +} + +static void store_barrier_buffer_add(MacroAssembler* masm, + Address ref_addr, + Register temp1, + Register temp2, + Label& slow_path) { + + Address buffer(Z_thread, ZThreadLocalData::store_barrier_buffer_offset()); + assert_different_registers(ref_addr.base(), ref_addr.index(), temp1, temp2); + + __ z_lg(temp1, buffer); + + // Combined pointer bump and check if the buffer is disabled or full + __ z_ltg(temp2, Address(temp1, ZStoreBarrierBuffer::current_offset())); + __ branch_optimized(Assembler::bcondEqual, slow_path); + + // Bump the pointer + __ z_agfi(temp2, -(int)sizeof(ZStoreBarrierEntry)); + __ z_stg(temp2, Address(temp1, ZStoreBarrierBuffer::current_offset())); + + // Compute the buffer entry address + __ load_address(temp2, Address(temp2, ZStoreBarrierBuffer::buffer_offset())); + __ z_agr(temp2, temp1); + + // Compute and log the store address + __ load_address(temp1, ref_addr); + __ z_stg(temp1, Address(temp2, in_bytes(ZStoreBarrierEntry::p_offset()))); + + // Load and log the prev value + __ z_lg(temp1, Address(temp1, 0)); + __ z_stg(temp1, Address(temp2, in_bytes(ZStoreBarrierEntry::prev_offset()))); +} + +void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm, + Address ref_addr, + Register temp1, + Register temp2, + bool is_native, + bool is_atomic, + Label& medium_path_continuation, + Label& slow_path, + Label& slow_path_continuation) const { + assert_different_registers(ref_addr.base(), temp1, temp2); + + // The reason to end up in the medium path is that the pre-value was not 'good'. + if (is_native) { + __ branch_optimized(Assembler::bcondAlways, slow_path); + __ bind(slow_path_continuation); + __ branch_optimized(Assembler::bcondAlways, medium_path_continuation); + } else if (is_atomic) { + // Atomic accesses can get to the medium fast path because the value was a + // raw null value. If it was not null, then there is no doubt we need to take a slow path. + __ z_ltg(temp2, ref_addr); + __ branch_optimized(Assembler::bcondNotZero, slow_path); + + // If we get this far, we know there is a young raw null value in the field. + // Try to self-heal null values for atomic accesses + + __ z_lg(temp1, Address(Z_thread , ZThreadLocalData::store_good_mask_offset())); + + __ z_csg(temp2, temp1, ref_addr); + + __ branch_optimized(Assembler::bcondNotEqual, slow_path); + + __ bind(slow_path_continuation); + __ branch_optimized(Assembler::bcondAlways, medium_path_continuation); + } else { + // A non-atomic relocatable object won't get to the medium fast path due to a + // raw null in the young generation. We only get here because the field is bad. + // In this path we don't need any self healing, so we can avoid a runtime call + // most of the time by buffering the store barrier to be applied lazily. + store_barrier_buffer_add(masm, + ref_addr, + temp1, + temp2, + slow_path); + __ bind(slow_path_continuation); + __ branch_optimized(Assembler::bcondAlways, medium_path_continuation); + } +} + +void ZBarrierSetAssembler::store_at(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + const Address& dst, + Register src, + Register temp1, + Register temp2, + Register temp3) { + BLOCK_COMMENT("ZBarrierSetAssembler::store_at {"); + + bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + + if (is_reference_type(type)) { + assert_different_registers(src, temp1, dst.base(), dst.index()); + + if (dest_uninitialized) { + if (src == noreg) { + __ clear_reg(temp1, true, false); + } else { + __ z_sllg(temp1, src, ZPointerLoadShift); + } + __ z_og(temp1, Address(Z_thread, ZThreadLocalData::store_good_mask_offset())); + } else { + NearLabel done; + Label medium; + Label medium_continuation; + Label slow; + Label slow_continuation; + store_barrier_fast(masm, dst /* ref_addr */, src /* rnew_zaddress */, temp1 /* rnew_zpointer */ + , false, false, medium, medium_continuation); + __ branch_optimized(Assembler::bcondAlways, done); + __ bind(medium); + store_barrier_medium(masm, + dst, /* ref_addr */ + temp1, + temp2, + false /* is_native */, + false /* is_atomic */, + medium_continuation, + slow, + slow_continuation); + + __ bind(slow); + { + // Call VM + ZRuntimeCallSpill rcs(masm, noreg); + __ z_lay(Z_ARG1, dst); + __ MacroAssembler::call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), Z_ARG1); + } + + __ branch_optimized(Assembler::bcondAlways, slow_continuation); + __ bind(done); + } + + // Store value + BarrierSetAssembler::store_at(masm, decorators, type, dst, temp1, noreg, noreg, noreg); + } else { + BarrierSetAssembler::store_at(masm, decorators, type, dst, src, noreg, noreg, noreg); + } + + BLOCK_COMMENT("} ZBarrierSetAssembler::store_at"); +} + +const Register _load_bad_mask = Z_R5, _store_bad_mask = Z_R6, _store_good_mask = Z_R7; + +void ZBarrierSetAssembler::copy_load_at_fast(MacroAssembler* masm, + Register zpointer, + Register addr, + Register load_bad_mask, + Label& slow_path, + Label& continuation) const { + __ z_lg(zpointer, Address(addr, 0)); + __ z_ngrk(Z_R0_scratch, zpointer, load_bad_mask); + __ branch_optimized(Assembler::bcondNotZero, slow_path); + __ bind(continuation); +} + +void ZBarrierSetAssembler::copy_load_at_slow(MacroAssembler* masm, + Register zpointer, + Register addr, + Label& slow_path, + Label& continuation) const { + + __ bind(slow_path); + + { + ZRuntimeCallSpill rcs(masm, Z_R0); + assert(zpointer != Z_ARG2, "or change argument setup"); + __ lgr_if_needed(Z_ARG2, addr); + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(), zpointer, Z_ARG2); + } + __ z_sllg(zpointer, Z_R0, ZPointerLoadShift); // Slow-path has uncolored; revert + __ branch_optimized(Assembler::bcondAlways, continuation); +} + +void ZBarrierSetAssembler::copy_store_at_fast(MacroAssembler* masm, + Register zpointer, + Register ref_addr, + Register store_bad_mask, + Register store_good_mask, + Label& medium_path, + Label& continuation, + bool dest_uninitialized) const { + + if (!dest_uninitialized) { + __ z_lg(Z_R0, Address(ref_addr, 0)); + __ z_ngr(Z_R0, store_bad_mask); + __ branch_optimized(Assembler::bcondNotZero, medium_path); + __ bind(continuation); + } + __ z_nill(zpointer, 0); + __ z_ogr(zpointer, store_good_mask); + __ z_stg(zpointer, Address(ref_addr, 0)); +} + +void ZBarrierSetAssembler::copy_store_at_slow(MacroAssembler* masm, + Register addr, + Label& medium_path, + Label& continuation, + bool dest_uninitialized) const { + if (!dest_uninitialized) { + Label slow_path, slow_path_continuation; + __ bind(medium_path); + store_barrier_medium(masm, Address(addr, 0), Z_tmp_1, Z_tmp_2, false, false, continuation, slow_path, slow_path_continuation); + __ bind(slow_path); + { + ZRuntimeCallSpill rcs(masm, noreg); + __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr(), addr); + } + __ branch_optimized(Assembler::bcondAlways, continuation); + } +} + +// Arguments for generated stub: +// from: Z_ARG1 +// to: Z_ARG2 +// count: Z_ARG3 (int >= 0) +// TODO: Use vector instructions +void ZBarrierSetAssembler::generate_disjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized) { + const Register zpointer = Z_R1; + NearLabel done, loop; + Label load_bad, load_good, store_bad, store_good; + __ z_ltgr(Z_ARG3, Z_ARG3); + __ z_bre(done); + + __ bind(loop); + copy_load_at_fast(masm, zpointer, Z_ARG1, _load_bad_mask, load_bad, load_good); + copy_store_at_fast(masm, zpointer, Z_ARG2, _store_bad_mask, _store_good_mask, store_bad, store_good, dest_uninitialized); + __ add2reg(Z_ARG1, 8); + __ add2reg(Z_ARG2, 8); + __ z_brct(Z_ARG3, loop); + + __ bind(done); + + __ restore_return_pc(); + __ z_lmg(Z_R5, Z_R11, 16, Z_SP); + __ pop_frame(); + + __ clear_reg(Z_RET, true, false); + __ z_br(Z_R14); + + copy_load_at_slow(masm, zpointer, Z_ARG1, load_bad, load_good); + copy_store_at_slow(masm, Z_ARG2, store_bad, store_good, dest_uninitialized); +} + +void ZBarrierSetAssembler::generate_conjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized) { + const Register zpointer = Z_R1; + NearLabel done, loop; + Label load_bad, load_good, store_bad, store_good; + __ z_slag(Z_R0, Z_ARG3, 3); + __ branch_optimized(Assembler::bcondZero, done); + // Point behind last elements and copy backwards. + __ z_agr(Z_ARG1, Z_R0); + __ z_agr(Z_ARG2, Z_R0); + + __ bind(loop); + __ add2reg(Z_ARG1, -8); + __ add2reg(Z_ARG2, -8); + copy_load_at_fast(masm, zpointer, Z_ARG1, _load_bad_mask, load_bad, load_good); + copy_store_at_fast(masm, zpointer, Z_ARG2, _store_bad_mask, _store_good_mask, store_bad, store_good, dest_uninitialized); + __ z_brct(Z_ARG3, loop); + + __ bind(done); + + __ restore_return_pc(); + __ z_lmg(Z_R5, Z_R11, 16, Z_SP); + __ pop_frame(); + + __ clear_reg(Z_RET, true, false); + __ z_br(Z_R14); + + copy_load_at_slow(masm, zpointer, Z_ARG1, load_bad, load_good); + copy_store_at_slow(masm, Z_ARG2, store_bad, store_good, dest_uninitialized); +} + +void ZBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + Register src, + Register dst, + Register count) { + bool is_checkcast_copy = (decorators & ARRAYCOPY_CHECKCAST) != 0, + dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + + if (!ZBarrierSet::barrier_needed(decorators, type) || is_checkcast_copy) { + return; + } + + __ block_comment("arraycopy_prologue (zgc) {"); + + int nbytes_save = 9 * BytesPerWord; // SP, PC, R5, R6, R7, R10, R11 + __ push_frame(nbytes_save); + __ save_return_pc(); + __ z_stmg(Z_R5, Z_R11, 16, Z_SP); + + load_copy_masks(masm, _load_bad_mask, _store_bad_mask, _store_good_mask, dest_uninitialized); + + __ block_comment("} arraycopy_prologue (zgc)"); +} + +void ZBarrierSetAssembler::load_copy_masks(MacroAssembler* masm, + Register load_bad_mask, + Register store_bad_mask, + Register store_good_mask, + bool dest_uninitialized) const { + __ z_lg(load_bad_mask, Address(Z_thread, ZThreadLocalData::load_bad_mask_offset())); + __ z_lg(store_good_mask, Address(Z_thread, ZThreadLocalData::store_good_mask_offset())); + if (dest_uninitialized) { + DEBUG_ONLY( __ load_const_optimized(store_bad_mask, (long)-1) ); + } else { + __ z_lg(store_bad_mask, Address(Z_thread, ZThreadLocalData::store_bad_mask_offset())); + } +} + +void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, + Register jni_env, + Register robj, + Register temp, + Label& slowpath) { + BLOCK_COMMENT("ZBarrierSetAssembler::try_resolve_jobject_in_native {"); + + NearLabel done, tagged, weak_tagged, uncolor; + Address load_bad_mask = load_bad_mask_from_jni_env(jni_env), + mark_bad_mask = mark_bad_mask_from_jni_env(jni_env); + + // Test for Tag + __ z_tmll(robj, JNIHandles::tag_mask); + __ branch_optimized(Assembler::bcondNotAllZero, tagged); + + // Resolve local handle + __ z_lg(robj, Address(robj)); + __ branch_optimized(Assembler::bcondAlways, done); + + __ bind(tagged); + + // Test for weak tag + __ z_tmll(robj, JNIHandles::TypeTag::weak_global); + __ branch_optimized(Assembler::bcondNotAllZero, weak_tagged); + + // Resolve global handle + __ z_lg(robj, Address(robj, -JNIHandles::TypeTag::global)); + __ z_lg(temp, load_bad_mask); + __ z_ngr(temp, robj); + __ branch_optimized(Assembler::bcondNotZero, slowpath); + __ branch_optimized(Assembler::bcondAlways, uncolor); + + __ bind(weak_tagged); + + // Resolve weak handle + __ z_lg(robj, Address(robj, -JNIHandles::TypeTag::weak_global)); + __ z_lg(temp, mark_bad_mask); + __ z_ngr(temp, robj); + __ branch_optimized(Assembler::bcondNotZero, slowpath); + + __ bind(uncolor); + + // Uncolor + __ z_srlg(robj, robj, ZPointerLoadShift); + + __ bind(done); + + BLOCK_COMMENT("} ZBarrierSetAssembler::try_resolve_jobject_in_native"); +} + +void ZBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, + Register weak_handle, + Register obj, + Register temp, + Label& slow_path) { + assert_different_registers(weak_handle, temp, noreg); + assert_different_registers(obj, temp, noreg); + + // Peek weak handle using the standard implementation. + BarrierSetAssembler::try_peek_weak_handle_in_nmethod(masm, weak_handle, obj, temp, slow_path); + + // Check if the oop is bad, in which case we need to take the slow path. + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeTest); + __ z_tmll(obj, barrier_Relocation::unpatched); + __ branch_optimized(Assembler::bcondNotAllZero, slow_path); + + // Oop is okay, so we uncolor it. + __ z_srlg(obj, obj, ZPointerLoadShift); +} + +#undef __ + +#ifdef COMPILER1 +#define __ ce->masm()-> + +static void z_uncolor(LIR_Assembler* ce, LIR_Opr ref) { + __ z_srlg(ref->as_register(), ref->as_register(), ZPointerLoadShift); +} + +static void z_color(LIR_Assembler* ce, LIR_Opr ref) { + __ z_sllg(ref->as_register(), ref->as_register(), ZPointerLoadShift); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_oill(ref->as_register(), barrier_Relocation::unpatched); +} + +void ZBarrierSetAssembler::generate_c1_uncolor(LIR_Assembler* ce, LIR_Opr ref) const { + z_uncolor(ce, ref); +} + +void ZBarrierSetAssembler::generate_c1_color(LIR_Assembler* ce, LIR_Opr ref) const { + z_color(ce, ref); +} + +void ZBarrierSetAssembler::generate_c1_load_barrier(LIR_Assembler* ce, + LIR_Opr ref, + ZLoadBarrierStubC1* stub, + bool on_non_strong) const { + + if (on_non_strong) { + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeTest); + __ z_tmll(ref->as_register(), barrier_Relocation::unpatched); + + __ branch_optimized(Assembler::bcondNotAllZero, *stub->entry()); + z_uncolor(ce, ref); + } else { + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatLoadBadBeforeTest); + __ z_tmll(ref->as_register(), barrier_Relocation::unpatched); + + __ branch_optimized(Assembler::bcondNotAllZero, *stub->entry()); + z_uncolor(ce, ref); + } + + __ bind(*stub->continuation()); +} + +// Code emitted by code stub "ZLoadBarrierStubC1" which in turn is emitted by ZBarrierSetC1::load_barrier. +// Invokes the runtime stub which is defined just below. +void ZBarrierSetAssembler::generate_c1_load_barrier_stub(LIR_Assembler* ce, + ZLoadBarrierStubC1* stub) const { + // Stub entry + __ bind(*stub->entry()); + + Register ref = stub->ref()->as_register(); + Register ref_addr = noreg; + Register temp = noreg; + + if (stub->tmp()->is_valid()) { + //Load address into tmp register + ce->leal(stub->ref_addr(), stub->tmp()); + ref_addr = temp = stub->tmp()->as_pointer_register(); + } else { + // 'tmp' register is not given, so address must have neither an index nor a displacement. + // The address' base register is thus usable as-is. + assert(stub->ref_addr()->as_address_ptr()->disp() == 0, "illegal displacement"); + assert(!stub->ref_addr()->as_address_ptr()->index()->is_valid(), "illegal index"); + + // Address already in register + ref_addr = stub->ref_addr()->as_address_ptr()->base()->as_pointer_register(); + } + + assert_different_registers(ref, ref_addr, noreg); + + // Setup arguments and call runtime stub + int nbytes_save = 4 * BytesPerWord; /* SP, PC, 2 args */ + int offset = 0; + + __ push_frame(nbytes_save); offset += 8; + __ save_return_pc(); offset += 8; + __ z_stg(ref, offset, Z_SP); offset += 8; + __ z_stg(ref_addr, offset, Z_SP); + __ call_stub(stub->runtime_stub()); + + __ restore_return_pc(); + __ pop_frame(); + + // The runtime stub passes the result via the R0 register + __ z_lgr(ref, Z_R0); + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); +} + +void ZBarrierSetAssembler::generate_c1_store_barrier(LIR_Assembler* ce, + LIR_Address* addr, + LIR_Opr new_zaddress, + LIR_Opr new_zpointer, + ZStoreBarrierStubC1* stub) const { + Register rnew_zaddress = new_zaddress->as_register(); + Register rnew_zpointer = new_zpointer->as_register(); + + store_barrier_fast(ce->masm(), + ce->as_Address(addr), + rnew_zaddress, + rnew_zpointer, + true, + stub->is_atomic(), + *stub->entry(), + *stub->continuation()); +} + +void ZBarrierSetAssembler::generate_c1_store_barrier_stub(LIR_Assembler* ce, + ZStoreBarrierStubC1* stub) const { + // Stub entry + __ bind(*stub->entry()); + + Register scratch = stub->new_zpointer()->as_register(); + + Label slow; + Label slow_continuation; + store_barrier_medium(ce->masm(), + ce->as_Address(stub->ref_addr()->as_address_ptr()), + Z_R1, + scratch, + false /* is_native */, + stub->is_atomic(), + *stub->continuation(), + slow, + slow_continuation); + + __ bind(slow); + + // Pass store address in the stack + __ z_lay(scratch, ce->as_Address(stub->ref_addr()->as_address_ptr())); + + int nbytes_save = 3 * BytesPerWord; // SP, PC, R0 + __ push_frame(nbytes_save); + __ save_return_pc(); + __ z_stg(scratch, 16, Z_SP); + + __ call_stub(stub->runtime_stub()); + + __ restore_return_pc(); + __ pop_frame(); + + // Stub exit + __ branch_optimized(Assembler::bcondAlways, slow_continuation); +} + +#undef __ +#define __ sasm-> + +void ZBarrierSetAssembler::generate_c1_load_barrier_runtime_stub(StubAssembler *sasm, + DecoratorSet decorators) const { + + int nbytes_save = 13 * BytesPerWord; // R1 to R5, F0 to F7 + + __ push_frame_abi160(nbytes_save); + __ save_return_pc(); + __ save_volatile_regs(Z_SP, frame::z_abi_160_size, true, false); + + int offset = 16 + nbytes_save + frame::z_abi_160_size; + + // Load the arguments back again from the stack + __ z_lg(Z_ARG1, offset, Z_SP); offset += 8; // ref + __ z_lg(Z_ARG2, offset, Z_SP); // ref_addr + + __ call_VM_leaf(ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators)); + __ z_lgr(Z_R0, Z_RET); + + __ restore_return_pc(); + __ restore_volatile_regs(Z_SP, frame::z_abi_160_size, true, false); + __ pop_frame(); + + __ z_br(Z_R14); +} + +void ZBarrierSetAssembler::generate_c1_store_barrier_runtime_stub(StubAssembler* sasm, + bool self_healing) const { + + int nbytes_save = 13 * BytesPerWord; // R1 to R5, F0 to F7 + + __ push_frame_abi160(nbytes_save); + __ save_return_pc(); + __ save_volatile_regs(Z_SP, frame::z_abi_160_size, true, false); + + __ z_lg(Z_ARG1, frame::z_abi_160_size + nbytes_save + 16, Z_SP); + + if (self_healing) { + __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr()); + } else { + __ call_VM_leaf(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr()); + } + + __ restore_return_pc(); + __ restore_volatile_regs(Z_SP, frame::z_abi_160_size, true, false); + __ pop_frame(); + + __ z_br(Z_R14); +} + +#endif // Compiler1 + +#ifdef COMPILER2 + +#undef __ +#define __ _masm-> + +class ZSetupArguments { + MacroAssembler* const _masm; + const Register _ref; + const Address _ref_addr; + +public: + ZSetupArguments(MacroAssembler* masm, ZLoadBarrierStubC2* stub) + : _masm(masm), + _ref(stub->ref()), + _ref_addr(stub->ref_addr()) { + + // Desired Register/argument configuration + // _ref: Z_ARG1 + // _ref_addr: Z_ARG2 + + // '_ref_addr' can be unspecified. In that case, the barrier will not heal the reference. + if (_ref_addr.base() == noreg) { + assert_different_registers(_ref, noreg); + + __ lgr_if_needed(Z_ARG1, _ref); + __ z_lghi(Z_ARG2, 0); + } else { + // Self Healing + assert_different_registers(_ref, _ref_addr.base(), noreg); + + if (_ref == Z_ARG1) { + // _ref is already at correct place + __ z_lay(Z_ARG2, _ref_addr); + } else if (_ref != Z_ARG2) { + // _ref is in wrong place, but not in Z_ARG2, so fix it first + __ z_lay(Z_ARG2, _ref_addr); + __ z_lgr(Z_ARG1, _ref); + } else if (_ref_addr.base() != Z_ARG1 && _ref_addr.index() != Z_ARG1) { + assert(_ref == Z_ARG2, "Mov ref first, vacating Z_ARG1"); + __ z_lgr(Z_ARG1, _ref); + __ z_lay(Z_ARG2, _ref_addr); + } else { + assert(_ref == Z_ARG2, "Need to vacate Z_ARG2 and _ref_addr is using Z_ARG1"); + if (_ref_addr.base() == Z_ARG1 || _ref_addr.index() == Z_ARG1) { + __ z_lgr(Z_R0_scratch, Z_ARG2); + __ z_lay(Z_ARG2, _ref_addr); + __ z_lgr(Z_ARG1, Z_R0_scratch); + } else { + ShouldNotReachHere(); + } + } + } + } + + ~ZSetupArguments() { + // Transfer result + __ lgr_if_needed(_ref, Z_R2); + } +}; + +#undef __ +#define __ masm-> + +void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skipped_counter(masm); + BLOCK_COMMENT("ZLoadBarrierStubC2"); + + // Stub entry + __ bind(*stub->entry()); + + { + SaveLiveRegisters save_live_registers(masm, stub); + ZSetupArguments setup_arguments(masm, stub); + __ call_stub(stub->slow_path()); + } + + __ branch_optimized(Assembler::bcondAlways, *stub->continuation()); +} + +void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const { + Assembler::InlineSkippedInstructionsCounter skipped_counter(masm); + BLOCK_COMMENT("ZStoreBarrierStubC2"); + + // Stub entry + __ bind(*stub->entry()); + + Label slow; + Label slow_continuation; + store_barrier_medium(masm, + stub->ref_addr(), + stub->new_zpointer(), + Z_R1_scratch, + stub->is_native(), + stub->is_atomic(), + *stub->continuation(), + slow, + slow_continuation); + + __ bind(slow); + { + SaveLiveRegisters save_live_registers(masm, stub); + __ z_lay(Z_ARG1, stub->ref_addr()); + + if (stub->is_native()) { + __ call_stub(ZBarrierSetRuntime::store_barrier_on_native_oop_field_without_healing_addr()); + } else if (stub->is_atomic()) { + __ call_stub(ZBarrierSetRuntime::store_barrier_on_oop_field_with_healing_addr()); + } else if (stub->is_nokeepalive()) { + __ call_stub(ZBarrierSetRuntime::no_keepalive_store_barrier_on_oop_field_without_healing_addr()); + } else { + __ call_stub(ZBarrierSetRuntime::store_barrier_on_oop_field_without_healing_addr()); + } + } + + // Stub exit + __ branch_optimized(Assembler::bcondAlways, slow_continuation); +} + +#undef __ +#endif // COMPILER2 + +#undef __ +#define __ masm-> + +// Verify a colored pointer +void ZBarrierSetAssembler::check_oop(MacroAssembler *masm, Register obj, const char* msg) { + if (!VerifyOops) { + return; + } + + assert_different_registers(obj, Z_R1); + + int nbytes_save = 3 * BytesPerWord; + int offset = 0; + + __ push_frame(nbytes_save); offset += 8; + __ save_return_pc(); offset += 8; + + __ z_stg(Z_R1, offset, Z_SP); + + NearLabel done, skip_uncolor; + // Skip (colored) null + __ z_srlg(Z_R1, obj, ZPointerLoadShift); + __ z_ltgr(Z_R1, Z_R1); + __ branch_optimized(Assembler::bcondZero, done); + + // Check if ZAddressHeapBase << ZPointerLoadShift is set. If so, we need to uncolor. + __ z_slag(Z_R1, obj, 64 - ZAddressHeapBaseShift - ZPointerLoadShift); + __ z_lgr(Z_R1, obj); + __ branch_optimized(Assembler::bcondNotOverflow, skip_uncolor); + + __ z_srlg(Z_R1, obj, ZPointerLoadShift); + __ bind(skip_uncolor); + + __ verify_oop(Z_R1, msg); + __ bind(done); + + offset = 8; + __ restore_return_pc(); offset += 8; + __ z_lg(Z_R1, offset, Z_SP); + __ pop_frame(); +} + +#undef __ + +static uint16_t patch_barrier_relocation_value(int format) { + switch (format) { + case ZBarrierRelocationFormatStoreGoodBeforeLoad: + return (uint16_t)ZPointerStoreGoodMask; + case ZBarrierRelocationFormatStoreBadBeforeLoad: + return (uint16_t)ZPointerStoreBadMask; + case ZBarrierRelocationFormatMarkBadBeforeTest: + return (uint16_t)ZPointerMarkBadMask; + case ZBarrierRelocationFormatLoadBadBeforeTest: + return (uint16_t)ZPointerLoadBadMask; + default: + ShouldNotReachHere(); + return 0; + } +} + +void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { + *(uint16_t*)(addr + 2) = patch_barrier_relocation_value(format); + ICache::invalidate_word(addr); +} diff --git a/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.hpp new file mode 100644 index 00000000000..9af6e96db11 --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zBarrierSetAssembler_s390.hpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 CPU_S390_GC_Z_ZBARRIERSETASSEMBLER_S390_HPP +#define CPU_S390_GC_Z_ZBARRIERSETASSEMBLER_S390_HPP + +#include "code/vmreg.hpp" +#include "oops/accessDecorators.hpp" +#ifdef COMPILER2 +#include "opto/optoreg.hpp" +#endif // COMPILER2 + +#ifdef COMPILER1 +class LIR_Address; +class LIR_Assembler; +class LIR_Opr; +class StubAssembler; +class ZLoadBarrierStubC1; +class ZStoreBarrierStubC1; +#endif // COMPILER1 + +#ifdef COMPILER2 +class MachNode; +class Node; +class ZLoadBarrierStubC2; +class ZStoreBarrierStubC2; +#endif // COMPILER2 + +const int ZBarrierRelocationFormatStoreGoodBeforeLoad = 0; +const int ZBarrierRelocationFormatStoreBadBeforeLoad = 1; +const int ZBarrierRelocationFormatLoadBadBeforeTest = 2; +const int ZBarrierRelocationFormatMarkBadBeforeTest = 3; + +class ZBarrierSetAssembler : public ZBarrierSetAssemblerBase { +public: + virtual void load_at(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + const Address& src, + Register dst, + Register temp1, + Register temp2, + Label *L_handle_null = nullptr); + + void store_barrier_fast(MacroAssembler* masm, + Address ref_addr, + Register rnew_zaddress, + Register rnew_zpointer, + bool in_nmethod, + bool is_atomic, + Label& medium_path, + Label& medium_path_continuation) const; + + void store_barrier_medium(MacroAssembler* masm, + Address ref_addr, + Register temp1, + Register temp2, + bool is_native, + bool is_atomic, + Label& medium_path_continuation, + Label& slow_path, + Label& slow_path_continuation) const; + + virtual void store_at(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + const Address& dst, + Register src, + Register temp1, + Register temp2, + Register temp3); + + void copy_load_at_fast(MacroAssembler* masm, + Register zpointer, + Register addr, + Register load_bad_mask, + Label& slow_path, + Label& continuation) const; + + void copy_load_at_slow(MacroAssembler* masm, + Register zpointer, + Register addr, + Label& slow_path, + Label& continuation) const; + + void copy_store_at_fast(MacroAssembler* masm, + Register zpointer, + Register addr, + Register store_bad_mask, + Register store_good_mask, + Label& medium_path, + Label& continuation, + bool dest_uninitialized) const; + + void copy_store_at_slow(MacroAssembler* masm, + Register addr, + Label& medium_path, + Label& continuation, + bool dest_uninitialized) const; + + void generate_disjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized); + + void generate_conjoint_oop_copy(MacroAssembler* masm, bool dest_uninitialized); + + void load_copy_masks(MacroAssembler* masm, + Register load_bad_mask, + Register store_bad_mask, + Register store_good_mask, + bool dest_uninitialized) const; + + virtual void arraycopy_prologue(MacroAssembler* masm, + DecoratorSet decorators, + BasicType type, + Register src, + Register dst, + Register count); + + virtual void try_resolve_jobject_in_native(MacroAssembler* masm, + Register jni_env, + Register robj, + Register temp, + Label& slowpath); + + virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, + Register weak_handle, + Register obj, + Register temp, + Label& slow_path); + +#ifdef COMPILER1 + + void generate_c1_color(LIR_Assembler* ce, LIR_Opr ref) const; + void generate_c1_uncolor(LIR_Assembler* ce, LIR_Opr ref) const; + + void generate_c1_load_barrier(LIR_Assembler* ce, + LIR_Opr ref, + ZLoadBarrierStubC1* stub, + bool on_non_string) const; + + void generate_c1_load_barrier_stub(LIR_Assembler* ce, + ZLoadBarrierStubC1* stub) const; + + void generate_c1_store_barrier(LIR_Assembler* ce, + LIR_Address* addr, + LIR_Opr new_zaddress, + LIR_Opr new_zpointer, + ZStoreBarrierStubC1* stub) const; + + void generate_c1_store_barrier_stub(LIR_Assembler* ce, + ZStoreBarrierStubC1* stub) const; + + void generate_c1_load_barrier_runtime_stub(StubAssembler *sasm, + DecoratorSet decorators) const; + + void generate_c1_store_barrier_runtime_stub(StubAssembler* sasm, + bool self_healing) const; + +#endif // COMPILER1 + +#ifdef COMPILER2 + + void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const; + + void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const; + +#endif // COMPILER2 + + void check_oop(MacroAssembler *masm, Register obj, const char* msg); + + void patch_barrier_relocation(address addr, int format); + + void patch_barriers() {} + +}; +#endif // CPU_S390_GC_Z_ZBARRIERSETASSEMBLER_S390_HPP diff --git a/src/hotspot/cpu/s390/gc/z/zGlobals_s390.hpp b/src/hotspot/cpu/s390/gc/z/zGlobals_s390.hpp new file mode 100644 index 00000000000..1e99f8f4783 --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/zGlobals_s390.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 CPU_S390_GC_Z_ZGLOBALS_S390_HPP +#define CPU_S390_GC_Z_ZGLOBALS_S390_HPP + +#include "globalDefinitions_s390.hpp" + +const size_t ZPlatformCacheLineSize = DEFAULT_CACHE_LINE_SIZE; + +#endif // CPU_S390_GC_Z_ZGLOBALS_S390_HPP + diff --git a/src/hotspot/cpu/s390/gc/z/z_s390.ad b/src/hotspot/cpu/s390/gc/z/z_s390.ad new file mode 100644 index 00000000000..2638b893f58 --- /dev/null +++ b/src/hotspot/cpu/s390/gc/z/z_s390.ad @@ -0,0 +1,216 @@ +// +// Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2026 IBM Corporation. All rights reserved. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +// or visit www.oracle.com if you need additional information or have any +// questions. +// + +source_hpp %{ + +#include "gc/shared/gc_globals.hpp" +#include "gc/z/c2/zBarrierSetC2.hpp" +#include "gc/z/zThreadLocalData.hpp" + +%} + +source %{ + +#include "gc/z/zBarrierSetAssembler.hpp" + +static void z_color(MacroAssembler* masm, Register dst, Register src) { + assert_different_registers(src, dst); + if (src != noreg) { + // noreg means null, no need to color + __ z_sllg(dst, src, ZPointerLoadShift); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_oill(dst, barrier_Relocation::unpatched); + } else { + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeLoad); + __ z_llill(dst, barrier_Relocation::unpatched); + } +} + +static void z_uncolor(MacroAssembler* masm, Register ref) { + __ z_srlg(ref, ref, ZPointerLoadShift); +} + +static void z_keep_alive_load_barrier(MacroAssembler* masm, const MachNode* node, Address ref_addr, Register ref) { + assert(ref != Z_R0_scratch, "ref must not be R0"); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeTest); + __ z_tmll(ref, barrier_Relocation::unpatched); + + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref); + __ branch_optimized(Assembler::bcondNotAllZero, *stub->entry()); + + z_uncolor(masm, ref); + + __ bind(*stub->continuation()); +} + +static void z_load_barrier(MacroAssembler* masm, const MachNode* node, Address ref_addr, Register ref) { + Assembler::InlineSkippedInstructionsCounter skipped_counter(masm); + if (node->barrier_data() == ZBarrierElided) { + z_uncolor(masm, ref); + return; + } + + const bool on_non_strong = + ((node->barrier_data() & ZBarrierWeak) != 0) || + ((node->barrier_data() & ZBarrierPhantom) != 0); + + if (on_non_strong) { + z_keep_alive_load_barrier(masm, node, ref_addr, ref); + return; + } + + ZLoadBarrierStubC2* const stub = ZLoadBarrierStubC2::create(node, ref_addr, ref); + __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatLoadBadBeforeTest); + __ z_tmll(ref, barrier_Relocation::unpatched); + + __ branch_optimized(Assembler::bcondNotAllZero, *stub->entry()); + + z_uncolor(masm, ref); + __ bind(*stub->continuation()); +} + +static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address ref_addr, Register rnew_zaddress, Register rnew_zpointer, bool is_atomic) { + Assembler::InlineSkippedInstructionsCounter skipped_counter(masm); + if (node->barrier_data() == ZBarrierElided) { + z_color(masm, rnew_zpointer, rnew_zaddress); + } else { + bool is_native = (node->barrier_data() & ZBarrierNative) != 0; + bool is_nokeepalive = (node->barrier_data() & ZBarrierNoKeepalive) != 0; + ZStoreBarrierStubC2* const stub = ZStoreBarrierStubC2::create(node, ref_addr, rnew_zaddress, rnew_zpointer, is_native, is_atomic, is_nokeepalive); + ZBarrierSetAssembler* bs_asm = ZBarrierSet::assembler(); + bs_asm->store_barrier_fast(masm, ref_addr, rnew_zaddress, rnew_zpointer, true /* in_nmethod */, is_atomic, *stub->entry(), *stub->continuation()); + } +} + +%} + +// Load Pointer +instruct zLoadP(iRegP dst, memory mem, flagsReg cr) %{ + match(Set dst (LoadP mem)); + predicate(UseZGC && n->as_Load()->barrier_data() != 0); + effect(TEMP dst, KILL cr); + ins_cost(MEMORY_REF_COST); + ins_is_late_expanded_null_check_candidate(true); + + format %{ "LG $dst,$mem\t # ptr" %} + + ins_encode %{ + __ z_lg($dst$$Register, $mem$$Address); + z_load_barrier(masm, this, $mem$$Address, $dst$$Register); + %} + + ins_pipe(pipe_class_dummy); +%} + +// Store Pointer +instruct zStoreP(memory mem, iRegP src, iRegP temp, flagsReg cr) %{ + predicate(UseZGC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem src)); + effect(TEMP temp, KILL cr); + ins_cost(2 * MEMORY_REF_COST); + + format %{ "STG $src,$mem\t # ptr" %} + ins_encode %{ + z_store_barrier(masm, this, $mem$$Address, $src$$Register, $temp$$Register, false /* is_atomic */); + __ z_stg($temp$$Register, $mem$$Address); + %} + ins_pipe(pipe_class_dummy); +%} + +// Store Null Pointer +instruct zStorePNull(memory mem, immP0 zero, iRegP temp, flagsReg cr) %{ + predicate(UseZGC && n->as_Store()->barrier_data() != 0); + match(Set mem (StoreP mem zero)); + effect(TEMP temp, KILL cr); + ins_cost(2 * MEMORY_REF_COST); + + format %{ "STG $mem,null\t # ptr" %} + ins_encode %{ + z_store_barrier(masm, this, $mem$$Address, noreg, $temp$$Register, false /* is_atomic */); + __ z_stg($temp$$Register, $mem$$Address); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct zCompareAndExchangeP(iRegP res, iRegP mem, iRegP oldval, iRegP newval, iRegP newval_temp, flagsReg cr) %{ + match(Set res (CompareAndExchangeP mem (Binary oldval newval))); + predicate(UseZGC && n->as_LoadStore()->barrier_data() != 0); + effect(TEMP_DEF res, USE mem, TEMP newval_temp, KILL cr); + + format %{ "CompareAndExchangeP $oldval, $newval, $mem" %} + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + const Address mem_addr = Address($mem$$Register, 0); + + z_store_barrier(masm, this, mem_addr, $newval$$Register, $newval_temp$$Register, true); + z_color(masm, $res$$Register, $oldval$$Register); + __ z_csg($res$$Register, $newval_temp$$Register, mem_addr); + z_uncolor(masm, $res$$Register); + %} + ins_pipe(pipe_class_dummy); +%} + +instruct zCompareAndSwap(iRegI res, iRegP mem, iRegP oldval, iRegP newval, iRegP oldval_temp, iRegP newval_temp, flagsReg cr) %{ + match(Set res (CompareAndSwapP mem (Binary oldval newval))); + match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); + predicate(UseZGC && n->as_LoadStore()->barrier_data() != 0); + effect(TEMP_DEF res, USE mem, USE oldval, TEMP oldval_temp, TEMP newval_temp, KILL cr); + + format %{ "CompareAndSwapP $oldval,$newval,$mem" %} + + ins_encode %{ + assert_different_registers($oldval$$Register, $mem$$Register); + const Address mem_addr = Address($mem$$Register, 0); + z_store_barrier(masm, this, mem_addr, $newval$$Register, $newval_temp$$Register, true /* is_atomic */); + z_color(masm, $oldval_temp$$Register, $oldval$$Register); + + __ z_csg($oldval_temp$$Register, $newval_temp$$Register, mem_addr); + __ z_lghi($res$$Register, 1L); // true (succeed) + __ load_on_condition_imm_64($res$$Register, 0L, Assembler::bcondNotEqual); // false (failed) + %} + ins_pipe(pipe_class_dummy); +%} + +instruct zGetAndSetP(iRegP newval, iRegP mem, iRegP temp1, iRegP temp2, flagsReg cr) %{ + match(Set newval (GetAndSetP mem newval)); + predicate(UseZGC && n->as_LoadStore()->barrier_data() != 0); + effect(TEMP temp1, TEMP temp2, KILL cr); + + format %{ "GetAndSetP $newval,[$mem]\t # EXCHANGE (oop, atomic), temp $temp1" %} + + ins_encode %{ + NearLabel retry; + const Address mem_addr = Address($mem$$Register, 0); + + z_store_barrier(masm, this, mem_addr, $newval$$Register, $temp1$$Register, true /* is_atomic */); + __ z_lg($temp2$$Register, mem_addr); + __ bind(retry); + __ z_csg($temp2$$Register, $temp1$$Register, mem_addr); + __ branch_optimized(Assembler::bcondNotEqual, retry); + // Uncolor the old value + __ z_srlg($newval$$Register, $temp2$$Register, ZPointerLoadShift); + %} + ins_pipe(pipe_class_dummy); +%} + diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index 30812eff42e..670ec56188e 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -501,7 +501,7 @@ void InterpreterMacroAssembler::load_method_entry(Register cache, Register index } // Load object from cpool->resolved_references(index). -void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index) { +void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index, Register tmp) { assert_different_registers(result, index); get_constant_pool(result); @@ -509,22 +509,21 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result // - from field index to resolved_references() index and // - from word index to byte offset. // Since this is a java object, it is potentially compressed. - Register tmp = index; // reuse z_sllg(index, index, LogBytesPerHeapOop); // Offset into resolved references array. // Load pointer for resolved_references[] objArray. z_lg(result, in_bytes(ConstantPool::cache_offset()), result); z_lg(result, in_bytes(ConstantPoolCache::resolved_references_offset()), result); - resolve_oop_handle(result, Z_R0_scratch, Z_R1_scratch); // Load resolved references array itself. + resolve_oop_handle(result, tmp, Z_R0_scratch); // Load resolved references array itself. #ifdef ASSERT NearLabel index_ok; z_lgf(Z_R0, Address(result, arrayOopDesc::length_offset_in_bytes())); z_sllg(Z_R0, Z_R0, LogBytesPerHeapOop); - compare64_and_branch(tmp, Z_R0, Assembler::bcondLow, index_ok); - stop("resolved reference index out of bounds", 0x09256); + compare64_and_branch(index, Z_R0, Assembler::bcondLow, index_ok); + stop("resolved reference index out of bounds", 0x56); bind(index_ok); #endif z_agr(result, index); // Address of indexed array element. - load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)), tmp, noreg); + load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)), tmp, Z_R0_scratch); } // load cpool->resolved_klass_at(index) @@ -1057,6 +1056,7 @@ void InterpreterMacroAssembler::narrow(Register result, Register ret_type) { // remove activation // +// Apply stack watermark barrier. // Unlock the receiver if this is a synchronized method. // Unlock any Java monitors from synchronized blocks. // Remove the activation from the stack. @@ -1084,6 +1084,22 @@ void InterpreterMacroAssembler::remove_activation(TosState state, unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception); + // The below poll is for the stack watermark barrier. It allows fixing up frames lazily, + // that would normally not be safe to use. Such bad returns into unsafe territory of + // the stack, will call InterpreterRuntime::at_unwind. + NearLabel fast_path; + Label slow_path; + safepoint_poll(slow_path, Z_R0_scratch, true /* at_return */, false /* in_nmethod */); + branch_optimized(Assembler::bcondAlways, fast_path); + bind (slow_path); + push(state); + set_last_Java_frame(Z_SP, noreg); + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::at_unwind), Z_thread); + reset_last_Java_frame(); + pop(state); + align(32); + bind(fast_path); + // Save result (push state before jvmti call and pop it afterwards) and notify jvmti. notify_method_exit(false, state, notify_jvmti ? NotifyJVMTI : SkipNotifyJVMTI); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index bb7013f139b..0ff223b6098 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -121,7 +121,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void load_field_entry (Register cache, Register index, int bcp_offset = 1); void load_method_entry(Register cache, Register index, int bcp_offset = 1); void get_cache_index_at_bcp(Register index, int bcp_offset, size_t index_size = sizeof(u2)); - void load_resolved_reference_at_index(Register result, Register index); + void load_resolved_reference_at_index(Register result, Register index, Register tmp); // load cpool->resolved_klass_at(index) void load_resolved_klass_at_offset(Register cpool, Register offset, Register iklass); diff --git a/src/hotspot/cpu/s390/jniFastGetField_s390.cpp b/src/hotspot/cpu/s390/jniFastGetField_s390.cpp index 3456afdc674..68fae63ded7 100644 --- a/src/hotspot/cpu/s390/jniFastGetField_s390.cpp +++ b/src/hotspot/cpu/s390/jniFastGetField_s390.cpp @@ -38,7 +38,7 @@ #define __ masm-> -#define BUFFER_SIZE 30*sizeof(jint) +#define BUFFER_SIZE 100*sizeof(jint) // Common register usage: // Z_RET/Z_FRET: result diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 097e2966624..d465d83445a 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -2784,11 +2784,25 @@ uint MacroAssembler::get_poll_register(address instr_loc) { return 0; } -void MacroAssembler::safepoint_poll(Label& slow_path, Register temp_reg) { - const Address poll_byte_addr(Z_thread, in_bytes(JavaThread::polling_word_offset()) + 7 /* Big Endian */); - // Armed page has poll_bit set. - z_tm(poll_byte_addr, SafepointMechanism::poll_bit()); - z_brnaz(slow_path); +void MacroAssembler::safepoint_poll(Label& slow_path, Register tmp_reg, bool at_return, bool in_nmethod) { + const Address poll_byte_addr(Z_thread, in_bytes(JavaThread::polling_word_offset())); + + if (at_return) { + if (in_nmethod) { + z_clg(Z_SP, poll_byte_addr); + branch_optimized(Assembler::bcondHigh, slow_path); + } else { + Register fp = tmp_reg; + z_lg(fp, _z_abi(callers_sp), Z_SP); + z_clg(fp, poll_byte_addr); + branch_optimized(Assembler::bcondHigh, slow_path); + } + } else { + assert(!in_nmethod, "should use load_from_polling_page"); + z_lg(tmp_reg, poll_byte_addr); + z_tmll(tmp_reg, SafepointMechanism::poll_bit()); + branch_optimized(Assembler::bcondNotAllZero, slow_path); + } } // Don't rely on register locking, always use Z_R1 as scratch register instead. diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index faafa21b6de..05f9869937f 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -654,7 +654,7 @@ class MacroAssembler: public Assembler { static uint get_poll_register(address instr_loc); // Check if safepoint requested and if so branch - void safepoint_poll(Label& slow_path, Register temp_reg); + void safepoint_poll(Label& slow_path, Register temp_reg, bool at_return, bool in_nmethod); // Stack overflow checking void bang_stack_with_offset(int offset); diff --git a/src/hotspot/cpu/s390/methodHandles_s390.cpp b/src/hotspot/cpu/s390/methodHandles_s390.cpp index aab0e64dddc..f936b2efb2a 100644 --- a/src/hotspot/cpu/s390/methodHandles_s390.cpp +++ b/src/hotspot/cpu/s390/methodHandles_s390.cpp @@ -197,17 +197,17 @@ void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, __ load_heap_oop(method_temp, Address(recv, NONZERO(java_lang_invoke_MethodHandle::form_offset())), - noreg, noreg, IS_NOT_NULL); + temp2, temp3, IS_NOT_NULL); __ verify_oop(method_temp, FILE_AND_LINE); __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset())), - noreg, noreg, IS_NOT_NULL); + temp2, temp3, IS_NOT_NULL); __ verify_oop(method_temp, FILE_AND_LINE); __ load_heap_oop(method_temp, Address(method_temp, NONZERO(java_lang_invoke_MemberName::method_offset())), - noreg, noreg, IS_NOT_NULL); + temp2, temp3, IS_NOT_NULL); __ verify_oop(method_temp, FILE_AND_LINE); __ z_lg(method_temp, Address(method_temp, @@ -433,7 +433,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, Register temp2_defc = temp2; __ load_heap_oop(temp2_defc, member_clazz, - noreg, noreg, IS_NOT_NULL); + temp3, temp4, IS_NOT_NULL); load_klass_from_Class(_masm, temp2_defc, temp3, temp4); __ verify_klass_ptr(temp2_defc); __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, temp4, L_ok); @@ -460,7 +460,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp3); } __ load_heap_oop(Z_method, member_vmtarget, - noreg, noreg, IS_NOT_NULL); + temp1, temp2, IS_NOT_NULL); __ z_lg(Z_method, vmtarget_method); method_is_live = true; break; @@ -470,7 +470,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp3); } __ load_heap_oop(Z_method, member_vmtarget, - noreg, noreg, IS_NOT_NULL); + temp1, temp2, IS_NOT_NULL); __ z_lg(Z_method, vmtarget_method); method_is_live = true; break; @@ -512,7 +512,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, Register temp3_intf = temp3; __ load_heap_oop(temp3_intf, member_clazz, - noreg, noreg, IS_NOT_NULL); + temp2, temp4, IS_NOT_NULL); load_klass_from_Class(_masm, temp3_intf, temp2, temp4); Register Z_index = Z_method; diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index f8e0d35646b..99457c6e068 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -848,6 +848,8 @@ definitions %{ source %{ +#include "opto/c2_CodeStubs.hpp" + #ifdef PRODUCT #define BLOCK_COMMENT(str) #define BIND(label) __ bind(label) @@ -1199,7 +1201,7 @@ void MachEpilogNode::format(PhaseRegAlloc *ra_, outputStream *os) const { os->print_cr("epilog"); os->print("\t"); if (do_polling() && ra_->C->is_method_compilation()) { - os->print_cr("load_from_polling_page Z_R1_scratch"); + os->print_cr("safepoint poll"); os->print("\t"); } } @@ -1221,11 +1223,15 @@ void MachEpilogNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const { // Touch the polling page. if (need_polling) { - __ z_lg(Z_R1_scratch, Address(Z_thread, JavaThread::polling_page_offset())); - // We need to mark the code position where the load from the safepoint - // polling page was emitted as relocInfo::poll_return_type here. - __ relocate(relocInfo::poll_return_type); - __ load_from_polling_page(Z_R1_scratch); + Label dummy_label; + Label* code_stub = &dummy_label; + if (!C->output()->in_scratch_emit_size()) { + C2SafepointPollStub* stub = new (C->comp_arena()) C2SafepointPollStub(__ offset()); + C->output()->add_stub(stub); + code_stub = &stub->entry(); + __ relocate(relocInfo::poll_return_type); + } + __ safepoint_poll(*code_stub, Z_R0_scratch, true /* at_return */, true /* in_nmethod */); } } @@ -2718,6 +2724,10 @@ ins_attrib ins_alignment(1); ins_attrib ins_cannot_rematerialize(false); ins_attrib ins_should_rematerialize(false); +// Whether this node is expanded during code emission into a sequence of +// instructions and the first instruction can perform an implicit null check. +ins_attrib ins_is_late_expanded_null_check_candidate(false); + //----------OPERANDS----------------------------------------------------------- // Operand definitions must precede instruction definitions for correct // parsing in the ADLC because operands constitute user defined types diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 9d5ab8d3e47..fe7325aaa99 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2759,7 +2759,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, save_native_result(masm, ret_type, workspace_slot_offset); // Make Z_R2 available as work reg. - __ safepoint_poll(sync, Z_R1); + __ safepoint_poll(sync, Z_R1, true /* at_return */, false /* in_nmethod */); __ load_and_test_int(Z_R0, Address(Z_thread, JavaThread::suspend_flags_offset())); __ z_bre(no_block); diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index ebe04d8afd8..bcf1210ba93 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -50,6 +50,9 @@ #include "utilities/formatBuffer.hpp" #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" +#if INCLUDE_ZGC +#include "gc/z/zBarrierSetAssembler.hpp" +#endif // Declaration and definition of StubGenerator (no .hpp file). // For a more detailed description of the stub routine structure @@ -1372,6 +1375,12 @@ class StubGenerator: public StubCodeGenerator { BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->arraycopy_prologue(_masm, decorators, T_OBJECT, Z_ARG1, Z_ARG2, Z_ARG3); +#if INCLUDE_ZGC + if (UseZGC) { + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)bs; + zbs->generate_disjoint_oop_copy(_masm, dest_uninitialized); + } else +#endif generate_disjoint_copy(aligned, size, true, true); bs->arraycopy_epilogue(_masm, decorators, T_OBJECT, Z_ARG2, Z_ARG3, true); @@ -1483,6 +1492,12 @@ class StubGenerator: public StubCodeGenerator { BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->arraycopy_prologue(_masm, decorators, T_OBJECT, Z_ARG1, Z_ARG2, Z_ARG3); +#if INCLUDE_ZGC + if (UseZGC) { + ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)bs; + zbs->generate_conjoint_oop_copy(_masm, dest_uninitialized); + } else +#endif generate_conjoint_copy(aligned, size, true); // Must preserve ARG2, ARG3. bs->arraycopy_epilogue(_masm, decorators, T_OBJECT, Z_ARG2, Z_ARG3, true); @@ -3448,11 +3463,11 @@ class StubGenerator: public StubCodeGenerator { __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); // Load target method from receiver __ load_heap_oop(Z_method, Address(Z_ARG1, java_lang_invoke_MethodHandle::form_offset()), - noreg, noreg, IS_NOT_NULL); + Z_tmp_1, Z_tmp_2, IS_NOT_NULL); __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_LambdaForm::vmentry_offset()), - noreg, noreg, IS_NOT_NULL); + Z_tmp_1, Z_tmp_2, IS_NOT_NULL); __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_MemberName::method_offset()), - noreg, noreg, IS_NOT_NULL); + Z_tmp_1, Z_tmp_2, IS_NOT_NULL); __ z_lg(Z_method, Address(Z_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset())); __ z_stg(Z_method, Address(Z_thread, JavaThread::callee_target_offset())); // just in case callee is deoptimized diff --git a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp index ddcba7b2533..7fc962dbc0e 100644 --- a/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/templateInterpreterGenerator_s390.cpp @@ -1213,7 +1213,8 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { // Get mirror and store it in the frame as GC root for this Method*. __ mem2reg_opt(Z_R1_scratch, Address(constants_addr, ConstantPool::pool_holder_offset())); __ mem2reg_opt(Z_R1_scratch, Address(Z_R1_scratch, Klass::java_mirror_offset())); - __ resolve_oop_handle(Z_R1_scratch, Z_R0_scratch, Z_R1_scratch); + Register mirror_tmp = Z_R4; + __ resolve_oop_handle(Z_R1_scratch, mirror_tmp, Z_R0_scratch); __ z_stg(Z_R1_scratch, _z_ijava_state_neg(mirror), fp); BLOCK_COMMENT("} generate_fixed_frame: initialize interpreter state"); @@ -1588,7 +1589,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // Check for safepoint operation in progress and/or pending suspend requests. { Label Continue, do_safepoint; - __ safepoint_poll(do_safepoint, Z_R1); + __ safepoint_poll(do_safepoint, Z_R1, true /* at_return */, false /* in_nmethod */); // Check for suspend. __ load_and_test_int(Z_R0/*suspend_flags*/, thread_(suspend_flags)); __ z_bre(Continue); // 0 -> no flag set -> not suspended @@ -1889,7 +1890,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() { Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path, Z_R1); + __ safepoint_poll(slow_path, Z_R1, false /* at_return */, false /* in_nmethod */); BLOCK_COMMENT("CRC32_update {"); @@ -1938,7 +1939,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI Label slow_path; // If we need a safepoint check, generate full interpreter entry. - __ safepoint_poll(slow_path, Z_R1); + __ safepoint_poll(slow_path, Z_R1, false /* at_return */, false /* in_nmethod */); // We don't generate local frame and don't align stack because // we call stub code and there is no safepoint on this path. @@ -2078,7 +2079,7 @@ address TemplateInterpreterGenerator::generate_currentThread() { uint64_t entry_off = __ offset(); __ z_lg(Z_RET, Address(Z_thread, JavaThread::vthread_offset())); - __ resolve_oop_handle(Z_RET, Z_R0_scratch, Z_R1_scratch); + __ resolve_oop_handle(Z_RET, Z_R1_scratch, Z_R0_scratch); // Restore caller sp for c2i case. __ resize_frame_absolute(Z_R10, Z_R0, true); // Cut the stack back to where the caller started. diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 364a60b2455..6a1ae086916 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -475,14 +475,14 @@ void TemplateTable::fast_aldc(LdcType type) { // We are resolved if the resolved reference cache entry contains a // non-null object (CallSite, etc.). __ get_cache_index_at_bcp(index, 1, index_size); // Load index. - __ load_resolved_reference_at_index(Z_tos, index); + __ load_resolved_reference_at_index(Z_tos, index, Z_R1_scratch); __ z_ltgr(Z_tos, Z_tos); __ z_bre(L_do_resolve); // Convert null sentinel to null. __ load_const_optimized(Z_R1_scratch, (intptr_t)Universe::the_null_sentinel_addr()); __ z_lg(Z_R1_scratch, Address(Z_R1_scratch)); - __ resolve_oop_handle(Z_R1_scratch, Z_R0_scratch, Z_R1_scratch); + __ resolve_oop_handle(Z_R1_scratch, Z_tmp_2, Z_R0_scratch); __ z_cgr(Z_tos, Z_R1_scratch); __ z_brne(L_resolved); __ clear_reg(Z_tos); @@ -2580,7 +2580,7 @@ void TemplateTable::load_resolved_field_entry(Register obj, if (is_static) { __ load_sized_value(obj, Address(cache, ResolvedFieldEntry::field_holder_offset()), sizeof(void*), false); __ load_sized_value(obj, Address(obj, in_bytes(Klass::java_mirror_offset())), sizeof(void*), false); - __ resolve_oop_handle(obj, Z_R0_scratch, Z_R1_scratch); + __ resolve_oop_handle(obj, Z_R1_scratch, Z_R0_scratch); } } @@ -2619,7 +2619,7 @@ void TemplateTable::load_invokedynamic_entry(Register method) { // Push the appendix as a trailing parameter. // This must be done before we get the receiver, // since the parameter_size includes it. - __ load_resolved_reference_at_index(appendix, index); + __ load_resolved_reference_at_index(appendix, index, Z_ARG2); __ verify_oop(appendix); __ push_ptr(appendix); // Push appendix (MethodType, CallSite, etc.). __ bind(L_no_push); @@ -2656,7 +2656,7 @@ void TemplateTable::load_resolved_method_entry_handle(Register cache, // This must be done before we get the receiver, // since the parameter_size includes it. Register appendix = method; - __ load_resolved_reference_at_index(appendix, ref_index); + __ load_resolved_reference_at_index(appendix, ref_index, flags); __ verify_oop(appendix); __ push_ptr(appendix); // Push appendix (MethodType, CallSite, etc.). __ bind(L_no_push); diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 591d30c3a1c..7783c4602fe 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -427,6 +427,8 @@ class VM_Version: public Abstract_VM_Version { constexpr static bool supports_recursive_fast_locking() { return true; } + constexpr static bool supports_stack_watermark_barrier() { return true; } + // CPU feature query functions static const char* get_model_string() { return _model_string; } static bool has_StoreFacilityListExtended() { return (_features[0] & StoreFacilityListExtendedMask) == StoreFacilityListExtendedMask; } diff --git a/src/hotspot/os_cpu/linux_s390/gc/z/zSyscall_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/gc/z/zSyscall_linux_s390.hpp new file mode 100644 index 00000000000..27915f1a8a7 --- /dev/null +++ b/src/hotspot/os_cpu/linux_s390/gc/z/zSyscall_linux_s390.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 OS_CPU_LINUX_S390_GC_Z_ZSYSCALL_LINUX_S390_HPP +#define OS_CPU_LINUX_S390_GC_Z_ZSYSCALL_LINUX_S390_HPP + +#include + +// +// Support for building on older Linux systems +// + +#ifndef SYS_memfd_create +#define SYS_memfd_create 350 +#endif +#ifndef SYS_fallocate +#define SYS_fallocate 314 +#endif + +#endif // OS_CPU_LINUX_S390_GC_Z_ZSYSCALL_LINUX_S390_HPP From 4c93efe5c46aeae7a470406bf68138a813a6daf3 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 7 Oct 2026 06:12:33 +0000 Subject: [PATCH 76/98] 8393351: Improve exception handling for ClassPathZipEntry::has_entry() Reviewed-by: dholmes, kvn --- src/hotspot/share/cds/aotClassLocation.cpp | 12 ++++++++- src/hotspot/share/cds/aotMetaspace.cpp | 30 +++++++++++++-------- src/hotspot/share/cds/aotMetaspace.hpp | 1 + src/hotspot/share/classfile/classLoader.cpp | 20 +++++--------- src/hotspot/share/classfile/classLoader.hpp | 2 +- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp index d7337dead95..19327406a05 100644 --- a/src/hotspot/share/cds/aotClassLocation.cpp +++ b/src/hotspot/share/cds/aotClassLocation.cpp @@ -766,7 +766,17 @@ bool AOTClassLocationConfig::is_valid_classpath_index(int classpath_index, Insta ik->name()->utf8_length()); Handle class_loader(current, ik->class_loader()); const AOTClassLocation* cl = AOTClassLocationConfig::class_location_at(classpath_index); - if (!zip->has_entry(current, file_name, class_loader, cl->is_multi_release_jar())) { + bool found = zip->has_entry(file_name, class_loader, cl->is_multi_release_jar(), current); + if (current->has_pending_exception()) { + AOTMetaspace::writing_error(current->pending_exception()); + aot_log_warning(aot)("class %s cannot be archived because an exception was thrown when checking %s", + class_name, zip->name()); + current->clear_pending_exception(); + // Treat it as if the zip file didn't contain this class. This means transient OOM would + // not be catastrophic -- just fewer classes would be cached. + return false; + } + if (!found) { aot_log_warning(aot)("class %s cannot be archived because it was not defined from %s as claimed", class_name, zip->name()); return false; diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 11ad8820984..8733c794ea7 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -978,17 +978,7 @@ void AOTMetaspace::dump_static_archive(TRAPS) { StaticArchiveBuilder builder; dump_static_archive_impl(builder, THREAD); if (HAS_PENDING_EXCEPTION) { - if (PENDING_EXCEPTION->is_a(vmClasses::OutOfMemoryError_klass())) { - aot_log_error(aot)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " - "%zuM", MaxHeapSize/M); - AOTMetaspace::writing_error(); - } else { - oop message = java_lang_Throwable::message(PENDING_EXCEPTION); - aot_log_error(aot)("%s: %s", PENDING_EXCEPTION->klass()->external_name(), - message == nullptr ? "(null)" : java_lang_String::as_utf8_string(message)); - AOTMetaspace::writing_error(err_msg("Unexpected exception, use -Xlog:aot%s,exceptions=trace for detail", - CDSConfig::new_aot_flags_used() ? "" : ",cds")); - } + writing_error(PENDING_EXCEPTION); } if (CDSConfig::new_aot_flags_used()) { @@ -1010,6 +1000,8 @@ void AOTMetaspace::dump_static_archive(TRAPS) { vm_direct_exit(0); } } + + // If we have a pending exception here, it will be propagated to the caller of this function. } #if INCLUDE_CDS_JAVA_HEAP && defined(_LP64) @@ -1513,6 +1505,22 @@ void AOTMetaspace::writing_error(const char* message) { } } +void AOTMetaspace::writing_error(oop exception_oop) { + if (exception_oop->is_a(vmClasses::OutOfMemoryError_klass())) { + aot_log_error(aot)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = " + "%zuM", MaxHeapSize/M); + AOTMetaspace::writing_error(); + } else { + ResourceMark rm; + oop message = java_lang_Throwable::message(exception_oop); + aot_log_error(aot)("%s: %s", exception_oop->klass()->external_name(), + message == nullptr ? "(null)" : java_lang_String::as_utf8_string(message)); + AOTMetaspace::writing_error(err_msg("Unexpected exception, use -Xlog:aot%s,exceptions=trace for detail", + CDSConfig::new_aot_flags_used() ? "" : ",cds")); + } +} + + void AOTMetaspace::initialize_runtime_shared_and_meta_spaces() { assert(CDSConfig::is_using_archive(), "Must be called when UseSharedSpaces is enabled"); MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE; diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 9a2151f7308..cc24bce52e0 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -123,6 +123,7 @@ class AOTMetaspace : AllStatic { static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0); [[noreturn]] static void unrecoverable_writing_error(const char* message = nullptr); static void writing_error(const char* message = nullptr); + static void writing_error(oop exception_oop); static void make_method_handle_intrinsics_shareable() NOT_CDS_RETURN; static void write_method_handle_intrinsics() NOT_CDS_RETURN; diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index d27681e0b26..00a6442d13c 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -369,13 +369,13 @@ ClassPathZipEntry::~ClassPathZipEntry() { FREE_C_HEAP_ARRAY(_zip_name); } -bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name, Handle class_loader, bool is_multi_release_jar) { +bool ClassPathZipEntry::has_entry(const char* name, Handle class_loader, bool is_multi_release_jar, TRAPS) { // check whether zip archive contains name jint name_len; jint filesize; { - ThreadToNativeFromVM ttn(current); + ThreadToNativeFromVM ttn(THREAD); jzentry* entry = ZipLibrary::find_entry(_zip, name, &filesize, &name_len); if (entry != nullptr) { ZipLibrary::free_entry(_zip, entry); @@ -391,10 +391,10 @@ bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name, Handle if (class_loader != nullptr && is_multi_release_jar) { assert(SystemDictionaryShared::is_builtin_loader(ClassLoaderData::class_loader_data(class_loader())), "must be"); JavaValue result(T_OBJECT); - oop class_name_oop = java_lang_String::create_oop_from_str(name, current); - Handle h_class_name(current, class_name_oop); - oop zip_name_oop = CDSProtectionDomain::to_file_URL(_zip_name, Handle(), current); - Handle h_zip_name(current, zip_name_oop); + oop class_name_oop = java_lang_String::create_oop_from_str(name, CHECK_false); + Handle h_class_name(THREAD, class_name_oop); + oop zip_name_oop = CDSProtectionDomain::to_file_URL(_zip_name, Handle(), CHECK_false); + Handle h_zip_name(THREAD, zip_name_oop); // URL ClassLoader.getResource(String name) JavaCalls::call_static(&result, @@ -404,13 +404,7 @@ bool ClassPathZipEntry::has_entry(JavaThread* current, const char* name, Handle class_loader, h_zip_name, h_class_name, - current); - - // Not using CHECK, the thread must be checked manually - if (current->has_pending_exception()) { - current->clear_pending_exception(); - return false; - } + CHECK_false); assert(result.get_type() == T_OBJECT, "just checking"); if (result.get_oop() != nullptr) { diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index 030e083374c..eafcb04f881 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -93,7 +93,7 @@ class ClassPathZipEntry: public ClassPathEntry { const char* name() const { return _zip_name; } ClassPathZipEntry(jzfile* zip, const char* zip_name); virtual ~ClassPathZipEntry(); - bool has_entry(JavaThread* current, const char* name, Handle class_loader, bool is_multi_release_jar); + bool has_entry(const char* name, Handle class_loader, bool is_multi_release_jar, TRAPS); u1* open_entry(JavaThread* current, const char* name, jint* filesize, bool nul_terminate); ClassFileStream* open_stream(JavaThread* current, const char* name); }; From 7a232d57a7cd9f1caae3cdcf30137f5d646ed212 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Wed, 7 Oct 2026 07:24:39 +0000 Subject: [PATCH 77/98] 8393454: Rename ValueFieldLayoutInfo to ValueFieldInfo Reviewed-by: aboldtch, matsaave --- .../gc/shared/barrierSetAssembler_aarch64.cpp | 6 +- .../gc/shared/barrierSetAssembler_aarch64.hpp | 2 +- .../cpu/aarch64/interp_masm_aarch64.cpp | 6 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 20 +++--- .../cpu/aarch64/macroAssembler_aarch64.hpp | 4 +- .../ppc/gc/shared/barrierSetAssembler_ppc.cpp | 6 +- .../ppc/gc/shared/barrierSetAssembler_ppc.hpp | 2 +- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 6 +- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 16 ++--- src/hotspot/cpu/ppc/macroAssembler_ppc.hpp | 4 +- .../gc/shared/barrierSetAssembler_riscv.cpp | 6 +- .../gc/shared/barrierSetAssembler_riscv.hpp | 2 +- src/hotspot/cpu/riscv/interp_masm_riscv.cpp | 6 +- .../cpu/riscv/macroAssembler_riscv.cpp | 22 +++---- .../cpu/riscv/macroAssembler_riscv.hpp | 4 +- .../gc/shared/barrierSetAssembler_s390.cpp | 6 +- .../gc/shared/barrierSetAssembler_s390.hpp | 2 +- src/hotspot/cpu/s390/interp_masm_s390.cpp | 10 +-- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 18 +++--- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 4 +- .../x86/gc/shared/barrierSetAssembler_x86.cpp | 6 +- .../x86/gc/shared/barrierSetAssembler_x86.hpp | 2 +- src/hotspot/cpu/x86/interp_masm_x86.cpp | 6 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 16 ++--- src/hotspot/cpu/x86/macroAssembler_x86.hpp | 4 +- .../share/classfile/classFileParser.cpp | 48 +++++++------- .../share/classfile/classFileParser.hpp | 4 +- .../share/classfile/fieldLayoutBuilder.cpp | 64 +++++++++---------- .../share/classfile/fieldLayoutBuilder.hpp | 10 +-- .../share/gc/shared/barrierSetRuntime.cpp | 12 ++-- .../share/gc/shared/barrierSetRuntime.hpp | 4 +- src/hotspot/share/memory/allocation.hpp | 2 +- src/hotspot/share/oops/instanceKlass.cpp | 26 ++++---- src/hotspot/share/oops/instanceKlass.hpp | 28 ++++---- .../share/oops/instanceKlass.inline.hpp | 4 +- src/hotspot/share/oops/valuePayload.hpp | 2 +- .../share/oops/valuePayload.inline.hpp | 22 +++---- src/hotspot/share/prims/jvmtiTagMap.cpp | 6 +- src/hotspot/share/prims/unsafe.cpp | 2 +- src/hotspot/share/runtime/vmStructs.cpp | 10 +-- src/hotspot/share/services/heapDumper.cpp | 6 +- .../sun/jvm/hotspot/oops/InstanceKlass.java | 10 +-- .../sun/jvm/hotspot/oops/OopField.java | 2 +- ...eldLayoutInfo.java => ValueFieldInfo.java} | 6 +- ...nfoArray.java => ValueFieldInfoArray.java} | 14 ++-- 45 files changed, 234 insertions(+), 234 deletions(-) rename src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/{ValueFieldLayoutInfo.java => ValueFieldInfo.java} (91%) rename src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/{ValueFieldLayoutInfoArray.java => ValueFieldInfoArray.java} (84%) diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp index 668176d86f8..294e65936a9 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp @@ -138,15 +138,15 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info) { + Register src, Register dst, Register value_field_info) { // flat_field_copy implementation is fairly complex, and there are not any // "short-cuts" to be made from asm. What there is, appears to have the same // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds // of hand-rolled instructions... if (decorators & IS_DEST_UNINITIALIZED) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info); } } diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp index 773814c9d8f..fb6405629be 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp @@ -100,7 +100,7 @@ class BarrierSetAssembler: public CHeapObj { Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info); + Register src, Register dst, Register value_field_info); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 174cedb41a9..43e7ad64c94 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -230,12 +230,12 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_ load_klass(tmp1, r0, tmp2); payload_address(r0, r0, tmp1); - Register layout_info = field_offset; + Register vfi = field_offset; load_unsigned_short(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset()))); ldr(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset()))); - value_field_layout_info(tmp2, tmp1, layout_info); + value_field_info(tmp2, tmp1, vfi); - flat_field_copy(IN_HEAP, r0, obj, layout_info); + flat_field_copy(IN_HEAP, r0, obj, vfi); b(done); bind(slow_path); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index e1bbb4a6aec..3069df838f8 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5675,9 +5675,9 @@ void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators, } void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, - Register value_field_layout_info) { + Register value_field_info) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->flat_field_copy(this, decorators, src, dst, value_field_layout_info); + bs->flat_field_copy(this, decorators, src, dst, value_field_info); } void MacroAssembler::payload_offset(Register value_klass, Register offset) { @@ -5810,19 +5810,19 @@ void MacroAssembler::verify_tlab() { #endif } -void MacroAssembler::value_field_layout_info(Register holder_klass, Register index, Register layout_info) { - assert_different_registers(holder_klass, index, layout_info); - ValueFieldLayoutInfo array[2]; +void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) { + assert_different_registers(holder_klass, index, vfi); + ValueFieldInfo array[2]; int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements if (is_power_of_2(size)) { lsl(index, index, log2i_exact(size)); // Scale index by power of 2 } else { - mov(layout_info, size); - mul(index, index, layout_info); // Scale the index to be the entry index * array_element_size + mov(vfi, size); + mul(index, index, vfi); // Scale the index to be the entry index * array_element_size } - ldr(layout_info, Address(holder_klass, InstanceKlass::value_field_layout_info_array_offset())); - add(layout_info, layout_info, Array::base_offset_in_bytes()); - lea(layout_info, Address(layout_info, index)); + ldr(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset())); + add(vfi, vfi, Array::base_offset_in_bytes()); + lea(vfi, Address(vfi, index)); } // Writes to stack successive pages until offset reached to check for diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index fedcdb2482c..6b192bddf43 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -980,7 +980,7 @@ class MacroAssembler: public Assembler { void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); - void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info); + void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info); // value type data payload offsets... void payload_offset(Register value_klass, Register offset); @@ -1055,7 +1055,7 @@ class MacroAssembler: public Assembler { ); void verify_tlab(); - void value_field_layout_info(Register holder_klass, Register index, Register layout_info); + void value_field_info(Register holder_klass, Register index, Register vfi); // interface method calling void lookup_interface_method(Register recv_klass, diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp index 8be5312b42d..2527cb30f84 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp @@ -117,15 +117,15 @@ void BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, } void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info) { + Register src, Register dst, Register value_field_info) { // flat_field_copy implementation is fairly complex, and there are not any // "short-cuts" to be made from asm. What there is, appears to have the same // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds // of hand-rolled instructions... if (decorators & IS_DEST_UNINITIALIZED) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info); } } diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp index 655f337e73e..f24fe11c833 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp @@ -61,7 +61,7 @@ class BarrierSetAssembler: public CHeapObj { MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null = nullptr); virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info); + Register src, Register dst, Register value_field_info); virtual void resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2, diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index f4abace2fa3..5dd5d830252 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -2449,12 +2449,12 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1, load_klass(tmp1, value); payload_address(value, value, tmp1, tmp2); - Register layout_info = field_offset; + Register vfi = field_offset; lhz(tmp1, in_bytes(ResolvedFieldEntry::field_index_offset()), entry); ld(tmp2, in_bytes(ResolvedFieldEntry::field_holder_offset()), entry); - value_field_layout_info(tmp2, tmp1, layout_info); + value_field_info(tmp2, tmp1, vfi); - flat_field_copy(IN_HEAP, value, obj, layout_info); + flat_field_copy(IN_HEAP, value, obj, vfi); b(done); bind(slow_path); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 7bec14d6007..a9b2eeb2382 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -3374,9 +3374,9 @@ void MacroAssembler::load_metadata(Register dst, Register src) { } } -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info) { +void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->flat_field_copy(this, decorators, src, dst, value_field_layout_info); + bs->flat_field_copy(this, decorators, src, dst, value_field_info); } void MacroAssembler::payload_offset(Register value_klass, Register offset) { @@ -3390,18 +3390,18 @@ void MacroAssembler::payload_address(Register oop, Register data, Register value add(data, oop, t1); } -void MacroAssembler::value_field_layout_info(Register holder_klass, Register index, Register layout_info) { - assert_different_registers(holder_klass, index, layout_info); - ValueFieldLayoutInfo array[2]; +void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) { + assert_different_registers(holder_klass, index, vfi); + ValueFieldInfo array[2]; int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements if (is_power_of_2(size)) { sldi(index, index, log2i_exact(size)); // Scale index by power of 2 } else { mulld(index, index, size); // Scale the index to be the entry index * array_element_size } - ld(layout_info, InstanceKlass::value_field_layout_info_array_offset(), holder_klass); - addi(layout_info, layout_info, Array::base_offset_in_bytes()); - add(layout_info, layout_info, index); + ld(vfi, InstanceKlass::value_field_info_array_offset(), holder_klass); + addi(vfi, vfi, Array::base_offset_in_bytes()); + add(vfi, vfi, index); } diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index d4d4da17c44..41c40ff0f81 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -844,9 +844,9 @@ class MacroAssembler: public Assembler { void load_metadata(Register dst, Register src); - void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info); + void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info); - void value_field_layout_info(Register holder_klass, Register index, Register layout_info); + void value_field_info(Register holder_klass, Register index, Register vfi); // value type data payload offsets... void payload_offset(Register value_klass, Register offset); diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp index a6339a7929e..cc1581942fd 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp @@ -137,15 +137,15 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info) { + Register src, Register dst, Register value_field_info) { // flat_field_copy implementation is fairly complex, and there are not any // "short-cuts" to be made from asm. What there is, appears to have the same // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds // of hand-rolled instructions... if (decorators & IS_DEST_UNINITIALIZED) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info); } } diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp index 0e159dfbc98..572a9574bab 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp @@ -76,7 +76,7 @@ class BarrierSetAssembler: public CHeapObj { Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info); + Register src, Register dst, Register value_field_info); virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env, Register obj, Register tmp, Label& slowpath); diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 9d3305c7030..99690c3c846 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1886,12 +1886,12 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_ load_klass(tmp1, x10); payload_address(x10, x10, tmp1); - Register layout_info = field_offset; + Register vfi = field_offset; load_unsigned_short(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset()))); ld(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset()))); - value_field_layout_info(tmp2, tmp1, layout_info); + value_field_info(tmp2, tmp1, vfi); - flat_field_copy(IN_HEAP, x10, obj, layout_info); + flat_field_copy(IN_HEAP, x10, obj, vfi); j(done); bind(slow_path); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 82fc4c6120c..f74845a8880 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -3803,26 +3803,26 @@ void MacroAssembler::mov_metadata(Register dst, Metadata* obj, Register tmp) { movptr(dst, Address((address)obj, rspec), tmp); } -void MacroAssembler::value_field_layout_info(Register holder_klass, Register index, Register layout_info) { - assert_different_registers(holder_klass, index, layout_info); - ValueFieldLayoutInfo array[2]; +void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) { + assert_different_registers(holder_klass, index, vfi); + ValueFieldInfo array[2]; int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements if (is_power_of_2(size)) { slli(index, index, log2i_exact(size)); // Scale index by power of 2 } else { - mv(layout_info, size); - mul(index, index, layout_info); // Scale the index to be the entry index * array_element_size + mv(vfi, size); + mul(index, index, vfi); // Scale the index to be the entry index * array_element_size } - ld(layout_info, Address(holder_klass, InstanceKlass::value_field_layout_info_array_offset())); - add(layout_info, layout_info, Array::base_offset_in_bytes()); - add(layout_info, layout_info, index); - la(layout_info, Address(layout_info)); + ld(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset())); + add(vfi, vfi, Array::base_offset_in_bytes()); + add(vfi, vfi, index); + la(vfi, Address(vfi)); } void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, - Register value_field_layout_info) { + Register value_field_info) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->flat_field_copy(this, decorators, src, dst, value_field_layout_info); + bs->flat_field_copy(this, decorators, src, dst, value_field_info); } void MacroAssembler::payload_offset(Register value_klass, Register offset) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 780ffa4ebc6..4b403a14326 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -270,9 +270,9 @@ class MacroAssembler: public Assembler { // Check array klass layout helper for flat or null-free arrays... void test_flat_array_layout(Register lh, Label& is_flat_array); - void value_field_layout_info(Register holder_klass, Register index, Register layout_info); + void value_field_info(Register holder_klass, Register index, Register vfi); - void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info); + void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info); // value type data payload offsets... void payload_offset(Register value_klass, Register offset); diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp index 3544d72a2c7..e50f84dfdd6 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp @@ -113,15 +113,15 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators // Generic implementation. GCs can provide an optimized one. void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info) { + Register src, Register dst, Register value_field_info) { // flat_field_copy implementation is fairly complex, and there are not any // "short-cuts" to be made from asm. What there is, appears to have the same // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds // of hand-rolled instructions... if (decorators & IS_DEST_UNINITIALIZED) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info); } } diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp index 0654eaec30a..d423210fe33 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp @@ -53,7 +53,7 @@ class BarrierSetAssembler: public CHeapObj { const Address& addr, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info); + Register src, Register dst, Register value_field_info); virtual void resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2); virtual void resolve_global_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index 670ec56188e..399e4a1e8f9 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -2241,16 +2241,16 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_ load_klass(tmp1, Z_tos); payload_addr(Z_tos, Z_tos, tmp1); - // Load the ValueFieldLayoutInfo for this field: + // Load the ValueFieldInfo for this field: // field_index (u2) from the entry, holder klass pointer from the entry. - // Reuse field_offset as the layout_info register from here on. - Register layout_info = field_offset; + // Reuse field_offset as the vfi (value_field_info) register from here on. + Register vfi = field_offset; load_sized_value(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset())), sizeof(u2), false); load_sized_value(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset())), sizeof(void*), false); - value_field_layout_info(tmp2, tmp1, layout_info); + value_field_info(tmp2, tmp1, vfi); // Inline byte-copy of the value's payload into the flat field slot. - flat_field_copy(IN_HEAP, Z_tos, obj, layout_info); + flat_field_copy(IN_HEAP, Z_tos, obj, vfi); z_bru(done); bind(slow_path); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index d465d83445a..3f8591d6241 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -4300,26 +4300,26 @@ void MacroAssembler::test_flat_array_layout(Register lh, Label& is_flat_array) { z_brnaz(is_flat_array); } -void MacroAssembler::value_field_layout_info(Register holder_klass, Register index, Register layout_info) { - assert_different_registers(holder_klass, index, layout_info); - z_lg(layout_info, Address(holder_klass, InstanceKlass::value_field_layout_info_array_offset())); +void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) { + assert_different_registers(holder_klass, index, vfi); + z_lg(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset())); #ifdef ASSERT { Label done; - z_ltgr(layout_info, layout_info); + z_ltgr(vfi, vfi); z_brne(done); - stop("value_field_layout_info_array is null"); + stop("value_field_info_array is null"); bind(done); } #endif - ValueFieldLayoutInfo array[2]; + ValueFieldInfo array[2]; int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements if (is_power_of_2(size)) { z_sllg(index, index, log2i_exact(size)); // Scale index by power of 2 } else { z_msgfi(index, size); // Scale the index to be the entry index * array_element_size } - z_lay(layout_info, Address(layout_info, index, Array::base_offset_in_bytes())); + z_lay(vfi, Address(vfi, index, Array::base_offset_in_bytes())); } // Compare klass ptr in memory against klass ptr in register. @@ -4566,9 +4566,9 @@ void MacroAssembler::store_heap_oop(Register Roop, const Address &a, } void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, - Register value_field_layout_info) { + Register value_field_info) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->flat_field_copy(this, decorators, src, dst, value_field_layout_info); + bs->flat_field_copy(this, decorators, src, dst, value_field_info); } void MacroAssembler::payload_offset(Register value_klass, Register offset) { diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 05f9869937f..df121bd575c 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -880,7 +880,7 @@ class MacroAssembler: public Assembler { void test_null_free_array_oop(Register oop, Register temp_reg, Label& is_null_free_array); void test_non_null_free_array_oop(Register oop, Register temp_reg, Label& is_non_null_free_array); void test_flat_array_layout(Register lh, Label& is_flat_array); - void value_field_layout_info(Register holder_klass, Register index, Register layout_info); + void value_field_info(Register holder_klass, Register index, Register vfi); // This function calculates the size of the code generated by // decode_klass_not_null(register dst) @@ -905,7 +905,7 @@ class MacroAssembler: public Assembler { const Address& addr, Register dst, Register tmp1, Register tmp2, Label *is_null = nullptr); - void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info); + void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info); // value type data payload offsets void payload_offset(Register value_klass, Register offset); diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp index fdae86bf7de..7e0e53f9dd1 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp @@ -165,15 +165,15 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators } void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info) { + Register src, Register dst, Register value_field_info) { // flat_field_copy implementation is fairly complex, and there are not any // "short-cuts" to be made from asm. What there is, appears to have the same // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds // of hand-rolled instructions... if (decorators & IS_DEST_UNINITIALIZED) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_layout_info); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info); } } diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp index 5879c74963b..44cd2d9becf 100644 --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp @@ -49,7 +49,7 @@ class BarrierSetAssembler: public CHeapObj { Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators, - Register src, Register dst, Register value_field_layout_info); + Register src, Register dst, Register value_field_info); // The copy_[load/store]_at functions are used by arraycopy stubs. Be careful to only use // r10 (aka rscratch1) in a context where restore_arg_regs_using_thread has been used instead diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 9c2c73ee2c7..9a91c07156c 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1145,10 +1145,10 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1, load_unsigned_short(idx, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset()))); movptr(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset()))); - Register layout_info = off; - value_field_layout_info(tmp2, idx, layout_info); + Register vfi = off; + value_field_info(tmp2, idx, vfi); - flat_field_copy(IN_HEAP, value, obj, layout_info); + flat_field_copy(IN_HEAP, value, obj, vfi); jmp(done); bind(slow_path); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index d09813760e7..346acaab432 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -3811,26 +3811,26 @@ void MacroAssembler::zero_memory(Register address, Register length_in_bytes, int bind(done); } -void MacroAssembler::value_field_layout_info(Register holder_klass, Register index, Register layout_info) { - movptr(layout_info, Address(holder_klass, InstanceKlass::value_field_layout_info_array_offset())); +void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) { + movptr(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset())); #ifdef ASSERT { Label done; - cmpptr(layout_info, 0); + cmpptr(vfi, 0); jcc(Assembler::notEqual, done); - stop("value_field_layout_info_array is null"); + stop("value_field_info_array is null"); bind(done); } #endif - ValueFieldLayoutInfo array[2]; + ValueFieldInfo array[2]; int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements if (is_power_of_2(size)) { shll(index, log2i_exact(size)); // Scale index by power of 2 } else { imull(index, index, size); // Scale the index to be the entry index * array_element_size } - lea(layout_info, Address(layout_info, index, Address::times_1, Array::base_offset_in_bytes())); + lea(vfi, Address(vfi, index, Address::times_1, Array::base_offset_in_bytes())); } // Look up the method for a megamorphic invokeinterface call. @@ -5639,9 +5639,9 @@ void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators, Ad } void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, - Register value_field_layout_info) { + Register value_field_info) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->flat_field_copy(this, decorators, src, dst, value_field_layout_info); + bs->flat_field_copy(this, decorators, src, dst, value_field_info); } void MacroAssembler::payload_offset(Register value_klass, Register offset) { diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 714efb5e7c0..3ffe8eba6c2 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -392,7 +392,7 @@ class MacroAssembler: public Assembler { void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3); - void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_layout_info); + void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info); // value type data payload offsets... void payload_offset(Register value_klass, Register offset); @@ -554,7 +554,7 @@ class MacroAssembler: public Assembler { ); void zero_memory(Register address, Register length_in_bytes, int offset_in_bytes, Register temp); - void value_field_layout_info(Register klass, Register index, Register layout_info); + void value_field_info(Register klass, Register index, Register vfi); void population_count(Register dst, Register src, Register scratch1, Register scratch2); diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 7c7fdd99a1e..e94f1161c4d 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -3973,7 +3973,7 @@ void ClassFileParser::apply_parsed_class_metadata( this_klass->set_annotations(_combined_annotations); this_klass->set_permitted_subclasses(_permitted_subclasses); this_klass->set_record_components(_record_components); - this_klass->set_value_field_layout_info_array(_value_field_layout_info_array); + this_klass->set_value_field_info_array(_value_field_info_array); DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table)); @@ -5568,9 +5568,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, TempNewSymbol name = Signature::strip_envelope(sig); if (name == _class_name) { // Replace the nullptr previously stored now that we have the InstanceKlass for this klass. - _value_field_layout_info_array->adr_at(fieldinfo.index())->set_klass(ValueKlass::cast(ik)); + _value_field_info_array->adr_at(fieldinfo.index())->set_klass(ValueKlass::cast(ik)); } - assert(_value_field_layout_info_array->adr_at(fieldinfo.index())->klass()->is_value_klass(), "Must be"); + assert(_value_field_info_array->adr_at(fieldinfo.index())->klass()->is_value_klass(), "Must be"); } } } @@ -5596,7 +5596,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, assert(nullptr == _combined_annotations, "invariant"); assert(nullptr == _record_components, "invariant"); assert(nullptr == _permitted_subclasses, "invariant"); - assert(nullptr == _value_field_layout_info_array, "invariant"); + assert(nullptr == _value_field_info_array, "invariant"); if (_has_localvariable_table) { ik->set_has_localvariable_table(true); @@ -5862,7 +5862,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream, _klass_to_deallocate(nullptr), _parsed_annotations(nullptr), _layout_info(nullptr), - _value_field_layout_info_array(nullptr), + _value_field_info_array(nullptr), _temp_field_info(nullptr), _method_ordering(nullptr), _all_mirandas(nullptr), @@ -5937,7 +5937,7 @@ void ClassFileParser::clear_class_metadata() { _class_annotations = _class_type_annotations = nullptr; _fields_annotations = _fields_type_annotations = nullptr; _record_components = nullptr; - _value_field_layout_info_array = nullptr; + _value_field_info_array = nullptr; } // Destructor to clean up @@ -5957,8 +5957,8 @@ ClassFileParser::~ClassFileParser() { MetadataFactory::free_array(_loader_data, _fields_status); } - if (_value_field_layout_info_array != nullptr) { - MetadataFactory::free_array(_loader_data, _value_field_layout_info_array); + if (_value_field_info_array != nullptr) { + MetadataFactory::free_array(_loader_data, _value_field_info_array); } if (_methods != nullptr) { @@ -6415,15 +6415,15 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st FieldLayoutBuilder lb(class_name(), loader_data(), super_klass(), _cp, /*_fields*/ _temp_field_info, access_flags().is_identity_class() && _parsed_annotations->is_contended(), is_concrete_value_class(), access_flags().is_abstract() && !access_flags().is_identity_class() && !access_flags().is_interface(), - _must_be_atomic, _layout_info, _value_field_layout_info_array); + _must_be_atomic, _layout_info, _value_field_info_array); lb.build_layout(); // If it turned out that we didn't flatten any of the fields, we deallocate - // the array of ValueFieldLayoutInfo since it isn't needed, and so it isn't + // the array of ValueFieldInfo since it isn't needed, and so it isn't // transferred to the allocated InstanceKlass. - if (_value_field_layout_info_array != nullptr && !(_layout_info->_has_flat_fields || _has_null_restricted_static_fields)) { - MetadataFactory::free_array(_loader_data, _value_field_layout_info_array); - _value_field_layout_info_array = nullptr; + if (_value_field_info_array != nullptr && !(_layout_info->_has_flat_fields || _has_null_restricted_static_fields)) { + MetadataFactory::free_array(_loader_data, _value_field_info_array); + _value_field_info_array = nullptr; } int injected_fields_count = _temp_field_info->length() - _java_fields_count; @@ -6482,9 +6482,9 @@ void ClassFileParser::fetch_field_classes(ConstantPool* cp, TRAPS) { if (fieldinfo.field_flags().is_null_free_value_type() && !is_concrete_value_class()) { fieldinfo.field_flags_addr()->update_null_free_value_type(false); } else { - // Dummy setting to trigger the allocation of the value_field_layout_info array - + // Dummy setting to trigger the allocation of the value_field_info array - // the real pointer will be set later in ::fill_instance_klass, once the ValueKlass has been allocated. - set_value_field_layout_info_klass(fieldinfo.index(), nullptr, CHECK); + set_value_field_info_klass(fieldinfo.index(), nullptr, CHECK); } continue; } @@ -6502,7 +6502,7 @@ void ClassFileParser::fetch_field_classes(ConstantPool* cp, TRAPS) { if (klass != nullptr) { if (klass->is_value_klass()) { - set_value_field_layout_info_klass(fieldinfo.index(), ValueKlass::cast(klass), CHECK); + set_value_field_info_klass(fieldinfo.index(), ValueKlass::cast(klass), CHECK); log_info(class, preload)("Preloading of class %s during loading of class %s " "(cause: field type in LoadableDescriptors attribute) succeeded", name->as_C_string(), _class_name->as_C_string()); @@ -6540,7 +6540,7 @@ void ClassFileParser::fetch_field_classes(ConstantPool* cp, TRAPS) { oop loader = loader_data()->class_loader(); InstanceKlass* klass = SystemDictionary::find_instance_klass(THREAD, name, Handle(THREAD, loader)); if (klass != nullptr && klass->is_value_klass()) { - set_value_field_layout_info_klass(fieldinfo.index(), ValueKlass::cast(klass), CHECK); + set_value_field_info_klass(fieldinfo.index(), ValueKlass::cast(klass), CHECK); ResourceMark rm(THREAD); log_info(class, preload)("During loading of class %s , class %s found in local system dictionary" "(field type not in LoadableDescriptors attribute)", @@ -6576,19 +6576,19 @@ void ClassFileParser::set_klass(InstanceKlass* klass) { _klass = klass; } -void ClassFileParser::set_value_field_layout_info_klass(int field_index, ValueKlass* vk, TRAPS) { +void ClassFileParser::set_value_field_info_klass(int field_index, ValueKlass* vk, TRAPS) { assert(field_index >= 0 && field_index < java_fields_count(), "IOOB: 0 <= %d < %d", field_index, (int)java_fields_count()); - // The array of ValueFieldLayoutInfo is allocated on demand. This way the array is + // The array of ValueFieldInfo is allocated on demand. This way the array is // never allocated for an InstanceKlass which has no need for this information. - if (_value_field_layout_info_array == nullptr) { - _value_field_layout_info_array = MetadataFactory::new_array(_loader_data, - java_fields_count(), - CHECK); + if (_value_field_info_array == nullptr) { + _value_field_info_array = MetadataFactory::new_array(_loader_data, + java_fields_count(), + CHECK); } // Set the Klass for the field's index - _value_field_layout_info_array->adr_at(field_index)->set_klass(vk); + _value_field_info_array->adr_at(field_index)->set_klass(vk); } void ClassFileParser::set_klass_to_deallocate(InstanceKlass* klass) { diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 4ac29b4b7d8..314303a4a56 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -178,7 +178,7 @@ class ClassFileParser { ClassAnnotationCollector* _parsed_annotations; FieldLayoutInfo* _layout_info; - Array* _value_field_layout_info_array; + Array* _value_field_info_array; GrowableArray* _temp_field_info; const intArray* _method_ordering; GrowableArray* _all_mirandas; @@ -254,7 +254,7 @@ class ClassFileParser { void set_klass(InstanceKlass* instance); - void set_value_field_layout_info_klass(int field_index, ValueKlass* vk, TRAPS); + void set_value_field_info_klass(int field_index, ValueKlass* vk, TRAPS); void set_class_bad_constant_seen(short bad_constant); short class_bad_constant_seen() { return _bad_constant_seen; } diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index 0c44f4bbfbb..d55adf8093f 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -38,7 +38,7 @@ #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" -static LayoutKind field_layout_selection(FieldInfo field_info, Array* value_field_layout_info_array, +static LayoutKind field_layout_selection(FieldInfo field_info, Array* value_field_info_array, bool can_use_atomic_flat) { // The can_use_atomic_flat argument indicates if an atomic flat layout can be used for this field. @@ -65,13 +65,13 @@ static LayoutKind field_layout_selection(FieldInfo field_info, Arrayadr_at(field_info.index())->klass() == nullptr) { + if (value_field_info_array == nullptr || value_field_info_array->adr_at(field_info.index())->klass() == nullptr) { // field's type is not a known value class, using a reference return LayoutKind::REFERENCE; } - ValueFieldLayoutInfo* value_field_layout_info = value_field_layout_info_array->adr_at(field_info.index()); - ValueKlass* vk = value_field_layout_info->klass(); + ValueFieldInfo* value_field_info = value_field_info_array->adr_at(field_info.index()); + ValueKlass* vk = value_field_info->klass(); if (field_info.field_flags().is_null_free_value_type()) { assert(field_info.access_flags().is_strict(), "null-free fields must be strict"); @@ -104,12 +104,12 @@ static LayoutKind field_layout_selection(FieldInfo field_info, Array* value_field_layout_info_array, +static LayoutKind adjust_with_budget(FieldInfo field_info, Array* value_field_info_array, LayoutKind lk, int& budget) { if (lk == LayoutKind::REFERENCE) return lk; assert(LayoutKindHelper::is_flat((lk)), "Must be"); - ValueFieldLayoutInfo* value_field_layout_info = value_field_layout_info_array->adr_at(field_info.index()); - ValueKlass* vk = value_field_layout_info->klass(); + ValueFieldInfo* value_field_info = value_field_info_array->adr_at(field_info.index()); + ValueKlass* vk = value_field_info->klass(); int size = vk->layout_size_in_bytes(lk); if (size > budget) { return LayoutKind::REFERENCE; @@ -119,7 +119,7 @@ static LayoutKind adjust_with_budget(FieldInfo field_info, Array* li) { +static bool field_is_flattenable(FieldInfo fieldinfo, LayoutKind lk, Array* vfi) { if (fieldinfo.field_flags().is_null_free_value_type()) { // A null-free value type is always flattenable return true; @@ -134,10 +134,10 @@ static bool field_is_flattenable(FieldInfo fieldinfo, LayoutKind lk, Arrayadr_at(field_index)->klass() != nullptr && - !li->adr_at(field_index)->klass()->is_identity_class() && - !li->adr_at(field_index)->klass()->is_abstract()) { + vfi != nullptr && + vfi->adr_at(field_index)->klass() != nullptr && + !vfi->adr_at(field_index)->klass()->is_identity_class() && + !vfi->adr_at(field_index)->klass()->is_abstract()) { // The field's klass is not an identity class or abstract return true; } @@ -249,9 +249,9 @@ void FieldGroup::add_to_big_primitive_list(LayoutRawBlock* block) { _big_primitive_fields->append(block); } -FieldLayout::FieldLayout(GrowableArray* field_info, Array* value_field_layout_info_array, ConstantPool* cp) : +FieldLayout::FieldLayout(GrowableArray* field_info, Array* value_field_info_array, ConstantPool* cp) : _field_info(field_info), - _value_field_layout_info_array(value_field_layout_info_array), + _value_field_info_array(value_field_info_array), _cp(cp), _blocks(nullptr), _start(_blocks), @@ -486,11 +486,11 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_nonstati } LayoutRawBlock* block; if (fs.is_flat()) { - ValueFieldLayoutInfo layout_info = ik->value_field_layout_info(fs.index()); - ValueKlass* vk = layout_info.klass(); + ValueFieldInfo value_field_info = ik->value_field_info(fs.index()); + ValueKlass* vk = value_field_info.klass(); block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, - vk->layout_size_in_bytes(layout_info.kind()), - vk->layout_alignment(layout_info.kind())); + vk->layout_size_in_bytes(value_field_info.kind()), + vk->layout_alignment(value_field_info.kind())); assert(_super_alignment == -1 || _super_alignment >= vk->payload_alignment(), "Invalid value alignment"); _super_min_align_required = _super_min_align_required > vk->payload_alignment() ? _super_min_align_required : vk->payload_alignment(); } else { @@ -675,7 +675,7 @@ void FieldLayout::remove_null_marker() { ShouldNotReachHere(); // if we reach this point, the null marker was not found! } -void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super, Array* value_fields, bool dummy_field_is_reused_as_null_marker) { +void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super, Array* value_fields, bool dummy_field_is_reused_as_null_marker) { ResourceMark rm; LayoutRawBlock* b = _blocks; while(b != _last) { @@ -775,14 +775,14 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* loader_data, const InstanceKlass* super_klass, ConstantPool* constant_pool, GrowableArray* field_info, bool is_contended, bool is_concrete_value, bool is_abstract_value, - bool must_be_atomic, FieldLayoutInfo* info, Array* value_field_layout_info_array) : + bool must_be_atomic, FieldLayoutInfo* info, Array* value_field_info_array) : _classname(classname), _loader_data(loader_data), _super_klass(super_klass), _constant_pool(constant_pool), _field_info(field_info), _info(info), - _value_field_layout_info_array(value_field_layout_info_array), + _value_field_info_array(value_field_info_array), _root_group(nullptr), _contended_groups(GrowableArray(8)), _static_fields(nullptr), @@ -826,14 +826,14 @@ FieldGroup* FieldLayoutBuilder::get_or_create_contended_group(int g) { } void FieldLayoutBuilder::prologue() { - _layout = new FieldLayout(_field_info, _value_field_layout_info_array, _constant_pool); + _layout = new FieldLayout(_field_info, _value_field_info_array, _constant_pool); const InstanceKlass* super_klass = _super_klass; _layout->initialize_instance_layout(super_klass, _super_ends_with_oop); _nonstatic_oopmap_count = super_klass == nullptr ? 0 : super_klass->nonstatic_oop_map_count(); if (super_klass != nullptr) { _has_nonstatic_fields = super_klass->has_nonstatic_fields(); } - _static_layout = new FieldLayout(_field_info, _value_field_layout_info_array, _constant_pool); + _static_layout = new FieldLayout(_field_info, _value_field_info_array, _constant_pool); _static_layout->initialize_static_layout(); _static_fields = new FieldGroup(); _root_group = new FieldGroup(); @@ -859,9 +859,9 @@ int FieldLayoutBuilder::add_field_to_group(FieldInfo fieldinfo, int idx, FieldGr // Atomic flat fields can always be used in identity classes. // Use them only for value classes if the container is itself atomic. const bool use_atomic_flat = !is_value_compatible_class || _must_be_atomic; - LayoutKind lk = field_layout_selection(fieldinfo, _value_field_layout_info_array, use_atomic_flat); - lk = adjust_with_budget(fieldinfo, _value_field_layout_info_array, lk, _flattening_budget); - if (field_is_flattenable(fieldinfo, lk, _value_field_layout_info_array)) { + LayoutKind lk = field_layout_selection(fieldinfo, _value_field_info_array, use_atomic_flat); + lk = adjust_with_budget(fieldinfo, _value_field_info_array, lk, _flattening_budget); + if (field_is_flattenable(fieldinfo, lk, _value_field_info_array)) { _has_flattenable_fields = true; } @@ -878,15 +878,15 @@ int FieldLayoutBuilder::add_field_to_group(FieldInfo fieldinfo, int idx, FieldGr "Invalid layout kind for flat field: %s", LayoutKindHelper::layout_kind_as_string(lk)); const int field_index = (int)fieldinfo.index(); - assert(_value_field_layout_info_array != nullptr, "Array must have been created"); - assert(_value_field_layout_info_array->adr_at(field_index)->klass() != nullptr, "Klass must have been set"); + assert(_value_field_info_array != nullptr, "Array must have been created"); + assert(_value_field_info_array->adr_at(field_index)->klass() != nullptr, "Klass must have been set"); _has_flat_fields = true; - ValueKlass* vk = _value_field_layout_info_array->adr_at(field_index)->klass(); + ValueKlass* vk = _value_field_info_array->adr_at(field_index)->klass(); if (is_value_compatible_class && !vk->is_naturally_atomic(LayoutKindHelper::is_null_free_flat(lk))) { _has_non_naturally_atomic_fields = true; } group->add_flat_field(idx, vk, lk); - _value_field_layout_info_array->adr_at(field_index)->set_kind(lk); + _value_field_info_array->adr_at(field_index)->set_kind(lk); _nonstatic_oopmap_count += vk->nonstatic_oop_map_count(); _field_info->adr_at(idx)->field_flags_addr()->update_flat(true); _field_info->adr_at(idx)->set_layout_kind(lk); @@ -1603,9 +1603,9 @@ void FieldLayoutBuilder::epilogue() { } st.print_cr("Instance fields:"); const bool dummy_field_is_reused_as_null_marker = _is_empty_value_class && _null_marker_offset != -1; - _layout->print(&st, false, _super_klass, _value_field_layout_info_array, dummy_field_is_reused_as_null_marker); + _layout->print(&st, false, _super_klass, _value_field_info_array, dummy_field_is_reused_as_null_marker); st.print_cr("Static fields:"); - _static_layout->print(&st, true, nullptr, _value_field_layout_info_array, false); + _static_layout->print(&st, true, nullptr, _value_field_info_array, false); st.print_cr("Instance size = %d bytes", _info->_instance_size * wordSize); if (_is_concrete_value) { st.print_cr("First field offset = %d", _payload_offset); diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index 8bd33b43db5..e1594255515 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -185,7 +185,7 @@ class FieldGroup : public ResourceObj { class FieldLayout : public ResourceObj { private: GrowableArray* _field_info; - Array* _value_field_layout_info_array; + Array* _value_field_info_array; ConstantPool* _cp; LayoutRawBlock* _blocks; // the layout being computed LayoutRawBlock* _start; // points to the first block where a field can be inserted @@ -199,7 +199,7 @@ class FieldLayout : public ResourceObj { bool _has_inherited_fields; public: - FieldLayout(GrowableArray* field_info, Array* value_field_layout_info_array, ConstantPool* cp); + FieldLayout(GrowableArray* field_info, Array* value_field_info_array, ConstantPool* cp); void initialize_static_layout(); void initialize_instance_layout(const InstanceKlass* ik, bool& super_ends_with_oop); @@ -242,7 +242,7 @@ class FieldLayout : public ResourceObj { void shift_fields(int shift); LayoutRawBlock* find_null_marker(); void remove_null_marker(); - void print(outputStream* output, bool is_static, const InstanceKlass* super, Array* value_fields, bool dummy_field_is_reused_as_null_marker); + void print(outputStream* output, bool is_static, const InstanceKlass* super, Array* value_fields, bool dummy_field_is_reused_as_null_marker); }; @@ -278,7 +278,7 @@ class FieldLayoutBuilder : public ResourceObj { ConstantPool* _constant_pool; GrowableArray* _field_info; FieldLayoutInfo* _info; - Array* _value_field_layout_info_array; + Array* _value_field_info_array; FieldGroup* _root_group; GrowableArray _contended_groups; FieldGroup* _static_fields; @@ -316,7 +316,7 @@ class FieldLayoutBuilder : public ResourceObj { public: FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* loader_data, const InstanceKlass* super_klass, ConstantPool* constant_pool, GrowableArray* field_info, bool is_contended, bool is_concrete_value, bool is_abstract_value, - bool must_be_atomic, FieldLayoutInfo* info, Array* value_field_layout_info_array); + bool must_be_atomic, FieldLayoutInfo* info, Array* value_field_info_array); int payload_offset() const { assert(_payload_offset != -1, "Uninitialized"); return _payload_offset; } int payload_layout_size_in_bytes() const { return _payload_size_in_bytes; } diff --git a/src/hotspot/share/gc/shared/barrierSetRuntime.cpp b/src/hotspot/share/gc/shared/barrierSetRuntime.cpp index 173ab0eda4b..67d7455dd46 100644 --- a/src/hotspot/share/gc/shared/barrierSetRuntime.cpp +++ b/src/hotspot/share/gc/shared/barrierSetRuntime.cpp @@ -28,14 +28,14 @@ #include "runtime/interfaceSupport.inline.hpp" #include "utilities/macros.hpp" -JRT_LEAF(void, BarrierSetRuntime::value_copy(address src, address dst, ValueFieldLayoutInfo* li)) - ValuePayload src_payload = ValuePayload::construct_from_parts(src, li->klass(), li->kind()); - ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, li->klass(), li->kind()); +JRT_LEAF(void, BarrierSetRuntime::value_copy(address src, address dst, ValueFieldInfo* vfi)) + ValuePayload src_payload = ValuePayload::construct_from_parts(src, vfi->klass(), vfi->kind()); + ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, vfi->klass(), vfi->kind()); HeapAccess<>::value_copy(src_payload, dst_payload); JRT_END -JRT_LEAF(void, BarrierSetRuntime::value_copy_is_dest_uninitialized(address src, address dst, ValueFieldLayoutInfo* li)) - ValuePayload src_payload = ValuePayload::construct_from_parts(src, li->klass(), li->kind()); - ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, li->klass(), li->kind()); +JRT_LEAF(void, BarrierSetRuntime::value_copy_is_dest_uninitialized(address src, address dst, ValueFieldInfo* vfi)) + ValuePayload src_payload = ValuePayload::construct_from_parts(src, vfi->klass(), vfi->kind()); + ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, vfi->klass(), vfi->kind()); HeapAccess::value_copy(src_payload, dst_payload); JRT_END diff --git a/src/hotspot/share/gc/shared/barrierSetRuntime.hpp b/src/hotspot/share/gc/shared/barrierSetRuntime.hpp index 67061f8c158..14abbc4bd9b 100644 --- a/src/hotspot/share/gc/shared/barrierSetRuntime.hpp +++ b/src/hotspot/share/gc/shared/barrierSetRuntime.hpp @@ -32,8 +32,8 @@ class BarrierSetRuntime: public AllStatic { public: // Template interpreter... - static void value_copy(address src, address dst, ValueFieldLayoutInfo* li); - static void value_copy_is_dest_uninitialized(address src, address dst, ValueFieldLayoutInfo* li); + static void value_copy(address src, address dst, ValueFieldInfo* vfi); + static void value_copy_is_dest_uninitialized(address src, address dst, ValueFieldInfo* vfi); }; #endif // SHARE_GC_SHARED_BARRIERSETRUNTIME_HPP diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 7cc76f9f1d7..88728a13737 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -330,7 +330,7 @@ class MetaspaceObj { f(ConstantPoolCache) \ f(Annotations) \ f(MethodCounters) \ - f(ValueFieldLayoutInfo) \ + f(ValueFieldInfo) \ f(RecordComponent) \ f(KlassTrainingData) \ f(MethodTrainingData) \ diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index a5e6f2dee3f..705e04c4c56 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -156,16 +156,16 @@ #endif // ndef DTRACE_ENABLED -void ValueFieldLayoutInfo::metaspace_pointers_do(MetaspaceClosure* it) { - log_trace(cds)("Iter(ValueFieldLayoutInfo): %p", this); +void ValueFieldInfo::metaspace_pointers_do(MetaspaceClosure* it) { + log_trace(cds)("Iter(ValueFieldInfo): %p", this); it->push(&_klass); } -void ValueFieldLayoutInfo::print() const { +void ValueFieldInfo::print() const { print_on(tty); } -void ValueFieldLayoutInfo::print_on(outputStream* st) const { +void ValueFieldInfo::print_on(outputStream* st) const { st->print_cr("_klass: " PTR_FORMAT, p2i(_klass)); if (_klass != nullptr) { StreamIndentor si(st); @@ -239,7 +239,7 @@ int InstanceKlass::field_null_marker_offset(int index) const { return -1; } - const ValueKlass* const vk = value_field_layout_info(index).klass(); + const ValueKlass* const vk = value_field_info(index).klass(); const int nm_offset = vk->null_marker_offset_in_payload(); return field_offset(index) + nm_offset; } @@ -614,7 +614,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, KlassKind kind, mark _reference_type(reference_type), _acmp_maps_offset(0), _init_thread(nullptr), - _value_field_layout_info_array(nullptr), + _value_field_info_array(nullptr), _loadable_descriptors(nullptr), _acmp_maps_array(nullptr), _adr_value_klass_members(nullptr) @@ -782,10 +782,10 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { } set_fields_status(nullptr); - if (value_field_layout_info_array() != nullptr) { - MetadataFactory::free_array(loader_data, value_field_layout_info_array()); + if (value_field_info_array() != nullptr) { + MetadataFactory::free_array(loader_data, value_field_info_array()); } - set_value_field_layout_info_array(nullptr); + set_value_field_info_array(nullptr); // If a method from a redefined class is using this constant pool, don't // delete it, yet. The new class's previous version will point to this. @@ -2285,10 +2285,10 @@ bool InstanceKlass::find_local_flat_field_containing_offset(int offset, fieldDes } const int offset_in_flat_field = offset - fs.offset(); - const ValueFieldLayoutInfo layout_info = value_field_layout_info(fs.index()); - const int field_size = layout_info.klass()->layout_size_in_bytes(layout_info.kind()); + const ValueFieldInfo vfi = value_field_info(fs.index()); + const int field_size = vfi.klass()->layout_size_in_bytes(vfi.kind()); - assert(LayoutKindHelper::is_flat(layout_info.kind()), "Must be flat"); + assert(LayoutKindHelper::is_flat(vfi.kind()), "Must be flat"); if (offset_in_flat_field < field_size) { fd->reinitialize(const_cast(this), fs.to_FieldInfo()); @@ -3091,7 +3091,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_loadable_descriptors); it->push(&_acmp_maps_array, MetaspaceClosure::_writable); it->push(&_record_components); - it->push(&_value_field_layout_info_array, MetaspaceClosure::_writable); + it->push(&_value_field_info_array, MetaspaceClosure::_writable); if (CDSConfig::is_dumping_full_module_graph() && !defined_by_other_loaders()) { it->push(&_package_entry); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 2ca8a187dac..5f915ea87d4 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -127,14 +127,14 @@ class OopMapBlock { struct JvmtiCachedClassFileData; -class ValueFieldLayoutInfo : public MetaspaceObj { +class ValueFieldInfo : public MetaspaceObj { friend class VMStructs; ValueKlass* _klass; LayoutKind _kind; public: - ValueFieldLayoutInfo(): _klass(nullptr), _kind(LayoutKind::UNKNOWN) {} + ValueFieldInfo(): _klass(nullptr), _kind(LayoutKind::UNKNOWN) {} ValueKlass* klass() const { return _klass; } void set_klass(ValueKlass* k) { _klass = k; } @@ -146,9 +146,9 @@ class ValueFieldLayoutInfo : public MetaspaceObj { void set_kind(LayoutKind lk) { _kind = lk; } void metaspace_pointers_do(MetaspaceClosure* it); - MetaspaceObj::Type type() const { return ValueFieldLayoutInfoType; } + MetaspaceObj::Type type() const { return ValueFieldInfoType; } - static ByteSize klass_offset() { return byte_offset_of(ValueFieldLayoutInfo, _klass); } + static ByteSize klass_offset() { return byte_offset_of(ValueFieldInfo, _klass); } // Print void print() const; @@ -306,7 +306,7 @@ class InstanceKlass: public Klass { Array* _fieldinfo_search_table; Array* _fields_status; - Array* _value_field_layout_info_array; + Array* _value_field_info_array; Array* _loadable_descriptors; Array* _acmp_maps_array; // Metadata copy of the acmp_maps oop used in value classes. // When loading a value klass from the CDS/AOT archive @@ -972,7 +972,7 @@ class InstanceKlass: public Klass { JFR_ONLY(DEFINE_KLASS_TRACE_ID_OFFSET;) static ByteSize init_thread_offset() { return byte_offset_of(InstanceKlass, _init_thread); } - static ByteSize value_field_layout_info_array_offset() { return byte_offset_of(InstanceKlass, _value_field_layout_info_array); } + static ByteSize value_field_info_array_offset() { return byte_offset_of(InstanceKlass, _value_field_info_array); } static ByteSize adr_value_klass_members_offset() { return byte_offset_of(InstanceKlass, _adr_value_klass_members); } // subclass/subinterface checks @@ -1065,17 +1065,17 @@ class InstanceKlass: public Klass { // Sub-klasses can place their fields after this address. inline address end_of_instance_klass() const; - void set_value_field_layout_info_array(Array* array) { _value_field_layout_info_array = array; } - Array* value_field_layout_info_array() const { return _value_field_layout_info_array; } + void set_value_field_info_array(Array* array) { _value_field_info_array = array; } + Array* value_field_info_array() const { return _value_field_info_array; } - ValueFieldLayoutInfo value_field_layout_info(int index) const { - assert(_value_field_layout_info_array != nullptr, "Array not created"); - return _value_field_layout_info_array->at(index); + ValueFieldInfo value_field_info(int index) const { + assert(_value_field_info_array != nullptr, "Array not created"); + return _value_field_info_array->at(index); } - ValueFieldLayoutInfo* value_field_layout_info_adr(int index) { - assert(_value_field_layout_info_array != nullptr, "Array not created"); - return _value_field_layout_info_array->adr_at(index); + ValueFieldInfo* value_field_info_adr(int index) { + assert(_value_field_info_array != nullptr, "Array not created"); + return _value_field_info_array->adr_at(index); } inline ValueKlass* get_value_type_field_klass(int idx) const ; diff --git a/src/hotspot/share/oops/instanceKlass.inline.hpp b/src/hotspot/share/oops/instanceKlass.inline.hpp index 0fcc0daa053..c2aae907f3c 100644 --- a/src/hotspot/share/oops/instanceKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceKlass.inline.hpp @@ -71,7 +71,7 @@ inline address InstanceKlass::end_of_instance_klass() const { inline ValueKlass* InstanceKlass::get_value_type_field_klass(int idx) const { assert(has_flat_fields() || has_null_restricted_static_fields(), "Sanity checking"); assert(idx < java_fields_count(), "IOOB"); - ValueKlass* k = value_field_layout_info(idx).klass(); + ValueKlass* k = value_field_info(idx).klass(); assert(k != nullptr, "Should always be set before being read"); return k; } @@ -79,7 +79,7 @@ inline ValueKlass* InstanceKlass::get_value_type_field_klass(int idx) const { inline ValueKlass* InstanceKlass::get_value_type_field_klass_or_null(int idx) const { assert(has_flat_fields() || has_null_restricted_static_fields(), "Sanity checking"); assert(idx < java_fields_count(), "IOOB"); - ValueKlass* k = value_field_layout_info(idx).klass(); + ValueKlass* k = value_field_info(idx).klass(); return k; } diff --git a/src/hotspot/share/oops/valuePayload.hpp b/src/hotspot/share/oops/valuePayload.hpp index 09fb3702226..545c8ae84c2 100644 --- a/src/hotspot/share/oops/valuePayload.hpp +++ b/src/hotspot/share/oops/valuePayload.hpp @@ -226,7 +226,7 @@ class FlatFieldPayload : public FlatValuePayload { inline FlatFieldPayload(instanceOop container, ptrdiff_t offset, - ValueFieldLayoutInfo* layout_info); + ValueFieldInfo* value_field_info); inline void assert_post_construction_invariants(instanceOop container, ResolvedFieldEntry* resolved_field_entry) const NOT_DEBUG_RETURN; diff --git a/src/hotspot/share/oops/valuePayload.inline.hpp b/src/hotspot/share/oops/valuePayload.inline.hpp index 74c3090add7..f0bd29b06bc 100644 --- a/src/hotspot/share/oops/valuePayload.inline.hpp +++ b/src/hotspot/share/oops/valuePayload.inline.hpp @@ -276,29 +276,29 @@ inline void ValuePayload::assert_is_flat_field(const InstanceKlass* klass, int o fieldDescriptor field_descriptor; postcond(klass->find_flat_field_containing_offset(offset, &field_descriptor)); - const ValueFieldLayoutInfo value_field_layout_info = field_descriptor.field_holder()->value_field_layout_info(field_descriptor.index()); + const ValueFieldInfo value_field_info = field_descriptor.field_holder()->value_field_info(field_descriptor.index()); - OnVMError on_assertion_failure_value_field_layout_info([&](outputStream* st) { + OnVMError on_assertion_failure_value_field_info([&](outputStream* st) { st->print_cr("=== assert_is_flat_field(" PTR_FORMAT ", %d) failure ===", p2i(klass), offset); StreamIndentor si(st); st->print("field_descriptor: "); field_descriptor.print_on(st); st->cr(); - st->print("value_field_layout_info: "); - value_field_layout_info.print_on(st); + st->print("value_field_info: "); + value_field_info.print_on(st); st->cr(); }); - if (value_field_layout_info.klass() == this->klass()) { + if (value_field_info.klass() == this->klass()) { // Found the field in klass postcond(offset == field_descriptor.offset()); - postcond(value_field_layout_info.kind() == layout_kind()); + postcond(value_field_info.kind() == layout_kind()); postcond(field_descriptor.layout_kind() == layout_kind()); postcond(field_descriptor.is_flat()); } else { // Nested flat field postcond(offset >= field_descriptor.offset()); - const ValueKlass* const field_klass = value_field_layout_info.klass(); + const ValueKlass* const field_klass = value_field_info.klass(); const int payload_offset = field_klass->payload_offset(); assert_is_flat_field(field_klass, offset - field_descriptor.offset() + payload_offset); } @@ -574,8 +574,8 @@ inline FlatFieldPayload::FlatFieldPayload(instanceOop container, inline FlatFieldPayload::FlatFieldPayload(instanceOop container, ptrdiff_t offset, - ValueFieldLayoutInfo* layout_info) - : FlatValuePayload(container, offset, layout_info->klass(), layout_info->kind()) {} + ValueFieldInfo* value_field_info) + : FlatValuePayload(container, offset, value_field_info->klass(), value_field_info->kind()) {} #ifdef ASSERT @@ -611,7 +611,7 @@ inline FlatFieldPayload::FlatFieldPayload(instanceOop container, fieldDescriptor* field_descriptor) : FlatFieldPayload(container, field_descriptor->offset(), - field_descriptor->field_holder()->value_field_layout_info_adr(field_descriptor->index())) { + field_descriptor->field_holder()->value_field_info_adr(field_descriptor->index())) { assert_post_construction_invariants(container, field_descriptor); } @@ -619,7 +619,7 @@ inline FlatFieldPayload::FlatFieldPayload(instanceOop container, ResolvedFieldEntry* resolved_field_entry) : FlatFieldPayload(container, resolved_field_entry->field_offset(), - resolved_field_entry->field_holder()->value_field_layout_info_adr(resolved_field_entry->field_index())) { + resolved_field_entry->field_holder()->value_field_info_adr(resolved_field_entry->field_index())) { assert_post_construction_invariants(container, resolved_field_entry); } diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp index e111aed7130..fbf959b4c63 100644 --- a/src/hotspot/share/prims/jvmtiTagMap.cpp +++ b/src/hotspot/share/prims/jvmtiTagMap.cpp @@ -592,9 +592,9 @@ class ClassFieldDescriptor: public CHeapObj { if (fld.is_flat()) { const fieldDescriptor& fd = fld.field_descriptor(); InstanceKlass* holder_klass = fd.field_holder(); - ValueFieldLayoutInfo* layout_info = holder_klass->value_field_layout_info_adr(fd.index()); - _value_klass = layout_info->klass(); - _layout_kind = layout_info->kind(); + ValueFieldInfo* vfi = holder_klass->value_field_info_adr(fd.index()); + _value_klass = vfi->klass(); + _layout_kind = vfi->kind(); } else { _value_klass = nullptr; _layout_kind = LayoutKind::REFERENCE; diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index d5cd392fe3a..6d44b1ab9c3 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -336,7 +336,7 @@ UNSAFE_ENTRY(jint, Unsafe_FieldLayout(JNIEnv *env, jobject unsafe, jobject field } else { InstanceKlass* ik = InstanceKlass::cast(k); if (ik->field_is_flat(slot)) { - return (jint)ik->value_field_layout_info(slot).kind(); + return (jint)ik->value_field_info(slot).kind(); } else { return (jint)LayoutKind::REFERENCE; } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 5cbf812818a..68f61f08098 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -191,7 +191,7 @@ nonstatic_field(ResolvedMethodEntry, _cpool_index, u2) \ nonstatic_field(ConstantPoolCache, _resolved_indy_entries, Array*) \ nonstatic_field(ResolvedIndyEntry, _cpool_index, u2) \ - nonstatic_field(ValueFieldLayoutInfo, _klass, ValueKlass*) \ + nonstatic_field(ValueFieldInfo, _klass, ValueKlass*) \ volatile_nonstatic_field(InstanceKlass, _array_klasses, ObjArrayKlass*) \ nonstatic_field(InstanceKlass, _methods, Array*) \ nonstatic_field(InstanceKlass, _default_methods, Array*) \ @@ -221,7 +221,7 @@ nonstatic_field(InstanceKlass, _method_ordering, Array*) \ nonstatic_field(InstanceKlass, _default_vtable_indices, Array*) \ nonstatic_field(InstanceKlass, _access_flags, AccessFlags) \ - nonstatic_field(InstanceKlass, _value_field_layout_info_array, Array*) \ + nonstatic_field(InstanceKlass, _value_field_info_array, Array*) \ nonstatic_field(InstanceKlass, _adr_value_klass_members, address) \ nonstatic_field(ValueKlass::Members, _payload_offset, int) \ nonstatic_field(ValueKlass::Members, _null_marker_offset, int) \ @@ -734,7 +734,7 @@ unchecked_nonstatic_field(Array,_data, sizeof(ResolvedMethodEntry)) \ unchecked_nonstatic_field(Array, _data, sizeof(ResolvedIndyEntry)) \ unchecked_nonstatic_field(Array*>, _data, sizeof(Array*)) \ - unchecked_nonstatic_field(Array,_data, sizeof(ValueFieldLayoutInfo)) \ + unchecked_nonstatic_field(Array, _data, sizeof(ValueFieldInfo)) \ \ /*********************************/ \ /* java_lang_Class fields */ \ @@ -953,7 +953,7 @@ declare_type(MethodCounters, MetaspaceObj) \ declare_type(ConstMethod, MetaspaceObj) \ declare_type(Annotations, MetaspaceObj) \ - declare_type(ValueFieldLayoutInfo, MetaspaceObj) \ + declare_type(ValueFieldInfo, MetaspaceObj) \ \ declare_toplevel_type(ValueKlass::Members) \ \ @@ -1216,7 +1216,7 @@ declare_type(Array, MetaspaceObj) \ declare_type(Array, MetaspaceObj) \ declare_type(Array*>, MetaspaceObj) \ - declare_type(Array, MetaspaceObj) \ + declare_type(Array, MetaspaceObj) \ \ declare_toplevel_type(BitMap) \ declare_type(BitMapView, BitMap) \ diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index b00d837e1ab..480b4323ac2 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -854,9 +854,9 @@ class DumperClassCacheTableEntry : public CHeapObj { if (field.is_flat()) { const fieldDescriptor& fd = field.field_descriptor(); InstanceKlass* holder_klass = fd.field_holder(); - ValueFieldLayoutInfo* layout_info = holder_klass->value_field_layout_info_adr(fd.index()); - _value_klass = layout_info->klass(); - _layout_kind = layout_info->kind(); + ValueFieldInfo* vfi = holder_klass->value_field_info_adr(fd.index()); + _value_klass = vfi->klass(); + _layout_kind = vfi->kind(); } else { _value_klass = nullptr; _layout_kind = LayoutKind::REFERENCE; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index d95928e1712..6b344a5e98b 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -106,7 +106,7 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeExc } headerSize = type.getSize(); accessFlags = new CIntField(type.getCIntegerField("_access_flags"), 0); - valueFieldLayoutInfoArray = type.getAddressField("_value_field_layout_info_array"); + valueFieldInfoArray = type.getAddressField("_value_field_info_array"); adrValueKlassMembers = type.getAddressField("_adr_value_klass_members"); // read internal field flags constants @@ -175,7 +175,7 @@ public InstanceKlass(Address addr) { private static CIntField nestHostIndex; private static CIntField accessFlags; private static AddressField breakpoints; - private static AddressField valueFieldLayoutInfoArray; + private static AddressField valueFieldInfoArray; private static AddressField adrValueKlassMembers; // type safe enum for ClassState from instanceKlass.hpp @@ -899,9 +899,9 @@ public Address getAdrValueKlassMembers() { return getAddress().getAddressAt(adrValueKlassMembers.getOffset()); } - public ValueFieldLayoutInfoArray getValueFieldLayoutInfoArray() { - Address addr = valueFieldLayoutInfoArray.getValue(getAddress()); - return VMObjectFactory.newObject(ValueFieldLayoutInfoArray.class, addr); + public ValueFieldInfoArray getValueFieldInfoArray() { + Address addr = valueFieldInfoArray.getValue(getAddress()); + return VMObjectFactory.newObject(ValueFieldInfoArray.class, addr); } //---------------------------------------------------------------------- diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java index 7bcb218e1ae..9eb1a62f753 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopField.java @@ -49,7 +49,7 @@ public Oop getValue(Oop obj) { } var heap = obj.getHeap(); if (isFlat()) { - var layout = ((InstanceKlass)obj.getKlass()).getValueFieldLayoutInfoArray().at(getFieldIndex()); + var layout = ((InstanceKlass)obj.getKlass()).getValueFieldInfoArray().at(getFieldIndex()); ValueKlass vk = layout.getKlass(); // OopHandle does not allow to call addOffsetTo() due to prevent interior diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldInfo.java similarity index 91% rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldInfo.java index 11817f3d0b9..7e1a713fb28 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldLayoutInfo.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ValueFieldInfo.java @@ -32,7 +32,7 @@ import sun.jvm.hotspot.types.WrongTypeException; -public class ValueFieldLayoutInfo extends VMObject { +public class ValueFieldInfo extends VMObject { private static MetadataField klassField; @@ -41,12 +41,12 @@ public class ValueFieldLayoutInfo extends VMObject { } private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { - Type type = db.lookupType("ValueFieldLayoutInfo"); + Type type = db.lookupType("ValueFieldInfo"); klassField = new MetadataField(type.getAddressField("_klass"), 0); } - public ValueFieldLayoutInfo(Address addr) { + public ValueFieldInfo(Address addr) { super(addr); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldInfoArray.java similarity index 84% rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldInfoArray.java index e9053febcaf..d7f2ef381be 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldLayoutInfoArray.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ValueFieldInfoArray.java @@ -25,7 +25,7 @@ package sun.jvm.hotspot.utilities; import sun.jvm.hotspot.debugger.Address; -import sun.jvm.hotspot.oops.ValueFieldLayoutInfo; +import sun.jvm.hotspot.oops.ValueFieldInfo; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.runtime.VMObjectFactory; import sun.jvm.hotspot.types.Type; @@ -33,7 +33,7 @@ import sun.jvm.hotspot.types.WrongTypeException; -public class ValueFieldLayoutInfoArray extends GenericArray { +public class ValueFieldInfoArray extends GenericArray { private static long dataFieldOffset; private static Type elemType; @@ -43,22 +43,22 @@ public class ValueFieldLayoutInfoArray extends GenericArray { } private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { - elemType = db.lookupType("ValueFieldLayoutInfo"); - Type type = db.lookupType("Array"); + elemType = db.lookupType("ValueFieldInfo"); + Type type = db.lookupType("Array"); dataFieldOffset = type.getAddressField("_data").getOffset(); } - public ValueFieldLayoutInfoArray(Address addr) { + public ValueFieldInfoArray(Address addr) { super(addr, dataFieldOffset); } - public ValueFieldLayoutInfo at(int i) { + public ValueFieldInfo at(int i) { if (i < 0 || i >= length()) { throw new ArrayIndexOutOfBoundsException(i); } var addr = getDataStart().addOffsetTo(i * getElemType().getSize()); - return new ValueFieldLayoutInfo(addr); + return new ValueFieldInfo(addr); } @Override From 3cf02e20650e15b6334862081abf183b0d3abe79 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Wed, 7 Oct 2026 07:49:56 +0000 Subject: [PATCH 78/98] 8384407: AArch64: Replace DMB + ST + DMB with STLR for C1 volatile field stores Reviewed-by: aph, smonteith --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 311 +++++++++++------- .../cpu/aarch64/c1_LIRAssembler_aarch64.hpp | 12 +- .../cpu/aarch64/templateTable_aarch64.cpp | 24 +- src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp | 6 +- src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp | 7 + .../cpu/riscv/c1_LIRGenerator_riscv.cpp | 2 + src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp | 2 + src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp | 2 + src/hotspot/share/c1/c1_LIRGenerator.hpp | 2 +- .../share/gc/shared/c1/barrierSetC1.cpp | 48 ++- src/hotspot/share/oops/accessDecorators.hpp | 3 +- .../gcbarriers/TestImplicitNullChecks.java | 41 ++- .../membars/DekkerTest_Interpreter_C1.java | 169 ++++++++++ 13 files changed, 457 insertions(+), 172 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/membars/DekkerTest_Interpreter_C1.java diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 72cd9e5c4df..745a3f58a7f 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -135,6 +135,31 @@ void LIR_Assembler::push(LIR_Opr opr) { Unimplemented(); } void LIR_Assembler::pop(LIR_Opr opr) { Unimplemented(); } bool LIR_Assembler::is_literal_address(LIR_Address* addr) { Unimplemented(); return false; } + +bool LIR_Assembler::is_null_or_non_fp_zero_constant(BasicType type, LIR_Opr opr) { + if (!opr->is_constant()) { + return false; + } + + LIR_Const* c = opr->as_constant_ptr(); + + switch (type) { + case T_ADDRESS: // fall through + case T_INT: // fall through + case T_CHAR: // fall through + case T_SHORT: // fall through + case T_BOOLEAN: // fall through + case T_BYTE: + return (c->as_jint() == 0); + case T_LONG: + return (c->as_jlong() == 0); + case T_OBJECT: // fall through + case T_ARRAY: + return (c->as_jobject() == nullptr); + default: + return false; + } +} //------------------------------------------- static Register as_reg(LIR_Opr op) { @@ -668,51 +693,20 @@ void LIR_Assembler::const2stack(LIR_Opr src, LIR_Opr dest) { } void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info, bool wide) { - assert(src->is_constant(), "should not call otherwise"); - LIR_Const* c = src->as_constant_ptr(); - LIR_Address* to_addr = dest->as_address_ptr(); + const2mem(src, dest, type, info, wide, /*is_volatile*/false); +} - void (Assembler::* insn)(Register Rt, const Address &adr); +void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info, bool wide, bool is_volatile) { + assert(is_null_or_non_fp_zero_constant(type, src), "should be"); + LIR_Address* to_addr = dest->as_address_ptr(); + Address addr = as_Address(to_addr, rscratch1); - switch (type) { - case T_ADDRESS: - assert(c->as_jint() == 0, "should be"); - insn = &Assembler::str; - break; - case T_LONG: - assert(c->as_jlong() == 0, "should be"); - insn = &Assembler::str; - break; - case T_INT: - assert(c->as_jint() == 0, "should be"); - insn = &Assembler::strw; - break; - case T_OBJECT: - case T_ARRAY: - assert(c->as_jobject() == nullptr, "should be"); - if (UseCompressedOops && !wide) { - insn = &Assembler::strw; - } else { - insn = &Assembler::str; - } - break; - case T_CHAR: - case T_SHORT: - assert(c->as_jint() == 0, "should be"); - insn = &Assembler::strh; - break; - case T_BOOLEAN: - case T_BYTE: - assert(c->as_jint() == 0, "should be"); - insn = &Assembler::strb; - break; - default: - ShouldNotReachHere(); - insn = &Assembler::str; // unreachable + if (is_volatile) { + assert(!wide, "unexpected for volatile_move_op"); + store_volatile(addr, src, type, info); + } else { + store_unordered(addr, src, type, wide, info); } - - if (info) add_debug_info_for_null_check_here(info); - (_masm->*insn)(zr, as_Address(to_addr, rscratch1)); } void LIR_Assembler::reg2reg(LIR_Opr src, LIR_Opr dest) { @@ -796,9 +790,12 @@ void LIR_Assembler::reg2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { void LIR_Assembler::reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide) { + reg2mem(src, dest, type, patch_code, info, wide, /*is_volatile*/false); +} + +void LIR_Assembler::reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide, bool is_volatile) { LIR_Address* to_addr = dest->as_address_ptr(); - PatchingStub* patch = nullptr; - Register compressed_src = rscratch1; + Address addr = as_Address(to_addr, rscratch1); if (patch_code != lir_patch_none) { deoptimize_trap(info); @@ -807,70 +804,22 @@ void LIR_Assembler::reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_Patch if (is_reference_type(type)) { __ verify_oop(src->as_register()); - - if (UseCompressedOops && !wide) { - __ encode_heap_oop(compressed_src, src->as_register()); - } else { - compressed_src = src->as_register(); - } } - int null_check_here = code_offset(); - switch (type) { - case T_FLOAT: { - __ strs(src->as_float_reg(), as_Address(to_addr)); - break; - } - - case T_DOUBLE: { - __ strd(src->as_double_reg(), as_Address(to_addr)); - break; - } - - case T_ARRAY: // fall through - case T_OBJECT: // fall through - if (UseCompressedOops && !wide) { - __ strw(compressed_src, as_Address(to_addr, rscratch2)); - } else { - __ str(compressed_src, as_Address(to_addr)); - } - break; - case T_METADATA: - // We get here to store a method pointer to the stack to pass to - // a dtrace runtime call. This can't work on 64 bit with - // compressed klass ptrs: T_METADATA can be a compressed klass - // ptr or a 64 bit method pointer. - ShouldNotReachHere(); - __ str(src->as_register(), as_Address(to_addr)); - break; - case T_ADDRESS: - __ str(src->as_register(), as_Address(to_addr)); - break; - case T_INT: - __ strw(src->as_register(), as_Address(to_addr)); - break; - - case T_LONG: { - __ str(src->as_register_lo(), as_Address_lo(to_addr)); - break; - } - - case T_BYTE: // fall through - case T_BOOLEAN: { - __ strb(src->as_register(), as_Address(to_addr)); - break; - } - - case T_CHAR: // fall through - case T_SHORT: - __ strh(src->as_register(), as_Address(to_addr)); - break; - - default: - ShouldNotReachHere(); + LIR_Opr src_maybe_compressed_oop; + if (is_reference_type(type) && UseCompressedOops && !wide) { + // Use rscratch2 because rscratch1 might be used for address lowering + __ encode_heap_oop(rscratch2, src->as_register()); + src_maybe_compressed_oop = FrameMap::rscratch2_opr; + } else { + src_maybe_compressed_oop = src; } - if (info != nullptr) { - add_debug_info_for_null_check(null_check_here, info); + + if (is_volatile) { + assert(!wide, "unexpected for volatile_move_op"); + store_volatile(addr, src_maybe_compressed_oop, type, info); + } else { + store_unordered(addr, src_maybe_compressed_oop, type, wide, info); } } @@ -960,11 +909,10 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide, bool is_volatile) { - LIR_Address* addr = src->as_address_ptr(); LIR_Address* from_addr = src->as_address_ptr(); - if (addr->base()->type() == T_OBJECT) { - __ verify_oop(addr->base()->as_pointer_register()); + if (from_addr->base()->type() == T_OBJECT) { + __ verify_oop(from_addr->base()->as_pointer_register()); } if (patch_code != lir_patch_none) { @@ -972,10 +920,12 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, return; } + Address addr = as_Address(from_addr); + if (is_volatile) { - load_volatile(from_addr, dest, type, info); + load_volatile(addr, dest, type, info); } else { - load_unordered(from_addr, dest, type, wide, info); + load_unordered(addr, dest, type, wide, info); } if (is_reference_type(type)) { @@ -987,7 +937,7 @@ void LIR_Assembler::mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, } } -void LIR_Assembler::load_unordered(LIR_Address *from_addr, LIR_Opr dest, +void LIR_Assembler::load_unordered(Address addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info) { if (info != nullptr) { add_debug_info_for_null_check_here(info); @@ -995,21 +945,21 @@ void LIR_Assembler::load_unordered(LIR_Address *from_addr, LIR_Opr dest, switch (type) { case T_FLOAT: { - __ ldrs(dest->as_float_reg(), as_Address(from_addr)); + __ ldrs(dest->as_float_reg(), addr); break; } case T_DOUBLE: { - __ ldrd(dest->as_double_reg(), as_Address(from_addr)); + __ ldrd(dest->as_double_reg(), addr); break; } case T_ARRAY: // fall through case T_OBJECT: // fall through if (UseCompressedOops && !wide) { - __ ldrw(dest->as_register(), as_Address(from_addr)); + __ ldrw(dest->as_register(), addr); } else { - __ ldr(dest->as_register(), as_Address(from_addr)); + __ ldr(dest->as_register(), addr); } break; case T_METADATA: @@ -1018,33 +968,33 @@ void LIR_Assembler::load_unordered(LIR_Address *from_addr, LIR_Opr dest, // compressed klass ptrs: T_METADATA can be a compressed klass // ptr or a 64 bit method pointer. ShouldNotReachHere(); - __ ldr(dest->as_register(), as_Address(from_addr)); + __ ldr(dest->as_register(), addr); break; case T_ADDRESS: - __ ldr(dest->as_register(), as_Address(from_addr)); + __ ldr(dest->as_register(), addr); break; case T_INT: - __ ldrw(dest->as_register(), as_Address(from_addr)); + __ ldrw(dest->as_register(), addr); break; case T_LONG: { - __ ldr(dest->as_register_lo(), as_Address_lo(from_addr)); + __ ldr(dest->as_register_lo(), addr); break; } case T_BYTE: - __ ldrsb(dest->as_register(), as_Address(from_addr)); + __ ldrsb(dest->as_register(), addr); break; case T_BOOLEAN: { - __ ldrb(dest->as_register(), as_Address(from_addr)); + __ ldrb(dest->as_register(), addr); break; } case T_CHAR: - __ ldrh(dest->as_register(), as_Address(from_addr)); + __ ldrh(dest->as_register(), addr); break; case T_SHORT: - __ ldrsh(dest->as_register(), as_Address(from_addr)); + __ ldrsh(dest->as_register(), addr); break; default: @@ -1067,9 +1017,69 @@ void LIR_Assembler::move(LIR_Opr src, LIR_Opr dst) { } } -void LIR_Assembler::load_volatile(LIR_Address *from_addr, LIR_Opr dest, +void LIR_Assembler::store_unordered(Address addr, LIR_Opr src, + BasicType type, bool wide, CodeEmitInfo* info) { + if (info != nullptr) { + add_debug_info_for_null_check_here(info); + } + + if (type == T_FLOAT) { + __ strs(src->as_float_reg(), addr); + } else if (type == T_DOUBLE) { + __ strd(src->as_double_reg(), addr); + } else { + Register src_reg; + if (is_null_or_non_fp_zero_constant(type, src)) { + src_reg = zr; + } else if (type == T_LONG) { + src_reg = src->as_register_lo(); + } else { + src_reg = src->as_register(); + } + + switch (type) { + case T_ARRAY: // fall through + case T_OBJECT: + if (UseCompressedOops && !wide) { + __ strw(src_reg, addr); + } else { + __ str(src_reg, addr); + } + break; + case T_METADATA: + // We get here to store a method pointer to the stack to pass to + // a dtrace runtime call. This can't work on 64 bit with + // compressed klass ptrs: T_METADATA can be a compressed klass + // ptr or a 64 bit method pointer. + ShouldNotReachHere(); + case T_ADDRESS: // fall through + case T_LONG: // fall through + case T_INT: // fall through + case T_BYTE: // fall through + case T_CHAR: // fall through + case T_SHORT: // fall through + case T_BOOLEAN: { + __ store(src_reg, addr, type); + break; + } + + default: + ShouldNotReachHere(); + } + } +} + +void LIR_Assembler::load_volatile(Address addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info) { - __ lea(rscratch1, as_Address(from_addr)); + Register addr_reg; + + if (addr.getMode() == Address::base_plus_offset + && addr.offset() == 0) { + addr_reg = addr.base(); + } else { + addr_reg = rscratch1; + __ lea(addr_reg, addr); + } Register dest_reg = rscratch2; if (!is_floating_point_type(type)) { @@ -1082,7 +1092,7 @@ void LIR_Assembler::load_volatile(LIR_Address *from_addr, LIR_Opr dest, } // Uses LDAR to ensure memory ordering. - __ load_store_volatile(dest_reg, type, rscratch1, /*is_load*/true); + __ load_store_volatile(dest_reg, type, addr_reg, /*is_load*/true); switch (type) { // LDAR is unsigned so need to sign-extend for byte and short @@ -1104,6 +1114,45 @@ void LIR_Assembler::load_volatile(LIR_Address *from_addr, LIR_Opr dest, } } +void LIR_Assembler::store_volatile(Address addr, LIR_Opr src, + BasicType type, CodeEmitInfo* info) { + Register addr_reg; + Register src_reg; + + if (is_null_or_non_fp_zero_constant(type, src)) { + src_reg = zr; + } else { + // Need to move from FPR to GPR before STLR with FMOV for floating types + if (type == T_FLOAT) { + src_reg = rscratch2; + __ fmovs(src_reg, src->as_float_reg()); + } else if (type == T_DOUBLE) { + src_reg = rscratch2; + __ fmovd(src_reg, src->as_double_reg()); + } else if (type == T_LONG) { + src_reg = src->as_register_lo(); + } else { + src_reg = src->as_register(); + } + assert(src_reg != rscratch1, "rscratch1 is reserved for the address"); + } + + if (addr.getMode() == Address::base_plus_offset + && addr.offset() == 0) { + addr_reg = addr.base(); + } else { + addr_reg = rscratch1; + __ lea(addr_reg, addr); + } + + if (info != nullptr) { + add_debug_info_for_null_check_here(info); + } + + // Uses STLR to ensure memory ordering. + __ load_store_volatile(src_reg, type, addr_reg, /*is_load*/false); +} + int LIR_Assembler::array_element_size(BasicType type) const { int elem_size = type2aelembytes(type); return exact_log2(elem_size); @@ -3016,8 +3065,16 @@ void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type, if (src->is_address()) { mem2reg(src, dest, type, lir_patch_none, info, /*wide*/false, /*is_volatile*/true); } else if (dest->is_address()) { - move_op(src, dest, type, lir_patch_none, info, /*wide*/false); + if (src->is_register()) { + reg2mem(src, dest, type, lir_patch_none, info, /*wide*/false, /*is_volatile*/true); + } else if (src->is_constant()) { + const2mem(src, dest, type, info, /*wide*/false, /*is_volatile*/true); + } else { + // Volatile operations should involve memory and can't involve stack. + ShouldNotReachHere(); + } } else { + // Volatile operations should involve memory and can't involve stack. ShouldNotReachHere(); } } diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 699b8129a47..c7fbe3d0611 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -42,6 +42,7 @@ friend class ArrayCopyStub; address int_constant(jlong n); bool is_literal_address(LIR_Address* addr); + bool is_null_or_non_fp_zero_constant(BasicType type, LIR_Opr opr); // When we need to use something other than rscratch1 use this method. Address as_Address(LIR_Address* addr, Register tmp); @@ -59,8 +60,15 @@ friend class ArrayCopyStub; void mem2reg(LIR_Opr src, LIR_Opr dest, BasicType type, LIR_PatchCode patch_code, CodeEmitInfo* info, bool wide, bool is_volatile); - void load_unordered(LIR_Address *from_addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info); - void load_volatile(LIR_Address *from_addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info); + void reg2mem(LIR_Opr src, LIR_Opr dest, BasicType type, + LIR_PatchCode patch_code, CodeEmitInfo* info, + bool wide, bool is_volatile); + void const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, + CodeEmitInfo* info, bool wide, bool is_volatile); + void load_unordered(Address addr, LIR_Opr dest, BasicType type, bool wide, CodeEmitInfo* info); + void store_unordered(Address addr, LIR_Opr src, BasicType type, bool wide, CodeEmitInfo* info); + void load_volatile(Address addr, LIR_Opr dest, BasicType type, CodeEmitInfo* info); + void store_volatile(Address addr, LIR_Opr src, BasicType type, CodeEmitInfo* info); static const int max_tableswitches = 20; struct tableswitch switches[max_tableswitches]; diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 9ca01c6d990..836c1b55892 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -2724,9 +2724,9 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr // volatile accesses forms a sequentially-consistent set of // operations when combined with STLR and LDAR. Without a leading // membar it's possible for a simple Dekker test to fail if loads - // use LDR;DMB but stores use STLR. This can happen if C2 compiles + // use LDR;DMB but stores use STLR. This can happen if C1 or C2 compiles // the stores in one method and we interpret the loads in another. - if (!CompilerConfig::is_c1_or_interpreter_only()){ + { Label notVolatile; __ tbz(flags, ResolvedFieldEntry::is_volatile_shift, notVolatile); __ membar(MacroAssembler::AnyAny); @@ -2886,10 +2886,12 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static, RewriteContr __ bind(Done); - Label notVolatile; - __ tbz(flags, ResolvedFieldEntry::is_volatile_shift, notVolatile); - __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); - __ bind(notVolatile); + { + Label notVolatile; + __ tbz(flags, ResolvedFieldEntry::is_volatile_shift, notVolatile); + __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); + __ bind(notVolatile); + } } @@ -3275,8 +3277,6 @@ void TemplateTable::fast_storefield(TosState state) __ bind(notVolatile); } - Label notVolatile; - // Get object from stack pop_and_check_object(r2); @@ -3385,9 +3385,9 @@ void TemplateTable::fast_accessfield(TosState state) // volatile accesses forms a sequentially-consistent set of // operations when combined with STLR and LDAR. Without a leading // membar it's possible for a simple Dekker test to fail if loads - // use LDR;DMB but stores use STLR. This can happen if C2 compiles + // use LDR;DMB but stores use STLR. This can happen if C1 or C2 compiles // the stores in one method and we interpret the loads in another. - if (!CompilerConfig::is_c1_or_interpreter_only()) { + { Label notVolatile; __ tbz(r3, ResolvedFieldEntry::is_volatile_shift, notVolatile); __ membar(MacroAssembler::AnyAny); @@ -3455,9 +3455,9 @@ void TemplateTable::fast_xaccess(TosState state) // volatile accesses forms a sequentially-consistent set of // operations when combined with STLR and LDAR. Without a leading // membar it's possible for a simple Dekker test to fail if loads - // use LDR;DMB but stores use STLR. This can happen if C2 compiles + // use LDR;DMB but stores use STLR. This can happen if C1 or C2 compiles // the stores in one method and we interpret the loads in another. - if (!CompilerConfig::is_c1_or_interpreter_only()) { + { Label notVolatile; __ load_unsigned_byte(r3, Address(r2, in_bytes(ResolvedFieldEntry::flags_offset()))); __ tbz(r3, ResolvedFieldEntry::is_volatile_shift, notVolatile); diff --git a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp index 555e7016f9b..7da13da1bf1 100644 --- a/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRGenerator_arm.cpp @@ -1302,6 +1302,7 @@ void LIRGenerator::trace_block_entry(BlockBegin* block) { void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info) { + __ membar_release(); if (value->is_double_cpu()) { assert(address->index()->is_illegal(), "should have a constant displacement"); LIR_Address* store_addr = nullptr; @@ -1314,9 +1315,10 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, store_addr = address; } __ volatile_store_mem_reg(value, store_addr, info); - return; + } else { + __ store(value, address, info, lir_patch_none); } - __ store(value, address, info, lir_patch_none); + __ membar(); } void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index c16b714d00e..0dcc19004cc 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -1140,16 +1140,23 @@ void LIRGenerator::trace_block_entry(BlockBegin* block) { void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info) { + __ membar_release(); #ifdef _LP64 __ store(value, address, info); #else Unimplemented(); // __ volatile_store_mem_reg(value, address, info); #endif + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ membar(); + } } void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ membar(); + } #ifdef _LP64 __ load(address, result, info); #else diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index 43b9fc7b59a..174b93ce06e 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -1253,7 +1253,9 @@ void LIRGenerator::trace_block_entry(BlockBegin* block) { Unimplemented(); } void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info) { + __ membar_release(); __ volatile_store_mem_reg(value, address, info); + __ membar(); } void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, diff --git a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp index 45da8d6da31..4f3c9f860bc 100644 --- a/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRGenerator_s390.cpp @@ -1055,7 +1055,9 @@ void LIRGenerator::trace_block_entry(BlockBegin* block) { void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info) { + __ membar_release(); __ store(value, address, info); + __ membar(); } void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 60c0a94510a..b2efdfd17ff 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -1413,6 +1413,7 @@ void LIRGenerator::trace_block_entry(BlockBegin* block) { void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info) { + __ membar_release(); if (address->type() == T_LONG) { address = new LIR_Address(address->base(), address->index(), address->scale(), @@ -1430,6 +1431,7 @@ void LIRGenerator::volatile_field_store(LIR_Opr value, LIR_Address* address, } else { __ store(value, address, info); } + __ membar(); } void LIRGenerator::volatile_field_load(LIR_Address* address, LIR_Opr result, diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index 89bf1c40c8e..3ce7e6f4d38 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -345,8 +345,8 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { // volatile field operations are never patchable because a klass // must be loaded to know it's volatile which means that the offset // is always known as well. + // volatile_field_store and volatile_field_load provide MO_SEQ_CST semantics. void volatile_field_store(LIR_Opr value, LIR_Address* address, CodeEmitInfo* info); - // volatile_field_load provides trailing membar semantics void volatile_field_load(LIR_Address* address, LIR_Opr result, CodeEmitInfo* info); void put_Object_unsafe(LIR_Opr src, LIR_Opr offset, LIR_Opr data, BasicType type, bool is_volatile); diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp index f90fbc517dc..8c0082afd5a 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.cpp @@ -138,29 +138,29 @@ LIR_Opr BarrierSetC1::atomic_add_at(LIRAccess& access, LIRItem& value) { } void BarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { + LIRGenerator* gen = access.gen(); DecoratorSet decorators = access.decorators(); bool is_volatile = (decorators & MO_SEQ_CST) != 0; bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0; bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0; - LIRGenerator* gen = access.gen(); if (mask_boolean) { value = gen->mask_boolean(access.base().opr(), value, access.access_emit_info()); } - if (is_volatile) { - __ membar_release(); - } - - LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; if (is_volatile && !needs_patching) { gen->volatile_field_store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info()); } else { + if (is_volatile) { + __ membar_release(); + } + + LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; __ store(value, access.resolved_addr()->as_address_ptr(), access.access_emit_info(), patch_code); - } - if (is_volatile && !support_IRIW_for_not_multiple_copy_atomic_cpu) { - __ membar(); + if (!support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile) { + __ membar(); + } } } @@ -171,26 +171,24 @@ void BarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { bool needs_patching = (decorators & C1_NEEDS_PATCHING) != 0; bool mask_boolean = (decorators & C1_MASK_BOOLEAN) != 0; bool in_native = (decorators & IN_NATIVE) != 0; - bool needs_trailing_membar = is_volatile; - if (support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile) { - __ membar(); - } - - LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; - if (in_native) { - __ move_wide(access.resolved_addr()->as_address_ptr(), result); - } else if (is_volatile && !needs_patching) { - // volatile_field_load provides trailing membar semantics. - // Hence separate trailing membar is not needed. - needs_trailing_membar = false; + if (is_volatile && !in_native && !needs_patching) { gen->volatile_field_load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info()); } else { - __ load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info(), patch_code); - } + if (support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile) { + __ membar(); + } - if (needs_trailing_membar) { - __ membar_acquire(); + if (in_native) { + __ move_wide(access.resolved_addr()->as_address_ptr(), result); + } else { + LIR_PatchCode patch_code = needs_patching ? lir_patch_normal : lir_patch_none; + __ load(access.resolved_addr()->as_address_ptr(), result, access.access_emit_info(), patch_code); + } + + if (is_volatile) { + __ membar_acquire(); + } } // Truncate boolean values returned by unsafe operations. diff --git a/src/hotspot/share/oops/accessDecorators.hpp b/src/hotspot/share/oops/accessDecorators.hpp index 6e673f8f390..94b5e68d302 100644 --- a/src/hotspot/share/oops/accessDecorators.hpp +++ b/src/hotspot/share/oops/accessDecorators.hpp @@ -91,7 +91,8 @@ const DecoratorSet INTERNAL_DECORATOR_MASK = INTERNAL_CONVERT_COMPRESS // - Guarantees from relaxed stores hold. // * MO_SEQ_CST: Sequentially consistent stores. // - The stores are observed in the same order by MO_SEQ_CST loads on other processors -// - Preceding loads and stores in program order are not reordered with subsequent loads and stores in program order. +// - Preceding MO_SEQ_CST loads and stores in program order are not reordered with +// subsequent MO_SEQ_CST loads and stores in program order. // - Guarantees from releasing stores hold. // === Loads === // * MO_UNORDERED (Default): No guarantees diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java b/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java index d25079f16cc..f2d9b557733 100644 --- a/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java +++ b/test/hotspot/jtreg/compiler/gcbarriers/TestImplicitNullChecks.java @@ -146,12 +146,49 @@ static void testStore(Outer o, Object o1) { o.f = o1; } - @Run(test = {"testStore"}) + @Test + // G1 and ZGC stores cannot be currently used to implement implicit null + // checks, because they expand into multiple memory access instructions that + // are not necessarily located at the initial instruction start address. + @IR(applyIfOr = {"UseZGC", "true", "UseG1GC", "true"}, + failOn = IRNode.NULL_CHECK, + phase = CompilePhase.FINAL_CODE) + static void testStoreVolatile(OuterWithVolatileField o, Object o1) { + o.f = o1; + } + + @Run(test = {"testStore", + "testStoreVolatile"}) static void runStoreTests() { { Outer o = new Outer(); Object o1 = new Object(); - testStore(o, o1); + // Trigger compilation with implicit null check. + for (int i = 0; i < 10_000; i++) { + testStore(o, o1); + } + // Trigger null pointer exception. + o = null; + boolean nullPointerException = false; + try { + testStore(o, o1); + } catch (NullPointerException e) { nullPointerException = true; } + Asserts.assertTrue(nullPointerException); + } + { + OuterWithVolatileField o = new OuterWithVolatileField(); + Object o1 = new Object(); + // Trigger compilation with implicit null check. + for (int i = 0; i < 10_000; i++) { + testStoreVolatile(o, o1); + } + // Trigger null pointer exception. + o = null; + boolean nullPointerException = false; + try { + testStoreVolatile(o, o1); + } catch (NullPointerException e) { nullPointerException = true; } + Asserts.assertTrue(nullPointerException); } } diff --git a/test/hotspot/jtreg/compiler/membars/DekkerTest_Interpreter_C1.java b/test/hotspot/jtreg/compiler/membars/DekkerTest_Interpreter_C1.java new file mode 100644 index 00000000000..6aee381a701 --- /dev/null +++ b/test/hotspot/jtreg/compiler/membars/DekkerTest_Interpreter_C1.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2013 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. + */ + +/** + * @test + * @summary A volatile store in C1 followed by a volatile load in interpreter. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:TieredStopAtLevel=1 -XX:CICompilerCount=1 -Xbatch + * -XX:CompileCommand=dontinline,*DekkerTest_Interpreter_C1::interpreter_get_field_* + * -XX:CompileCommand=exclude,*DekkerTest_Interpreter_C1::interpreter_get_field_* + * compiler.membars.DekkerTest_Interpreter_C1 + */ + +package compiler.membars; + +public class DekkerTest_Interpreter_C1 { + /* + Read After Write Test (basically a simple Dekker test with volatile variables) + Derived from the DekkerTest: + test/hotspot/jtreg/compiler/membars/DekkerTest.java + */ + + static final int ITERATIONS = 1000000; + + static class TestData { + public volatile int a; + public volatile int b; + } + + static class ResultData { + public int a; + public int b; + } + + TestData[] testDataArray; + ResultData[] results; + + volatile boolean start; + + public DekkerTest_Interpreter_C1() { + testDataArray = new TestData[ITERATIONS]; + results = new ResultData[ITERATIONS]; + for (int i = 0; i < ITERATIONS; ++i) { + testDataArray[i] = new TestData(); + results[i] = new ResultData(); + } + start = false; + } + + public void reset() { + for (int i = 0; i < ITERATIONS; ++i) { + testDataArray[i].a = 0; + testDataArray[i].b = 0; + results[i].a = 0; + results[i].b = 0; + } + start = false; + } + + int interpreter_get_field_a(TestData t) { + return t.a; + } + + int interpreter_get_field_b(TestData t) { + return t.b; + } + + int actor1(TestData t) { + t.a = 1; + return interpreter_get_field_b(t); + } + + int actor2(TestData t) { + t.b = 1; + return interpreter_get_field_a(t); + } + + class Runner1 extends Thread { + public void run() { + do {} while (!start); + for (int i = 0; i < ITERATIONS; ++i) { + results[i].a = actor1(testDataArray[i]); + } + } + } + + class Runner2 extends Thread { + public void run() { + do {} while (!start); + for (int i = 0; i < ITERATIONS; ++i) { + results[i].b = actor2(testDataArray[i]); + } + } + } + + void testRunner() { + Thread thread1 = new Runner1(); + Thread thread2 = new Runner2(); + thread1.start(); + thread2.start(); + do {} while (!thread1.isAlive()); + do {} while (!thread2.isAlive()); + start = true; + Thread.yield(); + try { + thread1.join(); + thread2.join(); + } catch (InterruptedException e) { + System.out.println("interrupted!"); + System.exit(1); + } + } + + boolean printResult() { + int[] count = new int[4]; + for (int i = 0; i < ITERATIONS; ++i) { + int event_kind = (results[i].a << 1) + results[i].b; + ++count[event_kind]; + } + if (count[0] == 0 && count[3] == 0) { + System.out.println("[not interesting]"); + return false; // not interesting + } + String error = (count[0] == 0) ? " ok" : " disallowed!"; + System.out.println("[0,0] " + count[0] + error); + System.out.println("[0,1] " + count[1]); + System.out.println("[1,0] " + count[2]); + System.out.println("[1,1] " + count[3]); + return (count[0] != 0); + } + + public static void main(String args[]) { + DekkerTest_Interpreter_C1 test = new DekkerTest_Interpreter_C1(); + final int runs = 100; + int failed = 0; + for (int c = 0; c < runs; ++c) { + test.testRunner(); + if (test.printResult()) { + failed++; + } + test.reset(); + } + if (failed > 0) { + throw new InternalError("FAILED. Got " + failed + " failures"); + } + System.out.println("PASSED."); + } + +} From e9557d8891bf37335d3866a26d03cac3b3a12c82 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Wed, 7 Oct 2026 08:40:09 +0000 Subject: [PATCH 79/98] 8393609: Remove redundant JFR test policy file Reviewed-by: egahlin --- .../event/runtime/exception.security.policy | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 test/jdk/jdk/jfr/event/runtime/exception.security.policy diff --git a/test/jdk/jdk/jfr/event/runtime/exception.security.policy b/test/jdk/jdk/jfr/event/runtime/exception.security.policy deleted file mode 100644 index 3e539340402..00000000000 --- a/test/jdk/jdk/jfr/event/runtime/exception.security.policy +++ /dev/null @@ -1,20 +0,0 @@ -grant { - // must allow file reads so that jtreg itself and JFR can run - permission java.io.FilePermission "<>", "read"; - // must allow file delete so that JFR can delete repository - permission java.io.FilePermission "<>", "delete"; - // must allow file write so that the test can create the recording - permission java.io.FilePermission "<>", "write"; - - // need to be able to create temporary files - permission java.util.PropertyPermission "java.io.tmpdir", "read"; - permission java.util.PropertyPermission "user.dir", "read"; - - // need ManagementPermission to control JFR from the test - permission java.lang.management.ManagementPermission "control"; - permission java.lang.management.ManagementPermission "monitor"; - - // JDK-8019403 - access to sun.security.util, which is needed for creation of temp files, - // is not permitted automatically on solaris - permission java.lang.RuntimePermission "accessClassInPackage.sun.security.util"; -}; From 9a57f0efa7195ad8f0139650e60ceab05bb0b338 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Wed, 7 Oct 2026 09:02:56 +0000 Subject: [PATCH 80/98] 8392993: Null pointer dereferences in screencast code Reviewed-by: prr, serb, avu --- .../native/libawt_xawt/awt/gtk3_interface.c | 8 +- .../native/libawt_xawt/awt/gtk3_interface.h | 8 +- .../native/libawt_xawt/awt/gtk_interface.h | 7 +- .../libawt_xawt/awt/screencast_pipewire.c | 8 +- .../libawt_xawt/awt/screencast_portal.c | 137 +++++++++++++----- 5 files changed, 123 insertions(+), 45 deletions(-) diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 6ed23ca7541..850ff92eb5b 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -583,6 +583,8 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_variant_get = dl_symbol("g_variant_get"); fp_g_variant_get_string = dl_symbol("g_variant_get_string"); fp_g_variant_get_uint32 = dl_symbol("g_variant_get_uint32"); + fp_g_variant_get_type_string = dl_symbol("g_variant_get_type_string"); + fp_g_variant_is_of_type = dl_symbol("g_variant_is_of_type"); fp_g_variant_iter_loop = dl_symbol("g_variant_iter_loop"); fp_g_variant_unref = dl_symbol("g_variant_unref"); fp_g_variant_lookup = dl_symbol("g_variant_lookup"); @@ -611,6 +613,7 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_error_free = dl_symbol("g_error_free"); fp_g_unix_fd_list_get = dl_symbol("g_unix_fd_list_get"); + fp_g_unix_fd_list_get_length = dl_symbol("g_unix_fd_list_get_length"); } /* Now we have only one kind of exceptions: NO_SYMBOL_EXCEPTION * Otherwise we can check the return value of setjmp method. @@ -3122,6 +3125,8 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_variant_get = fp_g_variant_get; gtk->g_variant_get_string = fp_g_variant_get_string; gtk->g_variant_get_uint32 = fp_g_variant_get_uint32; + gtk->g_variant_get_type_string = fp_g_variant_get_type_string; + gtk->g_variant_is_of_type = fp_g_variant_is_of_type; gtk->g_variant_lookup = fp_g_variant_lookup; @@ -3150,6 +3155,7 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_main_context_is_owner = fp_g_main_context_is_owner; gtk->g_error_free = fp_g_error_free; gtk->g_unix_fd_list_get = fp_g_unix_fd_list_get; + gtk->g_unix_fd_list_get_length = fp_g_unix_fd_list_get_length; gtk->gdk_pixbuf_new = fp_gdk_pixbuf_new; gtk->gdk_pixbuf_new_from_data = fp_gdk_pixbuf_new_from_data; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 637a3198e43..d51a6e8bb62 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -755,6 +755,10 @@ static const gchar *(*fp_g_variant_get_string)(GVariant *value, static guint32 (*fp_g_variant_get_uint32)(GVariant *value); +static const gchar *(*fp_g_variant_get_type_string)(GVariant *value); + +static gboolean (*fp_g_variant_is_of_type)(GVariant *value, const GVariantType *type); + static gboolean (*fp_g_variant_lookup)(GVariant *dictionary, const gchar *key, const gchar *format_string, @@ -816,4 +820,6 @@ static gint (*fp_g_unix_fd_list_get)(GUnixFDList *list, gint index_, GError **error); +static gint (*fp_g_unix_fd_list_get_length)(GUnixFDList* list); + #endif /* !_GTK3_INTERFACE_H */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index 37422094202..cd6d4337b77 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -684,6 +684,8 @@ typedef struct GtkApi { ...); const gchar *(*g_variant_get_string)(GVariant *value, gsize *length); guint32 (*g_variant_get_uint32)(GVariant *value); + const gchar *(*g_variant_get_type_string)(GVariant *value); + gboolean (*g_variant_is_of_type)(GVariant *value, const GVariantType *type); gboolean (*g_variant_lookup)(GVariant *dictionary, const gchar *key, @@ -803,6 +805,9 @@ typedef struct GtkApi { gint index_, GError **error); + gint (*g_unix_fd_list_get_length)(GUnixFDList* list); + + GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c index e29c6bc4319..13cf21994f9 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -316,9 +316,11 @@ static void onStreamProcess(void *userdata) { spaBuffer = pwBuffer->buffer; if (!spaBuffer || spaBuffer->n_datas < 1 - || spaBuffer->datas[0].data == NULL) { + || spaBuffer->datas[0].data == NULL + || spaBuffer->datas[0].chunk == NULL) { DEBUG_SCREEN_PREFIX(screen, "!!! no data, n_datas %d\n", - spaBuffer->n_datas); + spaBuffer ? (int) spaBuffer->n_datas : -1); + fp_pw_stream_queue_buffer(data->stream, pwBuffer); return; } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c index 24469edd057..462e3640cf6 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,27 @@ extern struct ScreenSpace screenSpace; struct XdgDesktopPortalApi *portal = NULL; extern int DEBUG_SCREENCAST_ENABLED; +static gboolean checkVariantType(const char *functionName, + int lineNum, + GVariant *value, + const char *expectedType, + const char *caption) { + if (value != NULL && gtk->g_variant_is_of_type( + value, (const GVariantType *) expectedType)) { + return TRUE; + } + + debug_screencast("%s:%i %s: invalid type %s, expected %s\n", + functionName, lineNum, caption, + value != NULL ? gtk->g_variant_get_type_string(value) : "NULL", + expectedType); + return FALSE; +} + +#define GET_VARIANT_CHECKED(VALUE, TYPE, FORMAT, CAPTION, ...) \ + (checkVariantType(__func__, __LINE__, (VALUE), (TYPE), (CAPTION)) && \ + (gtk->g_variant_get((VALUE), (FORMAT), __VA_ARGS__), TRUE)) + GDBusProxy *getProxy() { return isRemoteDesktop ? portal->remoteDesktopProxy : portal->screenCastProxy; } @@ -116,14 +137,20 @@ gboolean rebuildScreenData(GVariantIter *iterStreams, gboolean isTheOnlyMon) { DEBUG_SCREENCAST("\n==== screenId#%i\n", nodeID); if (screenIndex >= screenSpace.allocated) { - screenSpace.screens = realloc( + const int newAllocated = screenSpace.allocated + 1; + struct ScreenProps *newScreens = realloc( screenSpace.screens, - ++screenSpace.allocated * sizeof(struct ScreenProps) + newAllocated * sizeof(struct ScreenProps) ); - if (!screenSpace.screens) { + + if (!newScreens) { ERR("failed to allocate memory\n"); + gtk->g_variant_unref(prop); return FALSE; } + + screenSpace.screens = newScreens; + screenSpace.allocated = newAllocated; } struct ScreenProps * screen = &screenSpace.screens[screenIndex]; @@ -162,7 +189,6 @@ gboolean rebuildScreenData(GVariantIter *iterStreams, gboolean isTheOnlyMon) { DEBUG_SCREEN(screen); DEBUG_SCREENCAST("#---------------------#\n\n", NULL); - gtk->g_variant_unref(prop); screenIndex++; }; @@ -186,7 +212,7 @@ gboolean checkVersion() { : PORTAL_IFACE_SCREENCAST; if (version == 0) { - GError *error = NULL; + GError *err = NULL; GVariant *retVersion = gtk->g_dbus_proxy_call_sync( getProxy(), @@ -195,7 +221,7 @@ gboolean checkVersion() { interface, "version"), G_DBUS_CALL_FLAGS_NONE, - -1, NULL, NULL + -1, NULL, &err ); if (isRemoteDesktop) { @@ -206,13 +232,16 @@ gboolean checkVersion() { if (!retVersion) { //no backend on system DEBUG_SCREENCAST("!!! could not detect the %s version\n", interface); + ERR_HANDLE(err); return FALSE; } - ERR_HANDLE(error); - GVariant *varVersion = NULL; - gtk->g_variant_get(retVersion, "(v)", &varVersion); + if (!GET_VARIANT_CHECKED(retVersion, "(v)", "(v)", + "Version response", &varVersion)) { + gtk->g_variant_unref(retVersion); + return FALSE; + } if (!varVersion){ gtk->g_variant_unref(retVersion); @@ -220,11 +249,14 @@ gboolean checkVersion() { return FALSE; } - version = gtk->g_variant_get_uint32(varVersion); + if (!GET_VARIANT_CHECKED(varVersion, "u", "u", "Version", &version)) { + gtk->g_variant_unref(varVersion); + gtk->g_variant_unref(retVersion); + return FALSE; + } gtk->g_variant_unref(varVersion); gtk->g_variant_unref(retVersion); - } gboolean isVersionOk = isRemoteDesktop @@ -417,12 +449,12 @@ static void callbackScreenCastCreateSession( uint32_t status; GVariant *result = NULL; - gtk->g_variant_get( - parameters, - "(u@a{sv})", - &status, - &result - ); + if (!GET_VARIANT_CHECKED(parameters, "(ua{sv})", "(u@a{sv})", + "CreateSession response", &status, &result)) { + helper->isDone = TRUE; + callbackEnd(); + return; + } if (status != 0) { DEBUG_SCREENCAST("Failed to create ScreenCast: %u\n", status); @@ -431,6 +463,7 @@ static void callbackScreenCastCreateSession( DEBUG_SCREENCAST("session_handle returned %b %p\n", returned, helper->data) } + gtk->g_variant_unref(result); helper->isDone = TRUE; callbackEnd(); } @@ -541,7 +574,12 @@ static void callbackScreenCastSelectSources( uint32_t status; GVariant* result = NULL; - gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + if (!GET_VARIANT_CHECKED(parameters, "(ua{sv})", "(u@a{sv})", + "SelectSources response", &status, &result)) { + helper->isDone = TRUE; + callbackEnd(); + return; + } if (status != 0) { DEBUG_SCREENCAST("Failed select sources: %u\n", status); @@ -574,7 +612,12 @@ static void callbackRemoteDesktopSelectDevices( uint32_t status; GVariant* result = NULL; - gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + if (!GET_VARIANT_CHECKED(parameters, "(ua{sv})", "(u@a{sv})", + "SelectDevices response", &status, &result)) { + helper->isDone = TRUE; + callbackEnd(); + return; + } if (status != 0) { DEBUG_SCREENCAST("Failed select devices: %u\n", status); @@ -709,12 +752,19 @@ static void callbackScreenCastStart( GVariant* result = NULL; const gchar *oldToken = startHelper->token; - gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result); + if (!GET_VARIANT_CHECKED(parameters, "(ua{sv})", "(u@a{sv})", + "Start response", &status, &result)) { + startHelper->result = RESULT_ERROR; + helper->isDone = TRUE; + callbackEnd(); + return; + } if (status != 0) { // Cancel pressed on the system dialog DEBUG_SCREENCAST("Failed to start screencast: %u\n", status); startHelper->result = RESULT_DENIED; + gtk->g_variant_unref(result); helper->isDone = TRUE; callbackEnd(); return; @@ -723,14 +773,15 @@ static void callbackScreenCastStart( GVariant *streams = gtk->g_variant_lookup_value( result, "streams", - G_VARIANT_TYPE_ARRAY + (const GVariantType *) "a(ua{sv})" ); print_gvariant_content("Streams", streams); if (!streams) { - DEBUG_SCREENCAST("No streams available with current token\n", NULL); + DEBUG_SCREENCAST("No streams available with current token\n", NULL); startHelper->result = RESULT_NO_STREAMS; + gtk->g_variant_unref(result); helper->isDone = TRUE; callbackEnd(); return; @@ -774,6 +825,7 @@ static void callbackScreenCastStart( helper->isDone = TRUE; gtk->g_variant_unref(streams); + gtk->g_variant_unref(result); callbackEnd(); } @@ -869,27 +921,36 @@ int portalScreenCastOpenPipewireRemote() { &err ); - if (err || !response) { - DEBUG_SCREENCAST("Failed to call OpenPipeWireRemote on session: %s\n", - err->message); + if (err || !response || !fdList) { + DEBUG_SCREENCAST("Failed to call OpenPipeWireRemote on session: %s, fdList %p\n", + err ? err->message : (!response ? "no response" : "no fd list"), + (void *)fdList); + if (response) { + gtk->g_variant_unref(response); + } + if (fdList) { + gtk->g_object_unref(fdList); + } ERR_HANDLE(err); return RESULT_ERROR; } gint32 index; - gtk->g_variant_get( - response, - "(h)", - &index, - &err - ); + if (!GET_VARIANT_CHECKED(response, "(h)", "(h)", + "OpenPipeWireRemote response", &index)) { + gtk->g_variant_unref(response); + gtk->g_object_unref(fdList); + return RESULT_ERROR; + } gtk->g_variant_unref(response); - if (err) { - DEBUG_SCREENCAST("Failed to get pipewire fd index: %s\n", - err->message); - ERR_HANDLE(err); + gint length = gtk->g_unix_fd_list_get_length(fdList); + + if (index < 0 || index >= length) { + DEBUG_SCREENCAST("Invalid pipewire fd index: %d (fdList length: %d)\n", + index, length); + gtk->g_object_unref(fdList); return RESULT_ERROR; } @@ -899,9 +960,7 @@ int portalScreenCastOpenPipewireRemote() { &err ); - if (fdList) { - gtk->g_object_unref(fdList); - } + gtk->g_object_unref(fdList); if (err) { DEBUG_SCREENCAST("Failed to get pipewire fd: %s\n", err->message); From ac70f5612a69c8b6c048206512a323e206b9cb4c Mon Sep 17 00:00:00 2001 From: Gui Cao Date: Wed, 7 Oct 2026 09:55:50 +0000 Subject: [PATCH 81/98] 8388479: RISC-V: compiler/vectorapi/TestMaskedNotAllOnes.java fails after JDK-8386163 Reviewed-by: dzhang, fyang --- src/hotspot/cpu/riscv/riscv.ad | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 74f0e368705..9e974a277e9 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2308,7 +2308,7 @@ bool size_fits_all_mem_uses(AddPNode* addp, int shift) { } // Binary src (Replicate scalar/immediate) -static bool is_vector_scalar_bitwise_pattern(Node* n, Node* m) { +static bool is_vector_scalar_binary_pattern(Node* n, Node* m) { if (n == nullptr || m == nullptr) { return false; } @@ -2350,12 +2350,29 @@ static bool is_vector_bitwise_not_pattern(Node* n, Node* m) { return false; } +// 'n' is the current user of input 'm', which is being considered for cloning. +// Other users must also allow cloning to avoid matching 'm' both folded and standalone. +static bool are_users_clone_compatible(Node* n, Node* m) { + for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { + Node* use = m->fast_out(i); + if (use == n) { + continue; + } + if (!is_vector_bitwise_not_pattern(use, m) && + !is_vector_scalar_binary_pattern(use, m)) { + return false; + } + } + return true; +} + // Should the Matcher clone input 'm' of node 'n'? bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { assert_cond(m != nullptr); if (is_vshift_con_pattern(n, m) || // ShiftV src (ShiftCntV con) - is_vector_bitwise_not_pattern(n, m) || - is_vector_scalar_bitwise_pattern(n, m) || + ((is_vector_bitwise_not_pattern(n, m) || + is_vector_scalar_binary_pattern(n, m)) && + are_users_clone_compatible(n, m)) || is_encode_and_store_pattern(n, m)) { mstack.push(m, Visit); return true; From b651006a1d85b235184505c74e417e1914de3fa0 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Wed, 7 Oct 2026 12:30:46 +0000 Subject: [PATCH 82/98] 8393662: JFR: SettingDescriptor::getDefaultValue() is deserialized incorrectly Reviewed-by: mgronlun --- .../share/classes/jdk/jfr/internal/MetadataReader.java | 5 +++-- .../jfr/api/metadata/settingdescriptor/TestDefaultValue.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java index 81f4c79fba4..dd0a68e0ee0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION; import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID; import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DEFAULT_VALUE; import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE; import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE; import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID; @@ -229,7 +230,7 @@ private void defineTypes(Element metadata) { Type t = types.get(id); for (Element fieldElement : typeElement.elements(ELEMENT_SETTING)) { String name = fieldElement.attribute(ATTRIBUTE_NAME); - String defaultValue = fieldElement.attribute(ATTRIBUTE_NAME); + String defaultValue = fieldElement.attribute(ATTRIBUTE_DEFAULT_VALUE); Type settingType = getType(ATTRIBUTE_TYPE_ID, fieldElement); PlatformEventType eventType = (PlatformEventType) t; eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, name, defaultValue, new ArrayList<>(2))); diff --git a/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestDefaultValue.java b/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestDefaultValue.java index c88b2101348..c092591ea70 100644 --- a/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestDefaultValue.java +++ b/test/jdk/jdk/jfr/api/metadata/settingdescriptor/TestDefaultValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,6 @@ public static void main(String[] args) throws Exception { SettingDescriptor overridden = Events.getSetting(type, "overridden"); Asserts.assertEquals(overridden.getDefaultValue(), PlainSetting.DEFAULT_VALUE); - CustomEvent.assertOnDisk((x, y) -> x.getName().compareTo(y.getName())); + CustomEvent.assertOnDisk((x, y) -> x.getDefaultValue().compareTo(y.getDefaultValue())); } } From 51d43c00d204e36ae6548bc085cdea23d970d23b Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 7 Oct 2026 12:37:03 +0000 Subject: [PATCH 83/98] 8393195: Update Libpng to 1.6.59 Reviewed-by: serb, azvegint --- src/java.desktop/share/legal/libpng.md | 4 ++- .../native/libsplashscreen/libpng/CHANGES | 13 +++++++ .../native/libsplashscreen/libpng/README | 2 +- .../share/native/libsplashscreen/libpng/png.c | 4 +-- .../share/native/libsplashscreen/libpng/png.h | 14 ++++---- .../native/libsplashscreen/libpng/pngconf.h | 2 +- .../libsplashscreen/libpng/pnglibconf.h | 2 +- .../native/libsplashscreen/libpng/pngread.c | 6 ++-- .../native/libsplashscreen/libpng/pngrtran.c | 1 + .../native/libsplashscreen/libpng/pngrutil.c | 36 +++++++++++-------- .../native/libsplashscreen/libpng/pngset.c | 2 ++ 11 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 4c17a52a7ef..aa0ecb68b3b 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.58 +## libpng v1.6.59 ### libpng License

        @@ -160,6 +160,7 @@ Authors, for copyright and licensing purposes.
          * Adam Richter
          * Alexander Smorkalov
          * Andreas Dilger
        + * Anthony Hurtado
          * Chris Blume
          * Cosmin Truta
          * Dave Martindale
        @@ -197,6 +198,7 @@ Authors, for copyright and licensing purposes.
          * Vadim Barkov
          * Willem van Schaik
          * Yuelin Wang (王跃林)
        + * Yuki Sekiguchi
          * Zhijie Liang
          * Apple Inc.
             - Zixu Wang (王子旭)
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
        index 208ca6eaf17..cd637fb9880 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
        @@ -6385,6 +6385,19 @@ Version 1.6.58 [April 15, 2026]
             in-place.
             (Reported by ralfjunker .)
         
        +Version 1.6.59 [September 28, 2026]
        +  Fixed CVE-2026-46675 (medium severity):
        +    Use-after-free of zlib input in `png_read_end` after incomplete zTXt, iTXt
        +    or iCCP decompression.
        +    (Reported independently by Ze Sheng and
        +    .)
        +  Fixed a regression introduced in version 1.6.47 that caused libpng to reject
        +    hIST chunks in their correct position, after PLTE.
        +    (Contributed by Yuki Sekiguchi.)
        +  Prevented a double free of `png_struct` members after an allocation failure.
        +    (Contributed by Anthony Hurtado.)
        +  Applied fixes and updates to the CMake build.
        +  Adopted the REUSE Specification for licensing the CI files.
         
         Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
         Subscription is required; visit
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
        index 75a92ab7233..9fd4e425c4f 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/README
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
        @@ -1,4 +1,4 @@
        -README for libpng version 1.6.58
        +README for libpng version 1.6.59
         ================================
         
         See the note about version numbers near the top of `png.h`.
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
        index 12f7ce53f34..40381ac3773 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
        @@ -42,7 +42,7 @@
         #include "pngpriv.h"
         
         /* Generate a compiler error if there is an old png.h in the search path. */
        -typedef png_libpng_version_1_6_58 Your_png_h_is_not_version_1_6_58;
        +typedef png_libpng_version_1_6_59 Your_png_h_is_not_version_1_6_59;
         
         /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
          * corresponding macro definitions.  This causes a compile time failure if
        @@ -849,7 +849,7 @@ png_get_copyright(png_const_structrp png_ptr)
            return PNG_STRING_COPYRIGHT
         #else
            return PNG_STRING_NEWLINE \
        -      "libpng version 1.6.58" PNG_STRING_NEWLINE \
        +      "libpng version 1.6.59" PNG_STRING_NEWLINE \
               "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
               "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
               PNG_STRING_NEWLINE \
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
        index 86a7c8ca1b0..d5c65de77dc 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
        @@ -29,7 +29,7 @@
          * However, the following notice accompanied the original version of this
          * file and, per its terms, should not be removed:
          *
        - * libpng version 1.6.58
        + * libpng version 1.6.59
          *
          * Copyright (c) 2018-2026 Cosmin Truta
          * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
        @@ -43,7 +43,7 @@
          *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
          *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
          *     Glenn Randers-Pehrson
        - *   libpng versions 1.6.36, December 2018, through 1.6.58, April 2026:
        + *   libpng versions 1.6.36, December 2018, through 1.6.59, September 2026:
          *     Cosmin Truta
          *   See also "Contributing Authors", below.
          */
        @@ -267,7 +267,7 @@
          *    ...
          *    1.5.30                  15    10530  15.so.15.30[.0]
          *    ...
        - *    1.6.58                  16    10658  16.so.16.58[.0]
        + *    1.6.59                  16    10659  16.so.16.59[.0]
          *
          *    Henceforth the source version will match the shared-library major and
          *    minor numbers; the shared-library major version number will be used for
        @@ -303,7 +303,7 @@
          */
         
         /* Version information for png.h - this should match the version in png.c */
        -#define PNG_LIBPNG_VER_STRING "1.6.58"
        +#define PNG_LIBPNG_VER_STRING "1.6.59"
         #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
         
         /* The versions of shared library builds should stay in sync, going forward */
        @@ -314,7 +314,7 @@
         /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
         #define PNG_LIBPNG_VER_MAJOR   1
         #define PNG_LIBPNG_VER_MINOR   6
        -#define PNG_LIBPNG_VER_RELEASE 58
        +#define PNG_LIBPNG_VER_RELEASE 59
         
         /* This should be zero for a public release, or non-zero for a
          * development version.
        @@ -345,7 +345,7 @@
          * From version 1.0.1 it is:
          * XXYYZZ, where XX=major, YY=minor, ZZ=release
          */
        -#define PNG_LIBPNG_VER 10658 /* 1.6.58 */
        +#define PNG_LIBPNG_VER 10659 /* 1.6.59 */
         
         /* Library configuration: these options cannot be changed after
          * the library has been built.
        @@ -455,7 +455,7 @@ extern "C" {
         /* This triggers a compiler error in png.c, if png.c and png.h
          * do not agree upon the version number.
          */
        -typedef char *png_libpng_version_1_6_58;
        +typedef char *png_libpng_version_1_6_59;
         
         /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
          *
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
        index e035a155803..d57f2406ab5 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
        @@ -29,7 +29,7 @@
          * However, the following notice accompanied the original version of this
          * file and, per its terms, should not be removed:
          *
        - * libpng version 1.6.58
        + * libpng version 1.6.59
          *
          * Copyright (c) 2018-2026 Cosmin Truta
          * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
        index 46cba507f65..6e7bcfd308d 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
        @@ -31,7 +31,7 @@
          * However, the following notice accompanied the original version of this
          * file and, per its terms, should not be removed:
          */
        -/* libpng version 1.6.58 */
        +/* libpng version 1.6.59 */
         
         /* Copyright (c) 2018-2026 Cosmin Truta */
         /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
        index 70df18926f5..56aa16d41cf 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
        @@ -711,8 +711,10 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr)
            if (png_ptr == NULL)
               return;
         
        -   /* If png_read_end is called in the middle of reading the rows there may
        -    * still be pending IDAT data and an owned zstream.  Deal with this here.
        +   /* If png_read_end is called in the middle of reading the rows,
        +    * there may still be pending IDAT data and an owned zstream.
        +    * If it is called before row reading starts, there is pending
        +    * IDAT data but no owned zstream.
             */
         #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
            if (png_chunk_unknown_handling(png_ptr, png_IDAT) == 0)
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
        index 99050080e9d..ef1fb043097 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
        @@ -543,6 +543,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette,
                * this function more than once per png_struct.
                */
               png_free(png_ptr, png_ptr->quantize_index);
        +      png_ptr->quantize_index = NULL;
               png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr,
                   PNG_MAX_PALETTE_LENGTH);
               for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++)
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
        index 4712dfd418a..57afbcdde68 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
        @@ -434,6 +434,18 @@ png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size)
         }
         #endif /* READ_iCCP|iTXt|pCAL|sCAL|sPLT|tEXt|zTXt|eXIf|SEQUENTIAL_READ */
         
        +/* Detach the zstream from the input and output buffers left by
        + * the current or a previous owner, and possibly deallocated since.
        + */
        +static void
        +png_inflate_detach_buffers(png_structrp png_ptr)
        +{
        +   png_ptr->zstream.next_in = NULL;
        +   png_ptr->zstream.avail_in = 0;
        +   png_ptr->zstream.next_out = NULL;
        +   png_ptr->zstream.avail_out = 0;
        +}
        +
         /* png_inflate_claim: claim the zstream for some nefarious purpose that involves
          * decompression.  Returns Z_OK on success, else a zlib error code.  It checks
          * the owner but, in final release builds, just issues a warning if some other
        @@ -494,13 +506,7 @@ png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
         
         #endif /* ZLIB_VERNUM >= 0x1240 */
         
        -      /* Set this for safety, just in case the previous owner left pointers to
        -       * memory allocations.
        -       */
        -      png_ptr->zstream.next_in = NULL;
        -      png_ptr->zstream.avail_in = 0;
        -      png_ptr->zstream.next_out = NULL;
        -      png_ptr->zstream.avail_out = 0;
        +      png_inflate_detach_buffers(png_ptr);
         
               if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) != 0)
               {
        @@ -837,7 +843,8 @@ png_decompress_chunk(png_structrp png_ptr,
                  else if (ret == Z_OK)
                     ret = PNG_UNEXPECTED_ZLIB_RETURN;
         
        -         /* Release the claimed stream */
        +         /* Release the claimed stream. */
        +         png_inflate_detach_buffers(png_ptr);
                  png_ptr->zowner = 0;
               }
         
        @@ -1520,6 +1527,7 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
         
                                             if (errmsg == NULL)
                                             {
        +                                       png_inflate_detach_buffers(png_ptr);
                                                png_ptr->zowner = 0;
                                                return handled_ok;
                                             }
        @@ -1546,7 +1554,8 @@ png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
                        else /* profile truncated */
                           errmsg = png_ptr->zstream.msg;
         
        -               /* Release the stream */
        +               /* Release the claimed stream. */
        +               png_inflate_detach_buffers(png_ptr);
                        png_ptr->zowner = 0;
                     }
         
        @@ -3091,7 +3100,7 @@ read_chunks[PNG_INDEX_unknown] =
         #  define CDiTXt  NoCheck,    6U,      0, hIHDR,        1
               /* Allocates 'length+1'; checked in the handler */
         #  define CDbKGD       6U,    1U,  hIDAT, hIHDR,        0
        -#  define CDhIST    1024U,    0U,  hPLTE, hIHDR,        0
        +#  define CDhIST    1024U,    0U,  hIDAT, hPLTE,        0
         #  define CDpHYs       9U,    9U,  hIDAT, hIHDR,        0
         #  define CDsPLT  NoCheck,    3U,  hIDAT, hIHDR,        1
               /* Allocates 'length+1'; checked in the handler */
        @@ -4366,11 +4375,7 @@ png_read_finish_IDAT(png_structrp png_ptr)
             */
            if (png_ptr->zowner == png_IDAT)
            {
        -      /* Always do this; the pointers otherwise point into the read buffer. */
        -      png_ptr->zstream.next_in = NULL;
        -      png_ptr->zstream.avail_in = 0;
        -
        -      /* Now we no longer own the zstream. */
        +      png_inflate_detach_buffers(png_ptr);
               png_ptr->zowner = 0;
         
               /* The slightly weird semantics of the sequential IDAT reading is that we
        @@ -4633,6 +4638,7 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
            {
               png_free(png_ptr, png_ptr->big_row_buf);
               png_free(png_ptr, png_ptr->big_prev_row);
        +      png_ptr->big_row_buf = png_ptr->big_prev_row = NULL;
         
               if (png_ptr->interlaced != 0)
                  png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
        diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
        index 29082a6be08..d1a3fff77c2 100644
        --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
        +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
        @@ -840,6 +840,7 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
             * regardless of num_palette.
             */
            png_free(png_ptr, png_ptr->palette);
        +   png_ptr->palette = NULL;
            png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
                PNG_MAX_PALETTE_LENGTH * (sizeof (png_color))));
            info_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr,
        @@ -1252,6 +1253,7 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
                    * functions (e.g. png_do_expand_palette).
                    */
                   png_free(png_ptr, png_ptr->trans_alpha);
        +          png_ptr->trans_alpha = NULL;
                   png_ptr->trans_alpha = png_voidcast(png_bytep,
                       png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH));
                   memset(png_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH);
        
        From ac4981e399870db3a6e4eb884c82704330ea5f8d Mon Sep 17 00:00:00 2001
        From: Christian Hagedorn 
        Date: Wed, 7 Oct 2026 13:41:38 +0000
        Subject: [PATCH 84/98] 8393593: [IR Framework] IR matching for inner classes
         does not work
        
        Reviewed-by: mchevalier, thartmann
        ---
         .../ir_framework/driver/FlagVMProcess.java    |  4 +-
         .../flag/CompilePhaseCollector.java           |  4 +-
         .../ir_framework/tests/TestIRMatching.java    | 64 +++++++++----------
         .../tests/flag/TestCompilePhaseCollector.java |  2 +-
         4 files changed, 37 insertions(+), 37 deletions(-)
        
        diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java
        index 86eb1935207..d441aedf661 100644
        --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java
        +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java
        @@ -105,8 +105,8 @@ private void prepareVMFlags(Class testClass, List additionalFlags) {
                 cmds.add("-XX:+WhiteBoxAPI");
                 // TestFramework and scenario flags might have an influence on the later used Test VM flags. Add them as well.
                 cmds.addAll(additionalFlags);
        -        cmds.add(FlagVM.class.getCanonicalName());
        -        cmds.add(testClass.getCanonicalName());
        +        cmds.add(FlagVM.class.getName());
        +        cmds.add(testClass.getName());
             }
         
             private void start() {
        diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java
        index 0488b3f58e6..c15bc8221db 100644
        --- a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java
        +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java
        @@ -49,7 +49,7 @@ public static Map> collect(Class testClass) {
                 List irAnnotatedMethods = getIRAnnotatedMethods(testClass);
                 try {
                     for (Method method : irAnnotatedMethods) {
        -                methodNameToCompilePhasesMap.put(testClass.getCanonicalName() + "::" + method.getName(),
        +                methodNameToCompilePhasesMap.put(testClass.getName() + "::" + method.getName(),
                                                          collectCompilePhases(method));
                     }
                 } catch (TestFormatException e) {
        @@ -75,7 +75,7 @@ private static Map> createDefaultMap(Class testClas
                 HashSet defaultSet = new HashSet<>();
                 defaultSet.add(CompilePhase.PRINT_IDEAL);
                 defaultSet.add(CompilePhase.PRINT_OPTO_ASSEMBLY);
        -        defaultMap.put(testClass.getCanonicalName() + "::*", defaultSet);
        +        defaultMap.put(testClass.getName() + "::*", defaultSet);
                 return defaultMap;
             }
         }
        diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java
        index 0e410533cbf..0f9f1ec4484 100644
        --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java
        +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java
        @@ -27,14 +27,12 @@
         import compiler.lib.ir_framework.driver.irmatching.IRViolationException;
         import jdk.test.lib.Asserts;
         import jdk.test.lib.Platform;
        -import jdk.test.whitebox.WhiteBox;
         
         import java.io.ByteArrayOutputStream;
         import java.io.PrintStream;
         import java.util.*;
         import java.util.regex.Matcher;
         import java.util.regex.Pattern;
        -import java.util.stream.Collectors;
         
         /*
          * @test
        @@ -432,6 +430,38 @@ private static void assertNoIds(String output, String methodName) {
                     addException(new RuntimeException("Should not find ids for \"" + methodName + "\"" + System.lineSeparator()));
                 }
             }
        +
        +    // IR rules can also be defined in inner classes. In this case, the ourwe class is not allowed to also define
        +    // IR rules (results in a format violation).
        +    static class CountComparisons {
        +        int iFld;
        +
        +        @Test
        +        @IR(counts = {IRNode.STORE, "= 1",
        +                IRNode.STORE, "=1",
        +                IRNode.STORE, " = 1",
        +                IRNode.STORE, "  =  1",
        +                IRNode.STORE, ">= 1",
        +                IRNode.STORE, ">=1",
        +                IRNode.STORE, " >= 1",
        +                IRNode.STORE, "  >=  1",
        +                IRNode.STORE, "<= 1",
        +                IRNode.STORE, "<=1",
        +                IRNode.STORE, " <= 1",
        +                IRNode.STORE, "  <=  1",
        +                IRNode.STORE, "> 0",
        +                IRNode.STORE, ">0",
        +                IRNode.STORE, " > 0",
        +                IRNode.STORE, "  >  0",
        +                IRNode.STORE, "< 2",
        +                IRNode.STORE, "<2",
        +                IRNode.STORE, " < 2",
        +                IRNode.STORE, "  <  2",
        +        })
        +        public void countComparison() {
        +            iFld = 3;
        +        }
        +    }
         }
         
         class AndOr1 {
        @@ -636,36 +666,6 @@ public void testMatchAllIf50() {}
             public void testMatchNoneIf50() {}
         }
         
        -class CountComparisons {
        -    int iFld;
        -
        -    @Test
        -    @IR(counts = {IRNode.STORE, "= 1",
        -                  IRNode.STORE, "=1",
        -                  IRNode.STORE, " = 1",
        -                  IRNode.STORE, "  =  1",
        -                  IRNode.STORE, ">= 1",
        -                  IRNode.STORE, ">=1",
        -                  IRNode.STORE, " >= 1",
        -                  IRNode.STORE, "  >=  1",
        -                  IRNode.STORE, "<= 1",
        -                  IRNode.STORE, "<=1",
        -                  IRNode.STORE, " <= 1",
        -                  IRNode.STORE, "  <=  1",
        -                  IRNode.STORE, "> 0",
        -                  IRNode.STORE, ">0",
        -                  IRNode.STORE, " > 0",
        -                  IRNode.STORE, "  >  0",
        -                  IRNode.STORE, "< 2",
        -                  IRNode.STORE, "<2",
        -                  IRNode.STORE, " < 2",
        -                  IRNode.STORE, "  <  2",
        -    })
        -    public void countComparison() {
        -        iFld = 3;
        -    }
        -}
        -
         class GoodCount {
             boolean flag;
             char cFld;
        diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/flag/TestCompilePhaseCollector.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/flag/TestCompilePhaseCollector.java
        index 0098929eed7..c98a0d9f3a7 100644
        --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/flag/TestCompilePhaseCollector.java
        +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/flag/TestCompilePhaseCollector.java
        @@ -159,7 +159,7 @@ private void assertSize(Map> compilePhases, int size)
             }
         
             public String getFullMethodName(Class testClass, String methodName) {
        -        return testClass.getCanonicalName() + "::" + methodName;
        +        return testClass.getName() + "::" + methodName;
             }
         
             static class Ideal {
        
        From 26653b4a090f990deaf6a99d057ec264a65c43d6 Mon Sep 17 00:00:00 2001
        From: Azad Abdulov 
        Date: Wed, 7 Oct 2026 14:06:44 +0000
        Subject: [PATCH 85/98] 8393106: (fs) FileSystemLoopException created with
         message string rather than file name
        
        Reviewed-by: vyazici, avu
        ---
         .../classes/sun/nio/fs/UnixException.java     |  6 +-
         .../file/etc/FileSystemLoopExceptionTest.java | 87 +++++++++++++++++++
         2 files changed, 89 insertions(+), 4 deletions(-)
         create mode 100644 test/jdk/java/nio/file/etc/FileSystemLoopExceptionTest.java
        
        diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixException.java b/src/java.base/unix/classes/sun/nio/fs/UnixException.java
        index 0f0de768109..9ad8b58ea76 100644
        --- a/src/java.base/unix/classes/sun/nio/fs/UnixException.java
        +++ b/src/java.base/unix/classes/sun/nio/fs/UnixException.java
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -93,9 +93,7 @@ private IOException translateToIOException(String file, String other) {
                 if (errno() == UnixConstants.EEXIST)
                     return new FileAlreadyExistsException(file, other, null);
                 if (errno() == UnixConstants.ELOOP) {
        -            String msg = file + ": " + errorString()
        -                 + " or unable to access attributes of symbolic link";
        -            return new FileSystemLoopException(msg);
        +            return new FileSystemLoopException(file);
                 }
         
                 // fallback to the more general exception
        diff --git a/test/jdk/java/nio/file/etc/FileSystemLoopExceptionTest.java b/test/jdk/java/nio/file/etc/FileSystemLoopExceptionTest.java
        new file mode 100644
        index 00000000000..5e8cfb42c29
        --- /dev/null
        +++ b/test/jdk/java/nio/file/etc/FileSystemLoopExceptionTest.java
        @@ -0,0 +1,87 @@
        +/*
        + * Copyright (c) 2026, JetBrains s.r.o.. All rights reserved.
        + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        + *
        + * This code is free software; you can redistribute it and/or modify it
        + * under the terms of the GNU General Public License version 2 only, as
        + * published by the Free Software Foundation.
        + *
        + * This code is distributed in the hope that it will be useful, but WITHOUT
        + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        + * version 2 for more details (a copy is included in the LICENSE file that
        + * accompanied this code).
        + *
        + * You should have received a copy of the GNU General Public License version
        + * 2 along with this work; if not, write to the Free Software Foundation,
        + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
        + *
        + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
        + * or visit www.oracle.com if you need additional information or have any
        + * questions.
        + */
        +
        +/* @test
        + * @bug 8393106
        + * @summary Check file system loop exception structure
        + * @comment `Files.new{Input,Output}Stream()` doesn't trigger a
        + *          `FileSystemLoopException` on Windows, and therefore it's excluded
        + * @requires os.family != "windows"
        + * @library ..
        + * @run junit ${test.main.class}
        + */
        +
        +import org.junit.jupiter.api.AfterAll;
        +import org.junit.jupiter.api.BeforeAll;
        +import org.junit.jupiter.api.function.Executable;
        +import org.junit.jupiter.params.ParameterizedTest;
        +import org.junit.jupiter.params.provider.Arguments;
        +import org.junit.jupiter.params.provider.MethodSource;
        +
        +import java.nio.channels.FileChannel;
        +import java.nio.file.FileSystemLoopException;
        +import java.nio.file.Files;
        +import java.nio.file.Path;
        +import java.util.function.Function;
        +import java.util.stream.Stream;
        +
        +import static java.nio.file.StandardOpenOption.READ;
        +import static org.junit.jupiter.api.Assertions.assertEquals;
        +import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
        +import static org.junit.jupiter.api.Assumptions.assumeTrue;
        +
        +public class FileSystemLoopExceptionTest {
        +    private static Path link;
        +
        +    @BeforeAll
        +    static void beforeAll() throws Exception {
        +        link = Files.createTempDirectory("loop_test").resolve("link");
        +        assumeTrue(TestUtil.supportsSymbolicLinks(link.getParent()), "Test requires symbolic links support");
        +        Files.createSymbolicLink(link, link);
        +    }
        +
        +    @AfterAll
        +    static void afterAll() throws Exception {
        +        Files.deleteIfExists(link);
        +        Files.deleteIfExists(link.getParent());
        +    }
        +
        +    @ParameterizedTest
        +    @MethodSource
        +    public void symlinkLoop(Function executableProvider) {
        +        FileSystemLoopException expectedException = new FileSystemLoopException(link.toString());
        +        FileSystemLoopException actualException = assertThrowsExactly(FileSystemLoopException.class, executableProvider.apply(link));
        +
        +        assertEquals(expectedException.getFile(), actualException.getFile());
        +        assertEquals(expectedException.getOtherFile(), actualException.getOtherFile());
        +        assertEquals(expectedException.getReason(), actualException.getReason());
        +    }
        +
        +    static private Stream symlinkLoop() {
        +        return Stream.of(
        +                Arguments.of((Function) link -> () -> Files.newInputStream(link).close()),
        +                Arguments.of((Function) link -> () -> Files.newOutputStream(link).close()),
        +                Arguments.of((Function) link -> () -> FileChannel.open(link, READ).close())
        +        );
        +    }
        +}
        
        From 9bed54be7807b8344300b1fc8f49ff9338c8a96c Mon Sep 17 00:00:00 2001
        From: Yunbo Zhang 
        Date: Wed, 7 Oct 2026 14:21:41 +0000
        Subject: [PATCH 86/98] 8370047: 'make dist-clean' fails due to missing
         spec.gmk
        
        Reviewed-by: erikj
        ---
         make/Init.gmk | 10 ++++++----
         1 file changed, 6 insertions(+), 4 deletions(-)
        
        diff --git a/make/Init.gmk b/make/Init.gmk
        index 38959323628..b2d27d549c2 100644
        --- a/make/Init.gmk
        +++ b/make/Init.gmk
        @@ -1,5 +1,5 @@
         #
        -# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
        +# Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
         # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
         #
         # This code is free software; you can redistribute it and/or modify it
        @@ -151,9 +151,11 @@ main: $(INIT_TARGETS) create-make-helpers
         	    ( cd $(TOPDIR) && \
         	        $(MAKE) $(MAKE_ARGS) -j 1 -f make/Main.gmk $(USER_MAKE_VARS) \
         	        $(SEQUENTIAL_TARGETS) )
        -            # We might have cleaned away essential files, recreate them.
        -	    ( cd $(TOPDIR) && \
        -	        $(MAKE) $(MAKE_ARGS) -j 1 -f make/Init.gmk create-make-helpers )
        +            # Recreate essential files after cleaning, except for dist-clean.
        +            ifeq ($(filter dist-clean, $(SEQUENTIAL_TARGETS)), )
        +	      ( cd $(TOPDIR) && \
        +	          $(MAKE) $(MAKE_ARGS) -j 1 -f make/Init.gmk create-make-helpers )
        +            endif
                   endif
                   ifneq ($(PARALLEL_TARGETS), )
         	    $(call PrepareFailureLogs)
        
        From d40df6cfd85ef42b66a0af22c123ed7680d9d004 Mon Sep 17 00:00:00 2001
        From: Jorn Vernee 
        Date: Wed, 7 Oct 2026 14:44:53 +0000
        Subject: [PATCH 87/98] 8383244: Shared scope closure should only deoptimize if
         the scope oop is live
        
        Reviewed-by: vlivanov, dlong
        ---
         .../share/prims/scopedMemoryAccess.cpp        | 123 +++++---
         src/hotspot/share/prims/whitebox.cpp          |   6 +-
         .../java/foreign/TestSharedCloseDeopts.java   | 262 ++++++++++++++++++
         test/lib/jdk/test/whitebox/code/NMethod.java  |   5 +-
         4 files changed, 349 insertions(+), 47 deletions(-)
         create mode 100644 test/jdk/java/foreign/TestSharedCloseDeopts.java
        
        diff --git a/src/hotspot/share/prims/scopedMemoryAccess.cpp b/src/hotspot/share/prims/scopedMemoryAccess.cpp
        index 792bf74afcf..31cde608fe1 100644
        --- a/src/hotspot/share/prims/scopedMemoryAccess.cpp
        +++ b/src/hotspot/share/prims/scopedMemoryAccess.cpp
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -117,15 +117,10 @@ static bool is_accessing_session(JavaThread* jt, oop session, bool& in_scoped) {
           return is_accessing_session;
         }
         
        -static frame get_last_frame(JavaThread* jt) {
        +static frame get_last_frame(JavaThread* jt, RegisterMap* register_map) {
           frame last_frame = jt->last_frame();
        -  RegisterMap register_map(jt,
        -                            RegisterMap::UpdateMap::include,
        -                            RegisterMap::ProcessFrames::include,
        -                            RegisterMap::WalkContinuation::skip);
        -
           if (last_frame.is_safepoint_blob_frame()) {
        -    last_frame = last_frame.sender(®ister_map);
        +    last_frame = last_frame.sender(register_map);
           }
           return last_frame;
         }
        @@ -213,48 +208,86 @@ class CloseScopedMemoryHandshakeClosure : public HandshakeClosure {
               OopHandle error(Universe::vm_global(), JNIHandles::resolve(_error));
               jt->install_async_exception(new ScopedAsyncExceptionHandshakeClosure(session, error, _async_exceptions));
             } else if (!in_scoped) {
        -      frame last_frame = get_last_frame(jt);
        -      if (last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) {
        -        // We are not at a safepoint that is 'in' an @Scoped method, but due to the compiler
        -        // moving code around/hoisting checks, we may be in a situation like this:
        -        //
        -        // liveness check (from @Scoped method)
        -        // for (...) {
        -        //    for (...) { // strip-mining inner loop
        -        //        memory access (from @Scoped method)
        -        //    }
        -        //    safepoint <-- STOPPED HERE
        -        // }
        -        //
        -        // The safepoint at which we're stopped may be in between the liveness check
        -        // and actual memory access, but is itself 'outside' of @Scoped code
        -        //
        -        // However, we're not sure whether we are in this exact situation, and
        -        // we're also not sure whether a memory access will actually occur after
        -        // this safepoint. So, we can not just install an async exception here
        -        //
        -        // Instead, we mark the frame for deoptimization (which happens just before
        -        // execution in this frame continues) to get back to code like this:
        -        //
        -        // for (...) {
        -        //     call to ScopedMemoryAccess
        -        //     safepoint <-- STOPPED HERE
        -        // }
        -        //
        -        // This means that we will re-do the liveness check before attempting
        -        // another memory access. If the scope has been closed at that point,
        -        // the target thread will see it and throw an exception.
        -
        +      RegisterMap register_map(jt,
        +                               RegisterMap::UpdateMap::include,
        +                               RegisterMap::ProcessFrames::include,
        +                               RegisterMap::WalkContinuation::skip);
        +      frame last_frame = get_last_frame(jt, ®ister_map);
        +      if (last_frame.is_compiled_frame()) {
                 nmethod* code = last_frame.cb()->as_nmethod();
        -        if (code->has_scoped_access()) {
        -          // We would like to deoptimize here only if last_frame::oops_do
        -          // reports the session oop being live at this safepoint, but this
        -          // currently isn't possible due to JDK-8290892
        -          Deoptimization::deoptimize(jt, last_frame);
        +        if (last_frame.can_be_deoptimized()) {
        +          // We are not at a safepoint that is 'in' an @Scoped method, but due to the compiler
        +          // moving code around/hoisting checks, we may be in a situation like this:
        +          //
        +          // liveness check (from @Scoped method)
        +          // for (...) {
        +          //    for (...) { // strip-mining inner loop
        +          //        memory access (from @Scoped method)
        +          //    }
        +          //    safepoint <-- STOPPED HERE
        +          // }
        +          //
        +          // The safepoint at which we're stopped may be in between the liveness check
        +          // and actual memory access, but is itself 'outside' of @Scoped code
        +          //
        +          // However, we're not sure whether we are in this exact situation, and
        +          // we're also not sure whether a memory access will actually occur after
        +          // this safepoint. So, we can not just install an async exception here
        +          //
        +          // Instead, we mark the frame for deoptimization (which happens just before
        +          // execution in this frame continues) to get back to code like this:
        +          //
        +          // for (...) {
        +          //     call to ScopedMemoryAccess
        +          //     safepoint <-- STOPPED HERE
        +          // }
        +          //
        +          // This means that we will re-do the liveness check before attempting
        +          // another memory access. If the scope has been closed at that point,
        +          // the target thread will see it and throw an exception.
        +#ifndef PRODUCT
        +          {
        +            LogMessage(foreign, deoptimization) msg;
        +            NonInterleavingLogStream ls{LogLevelType::Trace, msg};
        +            if (ls.is_enabled()) {
        +              ls.print_cr("Inspected compiled frame. has_scoped_access=%s is_session_live=%s:",
        +                BOOL_TO_STR(code->has_scoped_access()),
        +                BOOL_TO_STR(is_session_live(last_frame, register_map)));
        +              last_frame.print_on(&ls);
        +            }
        +          }
        +#endif
        +          if (code->has_scoped_access() && is_session_live(last_frame, register_map)) {
        +            Deoptimization::deoptimize(jt, last_frame);
        +          }
        +        } else {
        +          assert(!code->has_scoped_access(), "Scoped access in non-deoptimizable frame!");
                 }
               }
             }
           }
        +
        +  bool is_session_live(frame the_frame, const RegisterMap& register_map) {
        +    struct OopFinder : public OopClosure {
        +      bool _found;
        +      oop _the_oop;
        +      OopFinder(oop the_oop) : _found(false), _the_oop(the_oop) {}
        +      void check(oop o) { if (o == _the_oop) { _found = true; } }
        +      void do_oop(oop* p) { check(*p); }
        +      void do_oop(narrowOop* p) { check(CompressedOops::decode(*p)); }
        +    };
        +
        +    struct NMethodOopFinder : public NMethodClosure {
        +      OopFinder* _oop_finder;
        +      NMethodOopFinder(OopFinder* oop_finder) : _oop_finder(oop_finder) {}
        +      void do_nmethod(nmethod* n) { n->oops_do(_oop_finder); }
        +    };
        +
        +    OopFinder finder(JNIHandles::resolve(_session));
        +    NMethodOopFinder nmof(&finder);
        +    the_frame.oops_do(&finder, &nmof, ®ister_map);
        +    return finder._found;
        +  }
         };
         
         /*
        diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
        index 0963ba23bf7..00d2bc57ff1 100644
        --- a/src/hotspot/share/prims/whitebox.cpp
        +++ b/src/hotspot/share/prims/whitebox.cpp
        @@ -1628,7 +1628,7 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
           ThreadToNativeFromVM ttn(thread);
           jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string());
           CHECK_JNI_EXCEPTION_(env, nullptr);
        -  result = env->NewObjectArray(5, clazz, nullptr);
        +  result = env->NewObjectArray(6, clazz, nullptr);
           if (result == nullptr) {
             return result;
           }
        @@ -1655,6 +1655,10 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
           CHECK_JNI_EXCEPTION_(env, nullptr);
           env->SetObjectArrayElement(result, 4, entry_point);
         
        +  jobject has_scoped_access = booleanBox(thread, env, (jboolean) code->has_scoped_access());
        +  CHECK_JNI_EXCEPTION_(env, nullptr);
        +  env->SetObjectArrayElement(result, 5, has_scoped_access);
        +
           return result;
         WB_END
         
        diff --git a/test/jdk/java/foreign/TestSharedCloseDeopts.java b/test/jdk/java/foreign/TestSharedCloseDeopts.java
        new file mode 100644
        index 00000000000..230572da6f1
        --- /dev/null
        +++ b/test/jdk/java/foreign/TestSharedCloseDeopts.java
        @@ -0,0 +1,262 @@
        +/*
        + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
        + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        + *
        + * This code is free software; you can redistribute it and/or modify it
        + * under the terms of the GNU General Public License version 2 only, as
        + * published by the Free Software Foundation.
        + *
        + * This code is distributed in the hope that it will be useful, but WITHOUT
        + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        + * version 2 for more details (a copy is included in the LICENSE file that
        + * accompanied this code).
        + *
        + * You should have received a copy of the GNU General Public License version
        + * 2 along with this work; if not, write to the Free Software Foundation,
        + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
        + *
        + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
        + * or visit www.oracle.com if you need additional information or have any
        + * questions.
        + */
        +
        +/*
        + * @test
        + * @requires vm.compiler2.enabled
        + * @requires vm.debug
        + * @modules java.base/jdk.internal.vm.annotation java.base/jdk.internal.misc
        + * @library /test/lib
        + *
        + * @build jdk.test.whitebox.WhiteBox
        + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
        + *
        + * @run junit/othervm
        + *   -Xbootclasspath/a:.
        + *   -XX:+UnlockDiagnosticVMOptions
        + *   -XX:+WhiteBoxAPI
        + *   -XX:CompileCommand=dontinline,TestSharedCloseDeopts::outOfLine
        + *   -XX:CompileCommand=dontinline,TestSharedCloseDeopts::payloadWithAccess
        + *   -XX:CompileCommand=dontinline,TestSharedCloseDeopts::payloadWithoutAccess
        + *   -XX:-UseOnStackReplacement
        + *   -XX:-BackgroundCompilation
        + *   -Xlog:foreign+deoptimization=trace:file=test_deopts.txt:none
        + *   TestSharedCloseDeopts
        + */
        +
        +import jdk.test.whitebox.WhiteBox;
        +import jdk.test.whitebox.code.NMethod;
        +import org.junit.jupiter.api.BeforeAll;
        +import org.junit.jupiter.params.ParameterizedTest;
        +import org.junit.jupiter.params.provider.MethodSource;
        +
        +import java.io.IOException;
        +import java.io.InputStream;
        +import java.lang.classfile.*;
        +import java.lang.classfile.instruction.InvokeInstruction;
        +import java.lang.classfile.instruction.LineNumber;
        +import java.lang.foreign.Arena;
        +import java.lang.foreign.MemorySegment;
        +import java.lang.foreign.ValueLayout;
        +import java.lang.reflect.Method;
        +import java.nio.file.Files;
        +import java.nio.file.Path;
        +import java.nio.file.StandardOpenOption;
        +import java.util.List;
        +import java.util.concurrent.atomic.AtomicBoolean;
        +import java.util.concurrent.atomic.AtomicReference;
        +import java.util.stream.Stream;
        +
        +import static org.junit.jupiter.api.Assertions.*;
        +
        +public class TestSharedCloseDeopts {
        +    static final WhiteBox WB = WhiteBox.getWhiteBox();
        +    static final Method PAYLOAD_WITH_ACCESS_METHOD;
        +    static final Method PAYLOAD_WITHOUT_ACCESS_METHOD;
        +    static final int C2_COMPILED_LEVEL = 4;
        +    static final String SHARED_SCOPE_CLOSED_DEOPT_REASON = "constraint";
        +    static final Path LOG_FILE = Path.of("test_deopts.txt");
        +
        +    static final ClassModel TEST_CLASS_MODEL;
        +
        +    static {
        +        try {
        +            PAYLOAD_WITH_ACCESS_METHOD = TestSharedCloseDeopts.class.getDeclaredMethod("payloadWithAccess",
        +                    MemorySegment.Scope.class, MemorySegment.class, AtomicBoolean.class);
        +            PAYLOAD_WITHOUT_ACCESS_METHOD = TestSharedCloseDeopts.class.getDeclaredMethod("payloadWithoutAccess",
        +                    MemorySegment.Scope.class, MemorySegment.class, AtomicBoolean.class);
        +            try (InputStream stream = TestSharedCloseDeopts.class
        +                    .getResourceAsStream("TestSharedCloseDeopts.class")) {
        +                TEST_CLASS_MODEL = ClassFile.of().parse(stream.readAllBytes());
        +            }
        +        } catch (ReflectiveOperationException | IOException e) {
        +            throw new ExceptionInInitializerError(e);
        +        }
        +    }
        +
        +    @BeforeAll
        +    public static void before() throws Throwable {
        +        for (int i = 0; i < 20_000; i++) {
        +            // compile first to avoid noise
        +            outOfLine();
        +        }
        +
        +        for (TestCase testCase : (Iterable) () -> cases().iterator()) {
        +            // do a dry run to compile the payload* methods
        +            // and reduce other noise due to uncommon traps triggering
        +            AtomicBoolean hold = new AtomicBoolean();
        +            AtomicBoolean shouldCallPayload = new AtomicBoolean(true);
        +            AtomicReference uncaughtException = new AtomicReference<>();
        +            try (Arena arena = Arena.ofShared()) {
        +                MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT);
        +                Thread workerThread = Thread.ofPlatform()
        +                    .name("Warmup-Worker")
        +                    .uncaughtExceptionHandler((_, ex) -> uncaughtException.set(ex))
        +                    .start(() -> {
        +                        while (shouldCallPayload.getAcquire()) {
        +                            testCase.payload().run(segment.scope(), segment, hold);
        +                        }
        +                    });
        +
        +                // await compilation while polluting the profile to avoid
        +                // uncommon trap deopt as result of loop backedge test
        +                NMethod code;
        +                do {
        +                    assertTrue(WB.isMethodCompilable(testCase.method(), C2_COMPILED_LEVEL, false));
        +                    for (int i = 0; i < 100_000; i++) {
        +                        hold.setRelease(true);
        +                        hold.setRelease(false);
        +                    }
        +                    code = NMethod.get(testCase.method(), false);
        +                } while (code == null || code.comp_level != C2_COMPILED_LEVEL);
        +                assertEquals(testCase.hasAccess(), code.has_scoped_access);
        +
        +                // Note: stop calling payload before closing the scope
        +                // to avoid uncommon trap/deopt in liveness state check
        +                shouldCallPayload.setRelease(false);
        +                workerThread.join();
        +                if (uncaughtException.get() != null) {
        +                    throw uncaughtException.get();
        +                }
        +            }
        +        }
        +    }
        +
        +    @ParameterizedTest
        +    @MethodSource("cases")
        +    public void testDeopts(TestCase testCase) throws Throwable {
        +        // clear log file for this test case
        +        Files.writeString(LOG_FILE, "", StandardOpenOption.TRUNCATE_EXISTING);
        +
        +        AtomicBoolean hold = new AtomicBoolean(true); // hold worker thread in loop
        +        AtomicReference uncaughtException = new AtomicReference<>();
        +        Thread workerThread;
        +        int deoptCountBeforeClose;
        +        try (Arena arena = Arena.ofShared();
        +            Arena _ = Arena.ofShared()) {
        +            MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT);
        +            workerThread = Thread.ofPlatform()
        +                .name("Test-Worker")
        +                .uncaughtExceptionHandler((_, ex) -> uncaughtException.set(ex))
        +                .start(() ->  testCase.payload.run(segment.scope(), segment, hold));
        +            waitUntilThreadIsAtSafepoint(workerThread, testCase.method().getName(), testCase.callLineNumber());
        +            // double check that we're still good to go
        +            NMethod code = NMethod.get(testCase.method(), false);
        +            assertNotNull(code);
        +            assertEquals(C2_COMPILED_LEVEL, code.comp_level);
        +            assertEquals(testCase.hasAccess(), code.has_scoped_access);
        +            deoptCountBeforeClose = WB.getDeoptCount(SHARED_SCOPE_CLOSED_DEOPT_REASON, null);
        +        }
        +        // We closed 2 shared arenas here. One of them was unrelated. Expect at most 1 deopt
        +        int deoptCountAfterClose = WB.getDeoptCount(SHARED_SCOPE_CLOSED_DEOPT_REASON, null);
        +        assertEquals(deoptCountBeforeClose + (testCase.hasAccess() ? 1 : 0), deoptCountAfterClose);
        +
        +        hold.setRelease(false); // release thread from loop
        +        workerThread.join();
        +        if (uncaughtException.get() != null) {
        +            throw uncaughtException.get();
        +        }
        +
        +        // assert that we saw the frame during scope closure using UL log messages
        +        List log = Files.readAllLines(LOG_FILE);
        +        // look for both scope closures. One with the same scope, one with the other
        +        assertTrue(log.stream().anyMatch(l ->
        +                l.contains("has_scoped_access=" + testCase.hasAccess()) && l.contains("is_session_live=false")));
        +        assertTrue(log.stream().anyMatch(l ->
        +                l.contains("has_scoped_access=" + testCase.hasAccess()) && l.contains("is_session_live=true")));
        +    }
        +
        +    // simulate a safepoint in a method with a scoped access
        +    public static MemorySegment.Scope payloadWithAccess(MemorySegment.Scope scope, MemorySegment segment,
        +                                                        AtomicBoolean hold) {
        +        segment.set(ValueLayout.JAVA_INT, 0L, 42);
        +        while (hold.getAcquire()) {
        +            // conditionally hold at the post call safepoint
        +            outOfLine();
        +        }
        +        return scope;
        +    }
        +
        +    // same but without a scoped access
        +    public static MemorySegment.Scope payloadWithoutAccess(MemorySegment.Scope scope, MemorySegment segment,
        +                                                           AtomicBoolean hold) {
        +        while (hold.getAcquire()) {
        +            outOfLine();
        +        }
        +        return scope;
        +    }
        +
        +    public static void outOfLine() {
        +    }
        +
        +    // test helpers
        +    public interface Payload {
        +        MemorySegment.Scope run(MemorySegment.Scope scope, MemorySegment segment, AtomicBoolean hold);
        +    }
        +
        +    public record TestCase(Method method, int callLineNumber, Payload payload, boolean hasAccess) {}
        +
        +    public static Stream cases() {
        +        return Stream.of(
        +                new TestCase(PAYLOAD_WITH_ACCESS_METHOD, findLineNumberOfCall(PAYLOAD_WITH_ACCESS_METHOD),
        +                        TestSharedCloseDeopts::payloadWithAccess, true),
        +                new TestCase(PAYLOAD_WITHOUT_ACCESS_METHOD, findLineNumberOfCall(PAYLOAD_WITHOUT_ACCESS_METHOD),
        +                        TestSharedCloseDeopts::payloadWithoutAccess, false)
        +        );
        +    }
        +
        +    // Wait until we see the target thread at the safepoint at least once,
        +    // to avoid false negatives due to incidental delays with the worker thread starting.
        +    private static void waitUntilThreadIsAtSafepoint(Thread t, String methodName, int lineNumber) throws InterruptedException {
        +        while (true) {
        +            Thread.sleep(100); // give the target thread a moment to advance
        +            StackTraceElement[] trace = t.getStackTrace();
        +            if (trace.length >= 1) {
        +                String lastMethodName = trace[0].getMethodName();
        +                int lastLineNumber = trace[0].getLineNumber();
        +                if (lastMethodName.equals(methodName) && lastLineNumber == lineNumber) {
        +                    return;
        +                }
        +            }
        +        }
        +    }
        +
        +    private static int findLineNumberOfCall(Method payload) {
        +        MethodModel methodModel = TEST_CLASS_MODEL.methods().stream()
        +                .filter(mm -> mm.methodName().equalsString(payload.getName()))
        +                .findFirst().orElseThrow();
        +        LineNumber result = null;
        +        for (CodeElement codeElement : methodModel.code().orElseThrow().elementList()) {
        +            if (codeElement instanceof LineNumber lineNumber) {
        +                result = lineNumber;
        +            }
        +            if (codeElement instanceof InvokeInstruction invoke
        +                    && invoke.opcode() == Opcode.INVOKESTATIC
        +                    && invoke.method().name().equalsString("outOfLine")) {
        +                break; // found our instruction
        +            }
        +        }
        +        assertNotNull(result);
        +        return result.line();
        +    }
        +}
        diff --git a/test/lib/jdk/test/whitebox/code/NMethod.java b/test/lib/jdk/test/whitebox/code/NMethod.java
        index 6092b1037db..24fc57792fd 100644
        --- a/test/lib/jdk/test/whitebox/code/NMethod.java
        +++ b/test/lib/jdk/test/whitebox/code/NMethod.java
        @@ -34,16 +34,18 @@ public static NMethod get(Executable method, boolean isOsr) {
           }
           private NMethod(Object[] obj) {
             super((Object[])obj[0]);
        -    assert obj.length == 5;
        +    assert obj.length == 6;
             comp_level = (Integer) obj[1];
             insts = (byte[]) obj[2];
             compile_id = (Integer) obj[3];
             entry_point = (Long) obj[4];
        +    has_scoped_access = (boolean) obj[5];
           }
           public final byte[] insts;
           public final int comp_level;
           public final int compile_id;
           public final long entry_point;
        +  public final boolean has_scoped_access;
         
           @Override
           public String toString() {
        @@ -53,6 +55,7 @@ public String toString() {
                 + ", comp_level=" + comp_level
                 + ", compile_id=" + compile_id
                 + ", entry_point=" + entry_point
        +        + ", has_scoped_access=" + has_scoped_access
                 + '}';
           }
         }
        
        From a49fd279e566edb36a4489d2c66b647a02b2e95d Mon Sep 17 00:00:00 2001
        From: Naoto Sato 
        Date: Wed, 7 Oct 2026 16:22:52 +0000
        Subject: [PATCH 88/98] 8393247: Pattern \X and \b{g} handling of UAX #29 GB11
        
        Reviewed-by: jlu
        ---
         .../jdk/internal/util/regex/Grapheme.java     | 29 ++++++++++---------
         .../jdk/java/util/regex/GraphemeTestCases.txt | 10 +++++--
         test/jdk/java/util/regex/RegExTest.java       |  4 +--
         3 files changed, 26 insertions(+), 17 deletions(-)
        
        diff --git a/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java b/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java
        index 303a76cb545..9457dc912b2 100644
        --- a/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java
        +++ b/src/java.base/share/classes/jdk/internal/util/regex/Grapheme.java
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -48,7 +48,7 @@ public static int nextBoundary(CharSequence src, int off, int limit) {
         
                 int ch0 = Character.codePointAt(src, off);
                 int ret = off + Character.charCount(ch0);
        -        // indicates whether gb11 or gb12 is underway
        +        // indicates whether GB11 or GB12 is underway
                 int t0 = getType(ch0);
                 int riCount = t0 == RI ? 1 : 0;
                 boolean gb11 = t0 == EXTENDED_PICTOGRAPHIC;
        @@ -65,17 +65,20 @@ public static int nextBoundary(CharSequence src, int off, int limit) {
                         }
                     }
         
        -            if (gb11 && t0 == ZWJ && t1 == EXTENDED_PICTOGRAPHIC) {
        -                // continue for gb11
        -            } else if (riCount % 2 == 1 && t0 == RI && t1 == RI) {
        -                // continue for gb12
        -            } else if (rules[t0][t1]) {
        -                if (ret > off) {
        -                    break;
        -                } else {
        -                    gb11 = t1 == EXTENDED_PICTOGRAPHIC;
        -                    riCount = 0;
        -                }
        +            if (!(gb11 && t0 == ZWJ && t1 == EXTENDED_PICTOGRAPHIC) &&
        +                !(riCount % 2 == 1 && t0 == RI && t1 == RI) &&
        +                rules[t0][t1]) {
        +                // found the break
        +                break;
        +            }
        +
        +            // Update the GB11 state
        +            if (t1 == EXTENDED_PICTOGRAPHIC) {
        +                gb11 = true;
        +            } else if (t0 == ZWJ || (t1 != EXTEND && t1 != ZWJ)) {
        +                // A non-Extended_Pictographic character after ZWJ resets the GB11 state,
        +                // even if it is another ZWJ or an Extend.
        +                gb11 = false;
                     }
         
                     riCount += (t1 == RI) ? 1 : 0;
        diff --git a/test/jdk/java/util/regex/GraphemeTestCases.txt b/test/jdk/java/util/regex/GraphemeTestCases.txt
        index 1d8f1a0827f..e346e3f5bf6 100644
        --- a/test/jdk/java/util/regex/GraphemeTestCases.txt
        +++ b/test/jdk/java/util/regex/GraphemeTestCases.txt
        @@ -1,5 +1,5 @@
         #
        -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
        +# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
         # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
         #
         # This code is free software; you can redistribute it and/or modify it
        @@ -30,4 +30,10 @@
         ÷ 1F468 × 200D × 1F469 × 200D × 1F466 × 200d ÷ 0041 ÷
         ÷ 1F468 × 200D × 1F469 × 200D × 1F466 ÷ 0041 ÷
         ÷ 0041 ÷ 1F468 × 200D × 1F469 × 200D × 1F466 ÷ 0041 ÷
        -÷ 1F468 × 200D × 1F3FE × 200D × 1F469 × 1F3FE × 200D × 1F466 ÷ 0041 ÷
        +÷ 1F468 × 200D × 1F3FE × 200D ÷ 1F469 × 1F3FE × 200D × 1F466 ÷ 0041 ÷
        +
        +# bug 8393247
        +÷ 1F44D × 200D × 200D ÷ 1F44D ÷
        +÷ 1F44D × 0301 × 200D × 1F44D ÷
        +÷ 1F44D × 200D × 0301 ÷ 1F44D ÷
        +÷ 1F44D × 093E × 200D ÷ 1F44D ÷
        diff --git a/test/jdk/java/util/regex/RegExTest.java b/test/jdk/java/util/regex/RegExTest.java
        index 2bd2dc3e38d..2052e2ac280 100644
        --- a/test/jdk/java/util/regex/RegExTest.java
        +++ b/test/jdk/java/util/regex/RegExTest.java
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved.
          * 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,7 @@
          * 6345469 6988218 6693451 7006761 8140212 8143282 8158482 8176029 8184706
          * 8194667 8197462 8184692 8221431 8224789 8228352 8230829 8236034 8235812
          * 8216332 8214245 8237599 8241055 8247546 8258259 8037397 8269753 8276694
        - * 8280403 8264160 8281315 8305107
        + * 8280403 8264160 8281315 8305107 8393247
          * @library /test/lib
          * @library /lib/testlibrary/java/lang
          * @build jdk.test.lib.RandomFactory
        
        From 731ad77c6f84fa17c0e3a00364c4cf749e6507df Mon Sep 17 00:00:00 2001
        From: Aleksey Shipilev 
        Date: Wed, 7 Oct 2026 17:47:13 +0000
        Subject: [PATCH 89/98] 8393258: Shenandoah: Robust (self-)forwarding handling
        
        Reviewed-by: wkemper, kdnilsen
        ---
         .../share/gc/shenandoah/shenandoahAsserts.cpp |  38 ++---
         .../gc/shenandoah/shenandoahBarrierSet.cpp    |  53 ++++---
         .../shenandoah/shenandoahClosures.inline.hpp  |  24 ++--
         .../gc/shenandoah/shenandoahConcurrentGC.cpp  |   9 +-
         .../gc/shenandoah/shenandoahForwarding.hpp    |  74 +++++-----
         .../shenandoahForwarding.inline.hpp           | 133 ++++++++++--------
         .../shenandoahGenerationalEvacuationTask.cpp  |   3 +-
         .../shenandoahGenerationalFullGC.cpp          |   7 +-
         .../shenandoah/shenandoahGenerationalHeap.cpp |  49 +++----
         .../share/gc/shenandoah/shenandoahHeap.cpp    |  22 +--
         .../share/gc/shenandoah/shenandoahHeap.hpp    |   7 -
         .../gc/shenandoah/shenandoahHeap.inline.hpp   |  67 ++++-----
         .../gc/shenandoah/shenandoahHeapRegion.hpp    |   2 +-
         .../gc/shenandoah/shenandoahMark.inline.hpp   |   3 +-
         .../share/gc/shenandoah/shenandoahNMethod.cpp |   2 +-
         .../gc/shenandoah/shenandoahVerifier.cpp      |  37 ++---
         16 files changed, 269 insertions(+), 261 deletions(-)
        
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
        index 2eceeb2eca6..20877fd2dd5 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
        @@ -80,7 +80,7 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) {
             ss.print_cr("%3s marked strong",              ctx->is_marked_strong(obj) ? "" : "not");
             ss.print_cr("%3s marked weak",                ctx->is_marked_weak(obj) ? "" : "not");
             ss.print_cr("%3s in collection set",          heap->in_collection_set(obj) ? "" : "not");
        -    if (heap->mode()->is_generational() && !obj->is_forwarded()) {
        +    if (heap->mode()->is_generational() && !ShenandoahForwarding::is_forwarded(obj)) {
               ss.print_cr("age: %d", obj->age());
             }
             ss.print_raw("mark: ");
        @@ -167,27 +167,31 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l
           msg.append("\n");
         
           if (level >= _safe_oop) {
        -    oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
             msg.append("Forwardee:\n");
        -    if (obj != fwd) {
        +    if (ShenandoahForwarding::is_real_forwarded(obj)) {
        +      oop fwd = ShenandoahForwarding::forwardee_raw(obj);
               if (level >= _safe_oop_fwd && os::is_readable_pointer(fwd)) {
                 print_obj(msg, fwd);
               } else {
                 print_obj_safe(msg, fwd);
               }
        +    } else if (ShenandoahForwarding::is_self_forwarded(obj)) {
        +      msg.append("  (self forwarded)");
             } else {
        -      msg.append("  (the object itself)");
        +      msg.append("  (not forwarded)");
             }
             msg.append("\n");
           }
         
           if (level >= _safe_oop_fwd) {
        -    oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
        -    oop fwd2 = ShenandoahForwarding::get_forwardee_raw_unchecked(fwd);
        -    if (fwd != fwd2) {
        -      msg.append("Second forwardee:\n");
        -      print_obj_safe(msg, fwd2);
        -      msg.append("\n");
        +    if (ShenandoahForwarding::is_real_forwarded(obj)) {
        +      oop fwd = ShenandoahForwarding::forwardee_raw(obj);
        +      if (ShenandoahForwarding::is_forwarded(fwd)) {
        +        oop fwd2 = ShenandoahForwarding::forwardee_raw(fwd);
        +        msg.append("Second forwardee:\n");
        +        print_obj_safe(msg, fwd2);
        +        msg.append("\n");
        +      }
             }
           }
         
        @@ -237,9 +241,10 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char*
                           file, line);
           }
         
        -  oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
        +  oop fwd = obj;
        +  if (ShenandoahForwarding::is_real_forwarded(obj)) {
        +    fwd = ShenandoahForwarding::forwardee_raw(obj);
         
        -  if (obj != fwd) {
             // When Full GC moves the objects, we cannot trust fwdptrs. If we got here, it means something
             // tries fwdptr manipulation when Full GC is running. The only exception is using the fwdptr
             // that still points to the object itself.
        @@ -276,8 +281,7 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char*
             }
         
             // Step 4. Check for multiple forwardings
        -    oop fwd2 = ShenandoahForwarding::get_forwardee_raw_unchecked(fwd);
        -    if (fwd != fwd2) {
        +    if (ShenandoahForwarding::is_forwarded(fwd)) {
               print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
                             "Multiple forwardings",
                             file, line);
        @@ -373,9 +377,8 @@ void ShenandoahAsserts::assert_in_correct_region(void* interior_loc, oop obj, co
         
         void ShenandoahAsserts::assert_forwarded(void* interior_loc, oop obj, const char* file, int line) {
           assert_correct(interior_loc, obj, file, line);
        -  oop fwd =   ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
         
        -  if (obj == fwd) {
        +  if (!ShenandoahForwarding::is_forwarded(obj)) {
             print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_forwarded failed",
                           "Object should be forwarded",
                           file, line);
        @@ -384,9 +387,8 @@ void ShenandoahAsserts::assert_forwarded(void* interior_loc, oop obj, const char
         
         void ShenandoahAsserts::assert_not_forwarded(void* interior_loc, oop obj, const char* file, int line) {
           assert_correct(interior_loc, obj, file, line);
        -  oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
         
        -  if (obj != fwd) {
        +  if (ShenandoahForwarding::is_forwarded(obj)) {
             print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_not_forwarded failed",
                           "Object should not be forwarded",
                           file, line);
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
        index a383aae9aef..f8eff1ddf34 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
        @@ -218,16 +218,20 @@ oop ShenandoahBarrierSet::load_reference_barrier_slow(oop obj, T* load_addr) {
           }
           assert(_heap->has_forwarded_objects(), "Filtered by caller");
           assert(_heap->in_collection_set(obj), "Filtered by caller");
        -  oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -  if (obj == fwd && _heap->is_evacuation_in_progress()) {
        -    Thread* t = Thread::current();
        -    fwd = _heap->evacuate_object(obj, t);
        +
        +  oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +  if (resolved == nullptr) {
        +    if (_heap->is_evacuation_in_progress()) {
        +      resolved = _heap->evacuate_object(obj, Thread::current());
        +    } else {
        +      resolved = obj;
        +    }
           }
        -  if (load_addr != nullptr && fwd != obj) {
        +  if (load_addr != nullptr && resolved != obj) {
             // Since we are here and we know the load address, update the reference.
        -    ShenandoahHeap::atomic_update_oop(fwd, load_addr, obj);
        +    ShenandoahHeap::atomic_update_oop(resolved, load_addr, obj);
           }
        -  return fwd;
        +  return resolved;
         }
         
         template oop ShenandoahBarrierSet::load_reference_barrier_slow(oop obj, oop* load_addr);
        @@ -265,13 +269,17 @@ class ShenandoahUpdateEvacForCloneOopClosure : public BasicOopIterateClosure {
             if (!CompressedOops::is_null(o)) {
               oop obj = CompressedOops::decode_not_null(o);
               if (_cset->is_in(obj)) {
        -        oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -        if (EVAC && obj == fwd) {
        -          fwd = _heap->evacuate_object(obj, _thread);
        +        oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +        if (resolved == nullptr) {
        +          if (EVAC) {
        +            resolved = _heap->evacuate_object(obj, _thread);
        +          } else {
        +            resolved = obj;
        +          }
        +        }
        +        if (resolved != obj) {
        +          ShenandoahHeap::atomic_update_oop(resolved, p, o);
                 }
        -        shenandoah_assert_forwarded_except(p, obj, _heap->cancelled_gc());
        -        ShenandoahHeap::atomic_update_oop(fwd, p, o);
        -        obj = fwd;
               }
             }
           }
        @@ -373,12 +381,13 @@ void ShenandoahBarrierSet::arraycopy_evacuation(T* src, size_t count) {
             if (!CompressedOops::is_null(o)) {
               oop obj = CompressedOops::decode_not_null(o);
               if (cset->is_in(obj)) {
        -        oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -        if (obj == fwd) {
        -          fwd = _heap->evacuate_object(obj, thread);
        +        oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +        if (resolved == nullptr) {
        +          resolved = _heap->evacuate_object(obj, thread);
        +        }
        +        if (resolved != obj) {
        +          ShenandoahHeap::atomic_update_oop(resolved, elem_ptr, o);
                 }
        -        shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc());
        -        ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o);
               }
             }
           }
        @@ -402,9 +411,11 @@ void ShenandoahBarrierSet::arraycopy_update(T* src, size_t count) {
             if (!CompressedOops::is_null(o)) {
               oop obj = CompressedOops::decode_not_null(o);
               if (cset->is_in(obj)) {
        -        oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -        shenandoah_assert_forwarded_except(elem_ptr, obj, _heap->cancelled_gc());
        -        ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o);
        +        shenandoah_assert_forwarded(elem_ptr, obj);
        +        oop resolved = ShenandoahForwarding::forwardee(obj);
        +        if (resolved != obj) {
        +          ShenandoahHeap::atomic_update_oop(resolved, elem_ptr, o);
        +        }
               }
             }
           }
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp
        index fcd564f1d43..16280d35c5d 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp
        @@ -89,9 +89,12 @@ bool ShenandoahForwardedIsAliveClosure::do_object_b(oop obj) {
           if (CompressedOops::is_null(obj)) {
             return false;
           }
        -  obj = ShenandoahForwarding::get_forwardee(obj);
        -  shenandoah_assert_not_forwarded_if(nullptr, obj, ShenandoahHeap::heap()->is_concurrent_mark_in_progress());
        -  return _mark_context->is_marked_or_old(obj);
        +  oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +  if (resolved == nullptr) {
        +    resolved = obj;
        +  }
        +  shenandoah_assert_not_forwarded_if(nullptr, resolved, ShenandoahHeap::heap()->is_concurrent_mark_in_progress());
        +  return _mark_context->is_marked_or_old(resolved);
         }
         
         ShenandoahIsAliveClosure::ShenandoahIsAliveClosure() :
        @@ -149,17 +152,18 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop_
             if (_heap->in_collection_set(obj)) {
               assert(_heap->is_evacuation_in_progress(), "Only do this when evacuation is in progress");
               shenandoah_assert_marked(p, obj);
        -      oop resolved = ShenandoahForwarding::get_forwardee(obj);
        -      if (resolved == obj) {
        +      oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +      if (resolved == nullptr) {
                 Thread* thr = STABLE_THREAD ? _thread : Thread::current();
                 assert(thr == Thread::current(), "Wrong thread");
        -
                 resolved = _heap->evacuate_object(obj, thr);
               }
        -      if (CONCURRENT) {
        -        ShenandoahHeap::atomic_update_oop(resolved, p, o);
        -      } else {
        -        RawAccess::oop_store(p, resolved);
        +      if (resolved != obj) {
        +        if (CONCURRENT) {
        +          ShenandoahHeap::atomic_update_oop(resolved, p, o);
        +        } else {
        +          RawAccess::oop_store(p, resolved);
        +        }
               }
             }
           }
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
        index decd2226f23..f6a61458697 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
        @@ -950,12 +950,13 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) {
                 ShenandoahHeap::atomic_clear_oop(p, obj);
               }
             } else if (_evac_in_progress && _heap->in_collection_set(obj)) {
        -      oop resolved = ShenandoahForwarding::get_forwardee(obj);
        -      if (resolved == obj) {
        +      oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +      if (resolved == nullptr) {
                 resolved = _heap->evacuate_object(obj, _thread);
               }
        -      shenandoah_assert_not_in_cset_except(p, resolved, _heap->cancelled_gc());
        -      ShenandoahHeap::atomic_update_oop(resolved, p, obj);
        +      if (resolved != obj) {
        +        ShenandoahHeap::atomic_update_oop(resolved, p, obj);
        +      }
             }
           }
         }
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahForwarding.hpp b/src/hotspot/share/gc/shenandoah/shenandoahForwarding.hpp
        index ca064ee5f2e..934992c9d24 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahForwarding.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahForwarding.hpp
        @@ -31,59 +31,65 @@
         
         class ShenandoahForwarding {
         public:
        -  /* Gets forwardee from the given object. For a self-forwarded object
        -   * (evacuation failure), returns the object itself.
        +  /* Returns the forwardee.
            */
        -  static inline oop get_forwardee(oop obj);
        +  static inline oop forwardee(oop obj);
         
        -  /* Returns the raw value from forwardee slot. For a self-forwarded
        -   * object, returns the object itself.
        +  /* Returns the forwardee, or null if not forwarded.
            */
        -  static inline oop get_forwardee_raw(oop obj);
        +  static inline oop forwardee_or_null(oop obj);
         
        -  /* Returns the raw value from forwardee slot without any checks.
        -   * Used for quick verification. For a self-forwarded object,
        -   * returns the object itself.
        +  /* Returns the raw forwardee without extra checks.
            */
        -  static inline oop get_forwardee_raw_unchecked(oop obj);
        +  static inline oop forwardee_raw(oop obj);
         
        -  /**
        -   * Returns true if the object is forwarded (including self-forwarded),
        -   * false otherwise.
        +  /* Returns true iff the object is forwarded:
        +   * either real-forwarded or self-forwarded.
            */
           static inline bool is_forwarded(oop obj);
         
        -  /**
        -   * Returns true iff obj has been self-forwarded (i.e. evacuation has
        -   * failed for this object in the current cycle).
        +  /* Returns true iff the object is real-forwarded:
        +   * there is a forwardee that is not the object itself.
        +   */
        +  static inline bool is_real_forwarded(oop obj);
        +
        +  /* Returns true iff the object is self-forwarded:
        +   * there is a forwardee, it is the object itself.
            */
           static inline bool is_self_forwarded(oop obj);
         
        -  /* Tries to atomically update forwardee in $holder object to $update.
        -   * Assumes $holder points at itself.
        -   * Asserts $holder is in from-space.
        -   * Asserts $update is in to-space.
        +  /* Tries to atomically update forwardee in $obj to $update.
            *
        -   * Returns the new object 'update' upon success, or
        -   * the new forwardee that a competing thread installed. If another
        -   * thread self-forwarded the object, returns the object itself.
        +   * Returns the actual forwardee, whether installed by this call
        +   * or discovered during the conflict.
            */
        -  static inline oop try_update_forwardee(oop obj, oop update);
        +  static inline oop try_forward_to(oop obj, oop update);
         
        -  /* Tries to atomically self-forward obj. Used by the evacuation path
        -   * when the copy allocation fails: the failing thread installs the
        -   * self-forwarded bit so other threads see the object as "already
        -   * handled" and return it unchanged.
        -   *
        -   * Returns nullptr on success (we installed the self-forward), or
        -   * the winning forwardee when another thread raced ahead (either a
        -   * real forwardee pointing at a copy, or obj itself if the winner
        -   * also self-forwarded).
        +  /* Unsets self-forwarding bit on the object.
        +   * WARNING: This is only safe to do when no evacuations happen.
            */
        -  static inline oop try_forward_to_self(oop obj, markWord old_mark);
        +  static inline void unset_self_forwarded(oop obj);
         
        +  /* Gets the size of the object, taking care of any forwardings.
        +   */
           static inline size_t size(oop obj);
        +
        +  /* Gets the klass of the object, taking care of any forwardings.
        +   */
           static inline Klass* klass(oop obj);
        +
        +  /* Gets the age of the object, taking care of any forwardings.
        +   */
        +  static inline uint age(oop obj);
        +
        +  /* Bumps the age of the object.
        +   * WARNING: This method is expected to operate on a copy that is
        +   * not accessible to normal use.
        +   */
        +  static inline void increase_age(oop obj, uint add);
        +
        +private:
        +  static inline oop forwardee_raw(oop obj, markWord mark);
         };
         
         #endif // SHARE_GC_SHENANDOAH_SHENANDOAHFORWARDING_HPP
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahForwarding.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahForwarding.inline.hpp
        index df0154871d7..8865e51554c 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahForwarding.inline.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahForwarding.inline.hpp
        @@ -31,90 +31,84 @@
         #include "oops/markWord.hpp"
         #include "runtime/javaThread.hpp"
         
        -inline oop ShenandoahForwarding::get_forwardee_raw(oop obj) {
        -  shenandoah_assert_in_heap_bounds(nullptr, obj);
        -  return get_forwardee_raw_unchecked(obj);
        +inline oop ShenandoahForwarding::forwardee_raw(oop obj) {
        +  return forwardee_raw(obj, obj->mark());
         }
         
        -inline oop ShenandoahForwarding::get_forwardee_raw_unchecked(oop obj) {
        -  // JVMTI and JFR code use mark words for marking objects for their needs.
        -  // On this path, we can encounter the "marked" object, but with null
        -  // fwdptr. That object is still not forwarded, and we need to return
        -  // the object itself.
        -  markWord mark = obj->mark();
        +inline oop ShenandoahForwarding::forwardee_raw(oop obj, markWord mark) {
        +  assert(mark.is_forwarded(), "Must only be here for forwarded objects");
           if (mark.is_marked()) {
        -    HeapWord* fwdptr = (HeapWord*) mark.clear_lock_bits().to_pointer();
        -    if (fwdptr != nullptr) {
        -      return cast_to_oop(fwdptr);
        -    }
        +    // Historically, JVMTI and JFR used mark words for marking objects
        +    // for their needs, without regard for GC. This path is now unreachable:
        +    // incompatible JFR code is disabled with Shenandoah. Assert paranoidly.
        +    HeapWord* fwd = (HeapWord*) mark.clear_lock_bits().to_pointer();
        +    assert(fwd != nullptr, "Sanity: forwardee must be set");
        +    return cast_to_oop(fwd);
           }
        -  // Self-forwarded (evacuation failure): the object stays put; the
        -  // self-fwd bit is set alongside normal lock bits.
        +  // Self-forwarded. Report itself.
        +  assert(mark.is_self_forwarded(), "The only remaining case");
           return obj;
         }
         
        -inline oop ShenandoahForwarding::get_forwardee(oop obj) {
        +inline oop ShenandoahForwarding::forwardee(oop obj) {
           shenandoah_assert_correct(nullptr, obj);
        -  return get_forwardee_raw_unchecked(obj);
        +  return forwardee_raw(obj);
        +}
        +
        +inline oop ShenandoahForwarding::forwardee_or_null(oop obj) {
        +  shenandoah_assert_correct(nullptr, obj);
        +  markWord mark = obj->mark();
        +  if (mark.is_forwarded()) {
        +    return forwardee_raw(obj, mark);
        +  } else {
        +    return nullptr;
        +  }
         }
         
         inline bool ShenandoahForwarding::is_forwarded(oop obj) {
           return obj->mark().is_forwarded();
         }
         
        +inline bool ShenandoahForwarding::is_real_forwarded(oop obj) {
        +  return obj->mark().is_marked();
        +}
        +
         inline bool ShenandoahForwarding::is_self_forwarded(oop obj) {
           return obj->mark().is_self_forwarded();
         }
         
        -inline oop ShenandoahForwarding::try_update_forwardee(oop obj, oop update) {
        +inline oop ShenandoahForwarding::try_forward_to(oop obj, oop update) {
        +  shenandoah_assert_correct(nullptr, obj);
        +
        +  // Optimistic: check if object is already forwarded.
           markWord old_mark = obj->mark();
        -  if (old_mark.is_marked()) {
        -    return cast_to_oop(old_mark.clear_lock_bits().to_pointer());
        -  }
        -  if (old_mark.is_self_forwarded()) {
        -    // Another thread lost the evacuation race; the object stays put.
        -    return obj;
        +  if (old_mark.is_forwarded()) {
        +    return forwardee_raw(obj, old_mark);
           }
         
        -  markWord new_mark = markWord::encode_pointer_as_mark(update);
        +  // Attempt to install and return on success.
        +  markWord new_mark = (update != obj) ?
        +    markWord::encode_pointer_as_mark(update) :
        +    old_mark.set_self_forwarded();
           markWord prev_mark = obj->cas_set_mark(new_mark, old_mark, memory_order_conservative);
           if (prev_mark == old_mark) {
             return update;
           }
        -  // Concurrent writers on a cset object's mark can only be other evacuation
        -  // threads installing forwarding (real or self). Mutators cannot reach the
        -  // mark of a not-yet-forwarded cset object: LRB + stack watermark barriers
        -  // redirect all reference uses before a Java-level operation can touch it.
        -  // So the only possible failure modes are a regular forwardee (marked) or
        -  // a self-forward (possibly with mutator lock/hash mods layered on top
        -  // after the self-forward became visible).
        -  if (prev_mark.is_marked()) {
        -    return cast_to_oop(prev_mark.clear_lock_bits().to_pointer());
        -  }
        -  assert(prev_mark.is_self_forwarded(),
        -         "concurrent writers on cset objects must install forwarding: prev=" INTPTR_FORMAT,
        -         prev_mark.value());
        -  return obj;
        +
        +  // Lost the update race. Pick the forwarding from the existing mark.
        +  // Barriers guarantee that we can see only forwardings here, either real or self.
        +  // Mutators cannot modify mark without executing barriers first and
        +  // completing the forwarding install. Self-forwarded objects can have
        +  // more data layered on top of mark word, this code handles it too.
        +  assert(prev_mark.is_forwarded(), "Must be forwarded: prev=" INTPTR_FORMAT, prev_mark.value());
        +  return forwardee_raw(obj, prev_mark);
         }
         
        -inline oop ShenandoahForwarding::try_forward_to_self(oop obj, markWord old_mark) {
        -  assert(!old_mark.is_forwarded(),
        -         "caller must pass a non-forwarded mark: old=" INTPTR_FORMAT, old_mark.value());
        -  markWord new_mark = old_mark.set_self_forwarded();
        -  markWord prev_mark = obj->cas_set_mark(new_mark, old_mark, memory_order_conservative);
        -  if (prev_mark == old_mark) {
        -    // We installed the self-forward.
        -    return nullptr;
        -  }
        -  // Same invariant as in try_update_forwardee: the only races on a
        -  // cset object's mark come from other evac threads installing forwarding.
        -  if (prev_mark.is_marked()) {
        -    return cast_to_oop(prev_mark.clear_lock_bits().to_pointer());
        +inline void ShenandoahForwarding::unset_self_forwarded(oop obj) {
        +  markWord m = obj->mark();
        +  if (m.is_self_forwarded()) {
        +    obj->set_mark(m.unset_self_forwarded());
           }
        -  assert(prev_mark.is_self_forwarded(),
        -         "concurrent writers on cset objects must install forwarding: prev=" INTPTR_FORMAT,
        -         prev_mark.value());
        -  return obj;
         }
         
         inline Klass* ShenandoahForwarding::klass(oop obj) {
        @@ -122,6 +116,7 @@ inline Klass* ShenandoahForwarding::klass(oop obj) {
             markWord mark = obj->mark();
             if (mark.is_marked()) {
               oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer());
        +      assert(fwd != nullptr, "Sanity: forwarding pointer must be set");
               mark = fwd->mark();
             }
             return mark.klass();
        @@ -134,4 +129,30 @@ inline size_t ShenandoahForwarding::size(oop obj) {
           return obj->size_given_klass(klass(obj));
         }
         
        +inline uint ShenandoahForwarding::age(oop obj) {
        +  markWord mark = obj->mark();
        +  uint age;
        +  if (!mark.is_marked()) {
        +    // Object has trustworthy mark.
        +    age = mark.age();
        +  } else {
        +    // Otherwise resolve the forwardee and pick age from there.
        +    oop fwd = forwardee_raw(obj, mark);
        +    markWord fwd_mark = fwd->mark();
        +    assert(!fwd_mark.is_marked(), "Must not be");
        +    age = fwd_mark.age();
        +  }
        +  assert(age <= markWord::max_age, "Age is in bounds");
        +  return age;
        +}
        +
        +inline void ShenandoahForwarding::increase_age(oop obj, uint add) {
        +  // This method is expected to be called on new copy before it is exposed.
        +  // It also means mark is safe to modify with a non-CAS store.
        +  markWord mark = obj->mark();
        +  assert(!mark.is_marked(), "Must not be");
        +  mark = mark.set_age(MIN2(markWord::max_age, mark.age() + add));
        +  obj->set_mark(mark);
        +}
        +
         #endif // SHARE_GC_SHENANDOAH_SHENANDOAHFORWARDING_INLINE_HPP
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
        index 54ab4c27038..2d58fbb63ae 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
        @@ -24,6 +24,7 @@
          */
         
         #include "gc/shenandoah/shenandoahAsserts.hpp"
        +#include "gc/shenandoah/shenandoahForwarding.inline.hpp"
         #include "gc/shenandoah/shenandoahGeneration.hpp"
         #include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp"
         #include "gc/shenandoah/shenandoahHeap.inline.hpp"
        @@ -40,7 +41,7 @@ class ShenandoahConcurrentEvacuator : public ObjectClosure {
         
           void do_object(oop p) override {
             shenandoah_assert_marked(nullptr, p);
        -    if (!p->is_forwarded()) {
        +    if (!ShenandoahForwarding::is_forwarded(p)) {
               _heap->evacuate_object(p, _thread);
             }
           }
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
        index cfd33994e5f..46230c9654f 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
        @@ -26,6 +26,7 @@
         #include "gc/shared/fullGCForwarding.inline.hpp"
         #include "gc/shared/preservedMarks.inline.hpp"
         #include "gc/shenandoah/shenandoahAffiliation.hpp"
        +#include "gc/shenandoah/shenandoahForwarding.inline.hpp"
         #include "gc/shenandoah/shenandoahGeneration.hpp"
         #include "gc/shenandoah/shenandoahGenerationalFullGC.hpp"
         #include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
        @@ -247,9 +248,9 @@ void ShenandoahPrepareForGenerationalCompactionObjectClosure::do_object(oop p) {
           assert(_heap->global_generation()->complete_marking_context()->is_marked(p), "must be marked");
           assert(!_heap->global_generation()->complete_marking_context()->allocated_after_mark_start(p), "must be truly marked");
         
        -  size_t obj_size = p->size();
        +  size_t obj_size = ShenandoahForwarding::size(p);
           uint from_region_age = _from_region->age();
        -  uint object_age = p->age();
        +  uint object_age = ShenandoahForwarding::age(p);
         
           bool promote_object = false;
           if ((_from_affiliation == ShenandoahAffiliation::YOUNG_GENERATION) &&
        @@ -317,7 +318,7 @@ void ShenandoahPrepareForGenerationalCompactionObjectClosure::do_object(oop p) {
         
             // After full gc compaction, all regions have age 0.  Embed the region's age into the object's age in order to preserve
             // tenuring progress.
        -    ShenandoahHeap::increase_object_age(p, from_region_age + 1);
        +    ShenandoahForwarding::increase_age(p, from_region_age + 1);
         
             if (_young_compact_point + obj_size > _young_to_region->end()) {
               ShenandoahHeapRegion* new_to_region;
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
        index f704908e0cb..703c04f6a7c 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
        @@ -200,13 +200,11 @@ oop ShenandoahGenerationalHeap::evacuate_object(oop p, Thread* thread) {
           const ShenandoahAffiliation target_gen = from_region->affiliation();
         
           if (target_gen == YOUNG_GENERATION) {
        -    markWord mark = p->mark();
        -    if (mark.is_marked()) {
        -      // Already forwarded.
        -      return ShenandoahForwarding::get_forwardee(p);
        +    oop resolved = ShenandoahForwarding::forwardee_or_null(p);
        +    if (resolved != nullptr) {
        +      return resolved;
             }
        -
        -    if (age_census()->is_tenurable(from_region->age() + mark.age())) {
        +    if (age_census()->is_tenurable(from_region->age() + ShenandoahForwarding::age(p))) {
               // If the object is tenurable, try to promote it
               oop result = try_evacuate_object(p, thread, from_region->age());
         
        @@ -314,21 +312,14 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint
             control_thread()->handle_alloc_failure_evac(size);
         
             // Install the self-forwarded bit so other evacuators/LRBs see the
        -    // object as "already handled, do not try to evacuate". The CAS may
        -    // fail if another thread concurrently installed a real forwardee or
        -    // self-forwarded first.
        -    markWord old_mark = p->mark();
        -    if (old_mark.is_forwarded()) {
        -      return ShenandoahForwarding::get_forwardee(p);
        -    }
        -    oop winner = ShenandoahForwarding::try_forward_to_self(p, old_mark);
        -    if (winner == nullptr) {
        -      // We own the self-forwarding. Flag the from-region so the degen/full
        +    // object as "already handled, do not try to evacuate".
        +    oop fwd = ShenandoahForwarding::try_forward_to(p, p);
        +    if (fwd == p) {
        +      // Now self-forwarded. Flag the from-region so the degen/full
               // GC entry drain knows to scan it for self_fwd bits to clear.
               heap_region_containing(p)->set_has_self_forwards();
        -      return p;
             }
        -    return winner;
        +    return fwd;
           }
         
           if (ShenandoahEvacTracking) {
        @@ -339,22 +330,22 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint
           Copy::aligned_disjoint_words(cast_from_oop(p), copy, size);
           oop copy_val = cast_to_oop(copy);
         
        -  // Update the age of the evacuated object
        -  if (TO_GENERATION == YOUNG_GENERATION) {
        -    increase_object_age(copy_val, from_region_age + 1);
        -  }
        -
        -  // Relativize stack chunks before publishing the copy. After the forwarding CAS,
        -  // mutators can see the copy and thaw it via the fast path if flags == 0. We must
        -  // relativize derived pointers and set gc_mode before that happens. Skip if the
        -  // copy's mark word is already a forwarding pointer (another thread won the race
        -  // and overwrote the original's header before we copied it).
           if (!ShenandoahForwarding::is_forwarded(copy_val)) {
        +    // Relativize stack chunks before publishing the copy. After the forwarding CAS,
        +    // mutators can see the copy and thaw it via the fast path if flags == 0. We must
        +    // relativize derived pointers and set gc_mode before that happens. Skip if the
        +    // copy's mark word is already a forwarding pointer (another thread won the race
        +    // and overwrote the original's header before we copied it).
             ContinuationGCSupport::relativize_stack_chunk(copy_val);
        +
        +    // Update the age of the evacuated object
        +    if (TO_GENERATION == YOUNG_GENERATION) {
        +      ShenandoahForwarding::increase_age(copy_val, from_region_age + 1);
        +    }
           }
         
           // Try to install the new forwarding pointer.
        -  oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val);
        +  oop result = ShenandoahForwarding::try_forward_to(p, copy_val);
           if (result == copy_val) {
             // Successfully evacuated. Our copy is now the public one!
             if (ShenandoahEvacTracking) {
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
        index 88bf122f7bd..b0361006633 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
        @@ -1115,7 +1115,7 @@ class ShenandoahConcurrentEvacuateRegionObjectClosure : public ObjectClosure {
         
           void do_object(oop p) {
             shenandoah_assert_marked(nullptr, p);
        -    if (!p->is_forwarded()) {
        +    if (!ShenandoahForwarding::is_forwarded(p)) {
               _heap->evacuate_object(p, _thread);
             }
           }
        @@ -1347,18 +1347,13 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
             // the object as "already handled, do not try to evacuate". The CAS
             // may fail if another thread concurrently installed a real forwardee
             // (they succeeded where we failed) or self-forwarded first.
        -    markWord old_mark = p->mark();
        -    if (old_mark.is_forwarded()) {
        -      return ShenandoahForwarding::get_forwardee(p);
        -    }
        -    oop winner = ShenandoahForwarding::try_forward_to_self(p, old_mark);
        -    if (winner == nullptr) {
        -      // We own the self-forwarding. Flag the region so the degen/full GC
        +    oop fwd = ShenandoahForwarding::try_forward_to(p, p);
        +    if (fwd == p) {
        +      // Now self-forwarded. Flag the region so the degen/full GC
               // entry drain knows to scan it for self_fwd bits to clear.
               from_region->set_has_self_forwards();
        -      return p;
             }
        -    return winner;
        +    return fwd;
           }
         
           if (ShenandoahEvacTracking) {
        @@ -1380,7 +1375,7 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
           }
         
           // Try to install the new forwarding pointer.
        -  oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val);
        +  oop result = ShenandoahForwarding::try_forward_to(p, copy_val);
           if (result == copy_val) {
             // Successfully evacuated. Our copy is now the public one!
             shenandoah_assert_correct(nullptr, copy_val);
        @@ -1418,10 +1413,7 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
         class ShenandoahUnSelfForwardObjectClosure : public ObjectClosure {
         public:
           void do_object(oop obj) override {
        -    markWord m = obj->mark();
        -    if (m.is_self_forwarded()) {
        -      obj->set_mark(m.unset_self_forwarded());
        -    }
        +    ShenandoahForwarding::unset_self_forwarded(obj);
           }
         };
         
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
        index 8e6b1aa97ba..c4639230b7a 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
        @@ -874,13 +874,6 @@ class ShenandoahHeap : public CollectedHeap {
         
           size_t trash_humongous_region_at(ShenandoahHeapRegion *r) const;
         
        -  static inline void increase_object_age(oop obj, uint additional_age);
        -
        -  // Return the object's age, or a sentinel value when the age can't
        -  // necessarily be determined because of concurrent locking by the
        -  // mutator
        -  static inline uint get_object_age(oop obj);
        -
           void log_heap_status(const char *msg) const;
         
         private:
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
        index 55e7f5845aa..f0723ddfef1 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
        @@ -109,14 +109,18 @@ inline void ShenandoahHeap::non_conc_update_with_forwarded(T* p) {
             oop obj = CompressedOops::decode_not_null(o);
             if (in_collection_set(obj)) {
               // Corner case: when evacuation fails, there are objects in collection
        -      // set that are not really forwarded. We can still go and try and update them
        -      // (uselessly) to simplify the common path.
        +      // set that are not forwarded, and can still be in cset.
               shenandoah_assert_forwarded_except(p, obj, cancelled_gc());
        -      oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -      shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc());
        -
        -      // Unconditionally store the update: no concurrent updates expected.
        -      RawAccess::oop_store(p, fwd);
        +      oop resolved = ShenandoahForwarding::forwardee_or_null(obj);
        +      if (resolved == nullptr) {
        +        resolved = obj;
        +      }
        +      shenandoah_assert_not_in_cset_except(p, resolved, cancelled_gc());
        +
        +      if (resolved != obj) {
        +        // Unconditionally store the update: no concurrent updates expected.
        +        RawAccess::oop_store(p, resolved);
        +      }
             }
           }
         }
        @@ -127,20 +131,20 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) {
           if (!CompressedOops::is_null(o)) {
             oop obj = CompressedOops::decode_not_null(o);
             if (in_collection_set(obj)) {
        -      // Corner case: when evacuation fails, there are objects in collection
        -      // set that are not really forwarded. We can still go and try CAS-update them
        -      // (uselessly) to simplify the common path.
        -      shenandoah_assert_forwarded_except(p, obj, cancelled_gc());
        -      oop fwd = ShenandoahForwarding::get_forwardee(obj);
        -      shenandoah_assert_not_in_cset_except(p, fwd, cancelled_gc());
        -
        -      // Sanity check: we should not be updating the cset regions themselves,
        -      // unless we are recovering from the evacuation failure.
        -      shenandoah_assert_not_in_cset_loc_except(p, !is_in(p) || cancelled_gc());
        -
        -      // Either we succeed in updating the reference, or something else gets in our way.
        -      // We don't care if that is another concurrent GC update, or another mutator update.
        -      atomic_update_oop(fwd, p, o);
        +      // For concurrent update-refs, we cannot reach the state
        +      // with non-forwarded objects in cset.
        +      shenandoah_assert_forwarded(p, obj);
        +      oop resolved = ShenandoahForwarding::forwardee(obj);
        +      shenandoah_assert_not_in_cset(p, resolved);
        +
        +      // We should not be updating the cset regions themselves.
        +      shenandoah_assert_not_in_cset_loc_except(p, !is_in(p));
        +
        +      if (resolved != obj) {
        +        // Either we succeed in updating the reference, or something else gets in our way.
        +        // We don't care if that is another concurrent GC update, or another mutator update.
        +        atomic_update_oop(resolved, p, o);
        +      }
             }
           }
         }
        @@ -300,27 +304,6 @@ inline HeapWord* ShenandoahHeap::allocate_from_gclab(Thread* thread, size_t size
           return allocate_from_gclab_slow(thread, size);
         }
         
        -void ShenandoahHeap::increase_object_age(oop obj, uint additional_age) {
        -  // This operates on new copy of an object. This means that the object's mark-word
        -  // is thread-local and therefore safe to access.
        -  markWord w = obj->mark();
        -  // It is possible that we have copied the object after another thread has
        -  // already successfully completed evacuation. While harmless (we would never
        -  // publish our copy), don't even attempt to modify the age when that
        -  // happens.
        -  if (!w.is_marked()) {
        -    w = w.set_age(MIN2(markWord::max_age, w.age() + additional_age));
        -    obj->set_mark(w);
        -  }
        -}
        -
        -uint ShenandoahHeap::get_object_age(oop obj) {
        -  markWord w = obj->mark();
        -  assert(!w.is_marked(), "must not be forwarded");
        -  assert(w.age() <= markWord::max_age, "Impossible!");
        -  return w.age();
        -}
        -
         inline bool ShenandoahHeap::is_in_active_generation(oop obj) const {
           if (!mode()->is_generational()) {
             // everything is the same single generation
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
        index 8074aac151b..86e3afae81e 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
        @@ -541,7 +541,7 @@ class ShenandoahHeapRegion {
           // installs a self-forward mark on an object in this region. Tested and cleared
           // at the drain phase (degen/full GC entry) and again on region recycle.
           bool has_self_forwards() const { return _has_self_forwards.is_set(); }
        -  void set_has_self_forwards()   { _has_self_forwards.set(); }
        +  void set_has_self_forwards()   { _has_self_forwards.try_set(); }
           void clear_has_self_forwards() { _has_self_forwards.unset(); }
         
         private:
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
        index d1cd6440ccf..7a3a5e89b11 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
        @@ -34,6 +34,7 @@
         #include "gc/shenandoah/shenandoahAsserts.hpp"
         #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp"
         #include "gc/shenandoah/shenandoahClosures.inline.hpp"
        +#include "gc/shenandoah/shenandoahForwarding.inline.hpp"
         #include "gc/shenandoah/shenandoahHeap.inline.hpp"
         #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
         #include "gc/shenandoah/shenandoahOldGeneration.hpp"
        @@ -160,7 +161,7 @@ void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klas
           if (GENERATION == YOUNG || (GENERATION == GLOBAL && region->is_young())) {
             assert(heap->mode()->is_generational(), "Only if generational");
             assert(region->is_young(), "Only for young objects");
        -    const uint age = ShenandoahHeap::get_object_age(obj);
        +    const uint age = ShenandoahForwarding::age(obj);
             ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census();
             CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);)
             NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);)
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp
        index e18e9c48473..b375b0c5d39 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp
        @@ -113,7 +113,7 @@ void ShenandoahNMethod::parse(nmethod* nm, GrowableArray& oops, bool& has_
                   oop* addr = r->oop_addr();
                   shenandoah_assert_correct(addr, value);
                   shenandoah_assert_not_in_cset_except(addr, value, ShenandoahHeap::heap()->cancelled_gc());
        -          shenandoah_assert_not_forwarded(addr, value);
        +          shenandoah_assert_not_forwarded_except(addr, value, ShenandoahHeap::heap()->cancelled_gc());
                   // Non-null immediate oop found. null oops can safely be
                   // ignored since the method will be re-registered if they
                   // are later patched to be non-null.
        diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
        index 43cc7b73792..ddb84f09e59 100644
        --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
        +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
        @@ -117,8 +117,8 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
               oop obj = CompressedOops::decode_raw_not_null(o);
               verify_oop_at_basic(p, obj);
         
        -      if (is_instance_ref_klass(ShenandoahForwarding::klass(obj))) {
        -        obj = ShenandoahForwarding::get_forwardee(obj);
        +      if (ShenandoahForwarding::is_forwarded(obj) && is_instance_ref_klass(ShenandoahForwarding::klass(obj))) {
        +        obj = ShenandoahForwarding::forwardee_raw(obj);
               }
               if (in_generation(obj) && _map->par_mark(obj)) {
                 verify_oop_at(p, obj);
        @@ -201,9 +201,12 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
               }
             }
         
        -    oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
        +    oop fwd = obj;
        +    if (ShenandoahForwarding::is_forwarded(obj)) {
        +      fwd = ShenandoahForwarding::forwardee_raw(obj);
        +    }
         
        -    ShenandoahHeapRegion* fwd_reg = nullptr;
        +    ShenandoahHeapRegion* fwd_reg = obj_reg;
         
             if (obj != fwd) {
               check(ShenandoahAsserts::_safe_oop, obj, _heap->is_in_reserved(fwd),
        @@ -237,11 +240,8 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
               check(ShenandoahAsserts::_safe_oop, obj, (fwd_addr + ShenandoahForwarding::size(fwd)) <= fwd_reg->top(),
                      "Forwardee end should be within the region");
         
        -      oop fwd2 = ShenandoahForwarding::get_forwardee_raw_unchecked(fwd);
        -      check(ShenandoahAsserts::_safe_oop, obj, (fwd == fwd2),
        -             "Double forwarding");
        -    } else {
        -      fwd_reg = obj_reg;
        +      check(ShenandoahAsserts::_safe_oop, obj, !ShenandoahForwarding::is_forwarded(fwd),
        +            "Double forwarding");
             }
         
             // Do additional checks for special objects: their fields can hold metadata as well.
        @@ -288,12 +288,12 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
                 // skip
                 break;
               case ShenandoahVerifier::_verify_forwarded_none: {
        -        check(ShenandoahAsserts::_safe_all, obj, (obj == fwd),
        +        check(ShenandoahAsserts::_safe_all, obj, !ShenandoahForwarding::is_forwarded(obj),
                        "Should not be forwarded");
                 break;
               }
               case ShenandoahVerifier::_verify_forwarded_allow: {
        -        if (obj != fwd) {
        +        if (ShenandoahForwarding::is_real_forwarded(obj)) {
                   check(ShenandoahAsserts::_safe_all, obj, obj_reg != fwd_reg,
                          "Forwardee should be in another region");
                 }
        @@ -313,7 +313,7 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
                 break;
               case ShenandoahVerifier::_verify_cset_forwarded:
                 if (_heap->in_collection_set(obj)) {
        -          check(ShenandoahAsserts::_safe_all, obj, (obj != fwd),
        +          check(ShenandoahAsserts::_safe_all, obj, ShenandoahForwarding::is_forwarded(obj),
                          "Object in collection set, should have forwardee");
                 }
                 break;
        @@ -369,8 +369,11 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
             // oop_iterate() can not deal with forwarded objects, because
             // it needs to load klass(), which may be overridden by the
             // forwarding pointer.
        -    oop fwd = ShenandoahForwarding::get_forwardee_raw(obj);
        -    fwd->oop_iterate(this);
        +    oop resolved = obj;
        +    if (ShenandoahForwarding::is_forwarded(obj)) {
        +      resolved = ShenandoahForwarding::forwardee_raw(obj);
        +    }
        +    resolved->oop_iterate(this);
             _loc = nullptr;
           }
         
        @@ -1269,8 +1272,7 @@ class ShenandoahVerifyNoForwarded : public BasicOopIterateClosure {
               oop obj = CompressedOops::decode_raw_not_null(o);
               ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__);
         
        -      oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
        -      if (obj != fwd) {
        +      if (ShenandoahForwarding::is_forwarded(obj)) {
                 ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr,
                                                  "Verify Roots", "Should not be forwarded", __FILE__, __LINE__);
               }
        @@ -1303,8 +1305,7 @@ class ShenandoahVerifyInToSpaceClosure : public BasicOopIterateClosure {
                         "Verify Roots In To-Space", "Should not be in collection set", __FILE__, __LINE__);
               }
         
        -      oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
        -      if (obj != fwd) {
        +      if (ShenandoahForwarding::is_forwarded(obj)) {
                 ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr,
                         "Verify Roots In To-Space", "Should not be forwarded", __FILE__, __LINE__);
               }
        
        From b41070a002908a947e80670850e90ffa08ae0355 Mon Sep 17 00:00:00 2001
        From: Srinivas Vamsi Parasa 
        Date: Wed, 7 Oct 2026 23:00:23 +0000
        Subject: [PATCH 90/98] 8392046: R30 and R31 should not be SOC on Windows
        
        Reviewed-by: missa, jbhateja, sviswanathan
        ---
         .../x86/gc/shared/barrierSetAssembler_x86.cpp |  3 +++
         src/hotspot/cpu/x86/macroAssembler_x86.cpp    |  5 +++++
         src/hotspot/cpu/x86/vm_version_x86.cpp        | 19 ++++++++++++++++---
         src/hotspot/cpu/x86/vm_version_x86.hpp        |  3 ++-
         src/hotspot/cpu/x86/x86.ad                    | 13 +++++++++++++
         .../foreign/abi/x64/X86_64Architecture.java   | 19 ++++++++++++++++++-
         .../foreign/abi/x64/sysv/CallArranger.java    |  7 +++++--
         .../foreign/abi/x64/windows/CallArranger.java |  7 +++++--
         8 files changed, 67 insertions(+), 9 deletions(-)
        
        diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        index 7e0e53f9dd1..38d2e5f6ae7 100644
        --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        @@ -529,8 +529,11 @@ void SaveLiveRegisters::initialize(BarrierStubC2* stub) {
             caller_saved.insert(OptoReg::as_OptoReg(r27->as_VMReg()));
             caller_saved.insert(OptoReg::as_OptoReg(r28->as_VMReg()));
             caller_saved.insert(OptoReg::as_OptoReg(r29->as_VMReg()));
        +#ifndef _WINDOWS
        +    // On Windows, r30 and r31 are nonvolatile per the x64 ABI.
             caller_saved.insert(OptoReg::as_OptoReg(r30->as_VMReg()));
             caller_saved.insert(OptoReg::as_OptoReg(r31->as_VMReg()));
        +#endif
           }
         
           int gp_spill_size = 0;
        diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        index 346acaab432..cd0a8add1f3 100644
        --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        @@ -3627,7 +3627,12 @@ RegSet MacroAssembler::call_clobbered_gp_registers() {
         #endif
           regs += RegSet::range(r8, r11);
           if (UseAPX) {
        +#ifdef _WINDOWS
        +    // On Windows, r30 and r31 are nonvolatile per the x64 ABI.
        +    regs += RegSet::range(r16, r29);
        +#else
             regs += RegSet::range(r16, as_Register(Register::number_of_registers - 1));
        +#endif
           }
           return regs;
         }
        diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
        index 7b08333a8ed..7000a04b5e3 100644
        --- a/src/hotspot/cpu/x86/vm_version_x86.cpp
        +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
        @@ -100,16 +100,21 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
           address clear_apx_test_state() {
         #   define __ _masm->
             address start = __ pc();
        -    // EGPRs are call clobbered registers, Explicit clearing of r16 and r31 during signal
        -    // handling guarantees that preserved register values post signal handling were
        -    // re-instantiated by operating system and not because they were not modified externally.
        +    // EGPRs are call clobbered registers. Explicit clearing of r16 and the last volatile EGPR
        +    // (r31 for non-Windows and r29 for Windows) during signal handling guarantees that
        +    // the preserved register values after signal handling were re-instantiated by the
        +    // operating system and not because they were not modified externally.
         
             bool save_apx = UseAPX;
             VM_Version::set_apx_cpuFeatures();
             UseAPX = true;
             // EGPR state save/restoration.
             __ mov64(r16, 0L);
        +#ifdef _WINDOWS
        +    __ mov64(r29, 0L);
        +#else
             __ mov64(r31, 0L);
        +#endif
             UseAPX = save_apx;
             VM_Version::clean_cpuFeatures();
             __ ret(0);
        @@ -442,7 +447,11 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
             VM_Version::set_apx_cpuFeatures();
             UseAPX = true;
             __ mov64(r16, VM_Version::egpr_test_value());
        +#ifdef _WINDOWS
        +    __ mov64(r29, VM_Version::egpr_test_value());
        +#else
             __ mov64(r31, VM_Version::egpr_test_value());
        +#endif
             __ xorl(rsi, rsi);
             VM_Version::set_cpuinfo_segv_addr_apx(__ pc());
             // Generate SEGV
        @@ -451,7 +460,11 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
             VM_Version::set_cpuinfo_cont_addr_apx(__ pc());
             __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_save_offset())));
             __ movq(Address(rsi, 0), r16);
        +#ifdef _WINDOWS
        +    __ movq(Address(rsi, 8), r29);
        +#else
             __ movq(Address(rsi, 8), r31);
        +#endif
         
             //
             // Query CPUID 0xD.19 for APX XSAVE offset
        diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp
        index 19ca2d56508..d1e3185be4b 100644
        --- a/src/hotspot/cpu/x86/vm_version_x86.hpp
        +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp
        @@ -677,7 +677,8 @@ class VM_Version : public Abstract_VM_Version {
             int          zmm_save[16*4]; // Save zmm0, zmm7, zmm8, zmm31
         
             // Space to save apx registers after signal handle
        -    jlong        apx_save[2]; // Save r16 and r31
        +    jlong        apx_save[2]; // Save r16 and the last volatile EGPR
        +                              // (r29 on Windows, r31 on non-Windows)
         
             // cpuid function 0xD, subleaf 19 (APX extended state)
             uint32_t          apx_xstate_size;          // EAX: size of APX state (128)
        diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
        index 45d91a9949c..cbbbc3e5e72 100644
        --- a/src/hotspot/cpu/x86/x86.ad
        +++ b/src/hotspot/cpu/x86/x86.ad
        @@ -170,12 +170,25 @@ reg_def R28_H(SOC, SOC, Op_RegI, 28, r28->as_VMReg()->next());
         reg_def R29  (SOC, SOC, Op_RegI, 29, r29->as_VMReg());
         reg_def R29_H(SOC, SOC, Op_RegI, 29, r29->as_VMReg()->next());
         
        +#ifdef _WIN64
        +
        +// On Win64, R30 and R31 are nonvolatile (callee saved)
        +reg_def R30  (SOC, SOE, Op_RegI, 30, r30->as_VMReg());
        +reg_def R30_H(SOC, SOE, Op_RegI, 30, r30->as_VMReg()->next());
        +
        +reg_def R31  (SOC, SOE, Op_RegI, 31, r31->as_VMReg());
        +reg_def R31_H(SOC, SOE, Op_RegI, 31, r31->as_VMReg()->next());
        +
        +#else
        +
         reg_def R30  (SOC, SOC, Op_RegI, 30, r30->as_VMReg());
         reg_def R30_H(SOC, SOC, Op_RegI, 30, r30->as_VMReg()->next());
         
         reg_def R31  (SOC, SOC, Op_RegI, 31, r31->as_VMReg());
         reg_def R31_H(SOC, SOC, Op_RegI, 31, r31->as_VMReg()->next());
         
        +#endif
        +
         // Floating Point Registers
         
         // Specify priority of register selection within phases of register
        diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java
        index 03fc39d0670..6cad947c092 100644
        --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java
        +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/X86_64Architecture.java
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -93,6 +93,23 @@ public static class Regs { // break circular dependency
                 public static final VMStorage r13 = integerRegister(13, "r13");
                 public static final VMStorage r14 = integerRegister(14, "r14");
                 public static final VMStorage r15 = integerRegister(15, "r15");
        +        // APX extended GPRs (EGPRs)
        +        public static final VMStorage r16 = integerRegister(16, "r16");
        +        public static final VMStorage r17 = integerRegister(17, "r17");
        +        public static final VMStorage r18 = integerRegister(18, "r18");
        +        public static final VMStorage r19 = integerRegister(19, "r19");
        +        public static final VMStorage r20 = integerRegister(20, "r20");
        +        public static final VMStorage r21 = integerRegister(21, "r21");
        +        public static final VMStorage r22 = integerRegister(22, "r22");
        +        public static final VMStorage r23 = integerRegister(23, "r23");
        +        public static final VMStorage r24 = integerRegister(24, "r24");
        +        public static final VMStorage r25 = integerRegister(25, "r25");
        +        public static final VMStorage r26 = integerRegister(26, "r26");
        +        public static final VMStorage r27 = integerRegister(27, "r27");
        +        public static final VMStorage r28 = integerRegister(28, "r28");
        +        public static final VMStorage r29 = integerRegister(29, "r29");
        +        public static final VMStorage r30 = integerRegister(30, "r30");
        +        public static final VMStorage r31 = integerRegister(31, "r31");
         
                 public static final VMStorage xmm0 = vectorRegister(0, "xmm0");
                 public static final VMStorage xmm1 = vectorRegister(1, "xmm1");
        diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java
        index 70b4c9f69f2..6b8db51ba46 100644
        --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java
        +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java
        @@ -1,5 +1,5 @@
         /*
        - *  Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
        + *  Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
          *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          *  This code is free software; you can redistribute it and/or modify it
        @@ -76,7 +76,10 @@ public class CallArranger {
                 new VMStorage[] { rax, rdx },
                 new VMStorage[] { xmm0, xmm1 },
                 2,
        -        new VMStorage[] { r10, r11 },
        +        new VMStorage[] { r10, r11,
        +                          // APX EGPRs r16-r31 are caller-saved.
        +                          r16, r17, r18, r19, r20, r21, r22, r23,
        +                          r24, r25, r26, r27, r28, r29, r30, r31 },
                 new VMStorage[] { xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
                                   xmm16, xmm17, xmm18, xmm19, xmm20, xmm21, xmm22, xmm23,
                                   xmm24, xmm25, xmm26, xmm27, xmm28, xmm29, xmm30, xmm31 },
        diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java
        index 1c15d5b0698..c542d76baf2 100644
        --- a/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java
        +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/x64/windows/CallArranger.java
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -67,7 +67,10 @@ public class CallArranger {
                 new VMStorage[] { rax },
                 new VMStorage[] { xmm0 },
                 0,
        -        new VMStorage[] { rax, r10, r11 },
        +        new VMStorage[] { rax, r10, r11,
        +                          // APX EGPRs r16-r29 are caller-saved as per Windows x64 ABI.
        +                          r16, r17, r18, r19, r20, r21, r22, r23,
        +                          r24, r25, r26, r27, r28, r29},
                 new VMStorage[] { xmm4, xmm5,
                                   xmm16, xmm17, xmm18, xmm19, xmm20, xmm21, xmm22, xmm23,
                                   xmm24, xmm25, xmm26, xmm27, xmm28, xmm29, xmm30, xmm31 },
        
        From 3eb38245a7151edf4ef80a76cd41ab27136df0a1 Mon Sep 17 00:00:00 2001
        From: Albert Mingkun Yang 
        Date: Thu, 8 Oct 2026 03:49:48 +0000
        Subject: [PATCH 91/98] 8393681: Remove redundant needs_explicit_null_check
         guard in PhaseCFG::implicit_null_check
        
        Reviewed-by: rcastanedalo, qamai
        ---
         src/hotspot/share/opto/lcm.cpp | 5 +----
         1 file changed, 1 insertion(+), 4 deletions(-)
        
        diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp
        index 4a654954b9f..833aa348b47 100644
        --- a/src/hotspot/share/opto/lcm.cpp
        +++ b/src/hotspot/share/opto/lcm.cpp
        @@ -114,10 +114,7 @@ void PhaseCFG::ensure_node_is_at_block_or_above(Node* n, Block* b) {
         // The val is the pointer being checked for nullness or
         // decodeHeapOop_not_null node if it did not fold into address.
         void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allowed_reasons) {
        -  // Assume if null check need for 0 offset then always needed
        -  // Intel solaris doesn't support any null checks yet and no
        -  // mechanism exists (yet) to set the switches at an os_cpu level
        -  if( !ImplicitNullChecks || MacroAssembler::needs_explicit_null_check(0)) return;
        +  if (!ImplicitNullChecks) return;
         
           // Make sure the ptr-is-null path appears to be uncommon!
           float f = block->end()->as_MachIf()->_prob;
        
        From 2872dd228cb6737841e016d5dc97b341a7fe9549 Mon Sep 17 00:00:00 2001
        From: David Carlier 
        Date: Thu, 8 Oct 2026 06:17:16 +0000
        Subject: [PATCH 92/98] 8382070: ZGC: assert(value <= max_power_of_2())
         failed: Overflowing maximum allowed power of two with 0xf2721721ca000000
        
        Reviewed-by: stefank, eosterlund
        ---
         src/hotspot/share/gc/z/zAddress.cpp           | 15 ++++-
         .../jtreg/gc/z/TestZHugeMaxHeapSize.java      | 57 +++++++++++++++++++
         2 files changed, 69 insertions(+), 3 deletions(-)
         create mode 100644 test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java
        
        diff --git a/src/hotspot/share/gc/z/zAddress.cpp b/src/hotspot/share/gc/z/zAddress.cpp
        index 98779419219..507d3d5902f 100644
        --- a/src/hotspot/share/gc/z/zAddress.cpp
        +++ b/src/hotspot/share/gc/z/zAddress.cpp
        @@ -1,5 +1,5 @@
         /*
        - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
        + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
          * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
          *
          * This code is free software; you can redistribute it and/or modify it
        @@ -153,7 +153,16 @@ void ZGlobalsPointers::flip_old_relocate_start() {
         
         size_t ZGlobalsPointers::min_address_offset_request() {
           // See ZVirtualMemoryReserver for logic around setting up the heap for NUMA
        -  const size_t desired_for_heap = MaxHeapSize * ZVirtualToPhysicalRatio;
           const size_t desired_for_numa_multiplier = ZNUMA::count() > 1 ? 2 : 1;
        -  return round_up_power_of_2(desired_for_heap * desired_for_numa_multiplier);
        +  const size_t desired_heap_multiplier = ZVirtualToPhysicalRatio * desired_for_numa_multiplier;
        +
        +  // Saturate to avoid overflow with a huge MaxHeapSize. The platform code
        +  // clamps the request and ZGlobalsPointers::initialize() then rejects the
        +  // heap as too large.
        +  const size_t max_desired_for_heap = max_power_of_2() / desired_heap_multiplier;
        +  const size_t desired_for_heap = (MaxHeapSize > max_desired_for_heap)
        +      ? max_power_of_2()
        +      : MaxHeapSize * desired_heap_multiplier;
        +
        +  return round_up_power_of_2(desired_for_heap);
         }
        diff --git a/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java b/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java
        new file mode 100644
        index 00000000000..39273555dea
        --- /dev/null
        +++ b/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java
        @@ -0,0 +1,57 @@
        +/*
        + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
        + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        + *
        + * This code is free software; you can redistribute it and/or modify it
        + * under the terms of the GNU General Public License version 2 only, as
        + * published by the Free Software Foundation.
        + *
        + * This code is distributed in the hope that it will be useful, but WITHOUT
        + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        + * version 2 for more details (a copy is included in the LICENSE file that
        + * accompanied this code).
        + *
        + * You should have received a copy of the GNU General Public License version
        + * 2 along with this work; if not, write to the Free Software Foundation,
        + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
        + *
        + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
        + * or visit www.oracle.com if you need additional information or have any
        + * questions.
        + */
        +
        +package gc.z;
        +
        +/**
        + * @test
        + * @bug 8382070
        + * @summary A max heap size large enough to overflow the ZGC address offset
        + *          request must be rejected with an error message, not crash the VM
        + * @requires vm.gc.Z
        + * @library /test/lib
        + * @run driver gc.z.TestZHugeMaxHeapSize
        + */
        +
        +import jdk.test.lib.process.ProcessTools;
        +
        +public class TestZHugeMaxHeapSize {
        +
        +    private static void test(String option) throws Exception {
        +        ProcessTools.executeTestJava("-XX:+UseZGC", option, "-version")
        +                .outputTo(System.out)
        +                .errorTo(System.out)
        +                .shouldHaveExitValue(1)
        +                .shouldContain("Java heap too large");
        +    }
        +
        +    public static void main(String[] args) throws Exception {
        +        // 2^60, large enough to overflow MaxHeapSize * ZVirtualToPhysicalRatio
        +        // past the largest power of two, with or without NUMA
        +        test("-XX:MaxHeapSize=1152921504606846976");
        +        // Value found by the flag fuzzer
        +        test("-XX:MaxHeapSize=17232779273145073716");
        +        // MinHeapSize larger than the default max heap size raises MaxHeapSize
        +        test("-XX:MinHeapSize=17232779273145073716");
        +    }
        +}
        
        From 90588ae548b58ca611a75ccf6c069c86574833b7 Mon Sep 17 00:00:00 2001
        From: Stefan Karlsson 
        Date: Thu, 8 Oct 2026 06:18:05 +0000
        Subject: [PATCH 93/98] 8393707: Simplify interpreter write_flat_field and
         supporting code
        
        Reviewed-by: aboldtch, matsaave, heidinga
        ---
         .../gc/shared/barrierSetAssembler_aarch64.cpp |  14 --
         .../gc/shared/barrierSetAssembler_aarch64.hpp |   3 -
         .../cpu/aarch64/interp_masm_aarch64.cpp       |  19 +-
         .../cpu/aarch64/interp_masm_aarch64.hpp       |   4 +-
         .../cpu/aarch64/macroAssembler_aarch64.cpp    |  21 --
         .../cpu/aarch64/macroAssembler_aarch64.hpp    |   4 -
         .../cpu/aarch64/templateTable_aarch64.cpp     |   5 +-
         .../ppc/gc/shared/barrierSetAssembler_ppc.cpp |  14 --
         .../ppc/gc/shared/barrierSetAssembler_ppc.hpp |   3 -
         src/hotspot/cpu/ppc/interp_masm_ppc.hpp       |   2 +-
         src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp    |  16 +-
         src/hotspot/cpu/ppc/macroAssembler_ppc.cpp    |  20 --
         src/hotspot/cpu/ppc/macroAssembler_ppc.hpp    |   4 -
         src/hotspot/cpu/ppc/templateTable_ppc_64.cpp  |   4 +-
         .../gc/shared/barrierSetAssembler_riscv.cpp   |  14 --
         .../gc/shared/barrierSetAssembler_riscv.hpp   |   3 -
         src/hotspot/cpu/riscv/interp_masm_riscv.cpp   |  20 +-
         src/hotspot/cpu/riscv/interp_masm_riscv.hpp   |   5 +-
         .../cpu/riscv/macroAssembler_riscv.cpp        |  22 ---
         .../cpu/riscv/macroAssembler_riscv.hpp        |   4 -
         src/hotspot/cpu/riscv/templateTable_riscv.cpp |   5 +-
         .../gc/shared/barrierSetAssembler_s390.cpp    |  15 --
         .../gc/shared/barrierSetAssembler_s390.hpp    |   3 -
         src/hotspot/cpu/s390/interp_masm_s390.cpp     |  27 +--
         src/hotspot/cpu/s390/interp_masm_s390.hpp     |   5 +-
         src/hotspot/cpu/s390/macroAssembler_s390.cpp  |  28 ---
         src/hotspot/cpu/s390/macroAssembler_s390.hpp  |   3 -
         src/hotspot/cpu/s390/templateTable_s390.cpp   |   8 +-
         .../x86/gc/shared/barrierSetAssembler_x86.cpp |  14 --
         .../x86/gc/shared/barrierSetAssembler_x86.hpp |   3 -
         src/hotspot/cpu/x86/interp_masm_x86.cpp       |  18 +-
         src/hotspot/cpu/x86/interp_masm_x86.hpp       |   5 +-
         src/hotspot/cpu/x86/macroAssembler_x86.cpp    |  28 ---
         src/hotspot/cpu/x86/macroAssembler_x86.hpp    |   4 -
         src/hotspot/cpu/x86/templateTable_x86.cpp     |   4 +-
         .../share/gc/shared/barrierSetRuntime.cpp     |  41 ----
         .../share/gc/shared/barrierSetRuntime.hpp     |  39 ----
         .../share/interpreter/interpreterRuntime.cpp  |   9 +
         .../share/interpreter/interpreterRuntime.hpp  |   1 +
         src/hotspot/share/oops/valuePayload.hpp       |  41 +---
         .../share/oops/valuePayload.inline.hpp        | 185 ++++--------------
         41 files changed, 88 insertions(+), 599 deletions(-)
         delete mode 100644 src/hotspot/share/gc/shared/barrierSetRuntime.cpp
         delete mode 100644 src/hotspot/share/gc/shared/barrierSetRuntime.hpp
        
        diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
        index 294e65936a9..c8629136bdd 100644
        --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
        +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
        @@ -27,7 +27,6 @@
         #include "gc/shared/barrierSet.hpp"
         #include "gc/shared/barrierSetAssembler.hpp"
         #include "gc/shared/barrierSetNMethod.hpp"
        -#include "gc/shared/barrierSetRuntime.hpp"
         #include "gc/shared/collectedHeap.hpp"
         #include "interpreter/interp_masm.hpp"
         #include "memory/universe.hpp"
        @@ -137,19 +136,6 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators
           }
         }
         
        -void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                                          Register src, Register dst, Register value_field_info) {
        -  // flat_field_copy implementation is fairly complex, and there are not any
        -  // "short-cuts" to be made from asm. What there is, appears to have the same
        -  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
        -  // of hand-rolled instructions...
        -  if (decorators & IS_DEST_UNINITIALIZED) {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info);
        -  } else {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info);
        -  }
        -}
        -
         void BarrierSetAssembler::copy_load_at(MacroAssembler* masm,
                                                DecoratorSet decorators,
                                                BasicType type,
        diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
        index fb6405629be..c8ed7941983 100644
        --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
        +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
        @@ -99,9 +99,6 @@ class BarrierSetAssembler: public CHeapObj {
           virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                 Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
         
        -  virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                               Register src, Register dst, Register value_field_info);
        -
           virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
                                                      Register obj, Register tmp, Label& slowpath);
         
        diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
        index 43e7ad64c94..c2bf73092cc 100644
        --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
        +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
        @@ -214,10 +214,8 @@ void InterpreterMacroAssembler::read_flat_field(Register entry, Register obj) {
           call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_flat_field), obj, entry);
         }
         
        -void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_offset,
        -                                                 Register tmp1, Register tmp2,
        -                                                 Register obj) {
        -  assert_different_registers(entry, field_offset, tmp1, tmp2, obj);
        +void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1, Register tmp2, Register obj) {
        +  assert_different_registers(entry, tmp1, tmp2, obj);
           Label slow_path, done;
         
           load_unsigned_byte(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::flags_offset())));
        @@ -225,17 +223,8 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_
         
           null_check(r0); // FIXME JDK-8341120
         
        -  add(obj, obj, field_offset);
        -
        -  load_klass(tmp1, r0, tmp2);
        -  payload_address(r0, r0, tmp1);
        -
        -  Register vfi = field_offset;
        -  load_unsigned_short(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset())));
        -  ldr(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset())));
        -  value_field_info(tmp2, tmp1, vfi);
        -
        -  flat_field_copy(IN_HEAP, r0, obj, vfi);
        +  mov(tmp1, r0);
        +  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::write_null_free_flat_field), obj, tmp1, entry);
           b(done);
         
           bind(slow_path);
        diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
        index 0c3cee6178b..593488ba69f 100644
        --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
        +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
        @@ -164,9 +164,7 @@ class InterpreterMacroAssembler: public MacroAssembler {
           //     will return new instance via the same reg
           void read_flat_field(Register entry, Register obj);
         
        -  void write_flat_field(Register entry, Register field_offset,
        -                        Register tmp1, Register tmp2,
        -                        Register obj);
        +  void write_flat_field(Register entry, Register tmp1, Register tmp2, Register obj);
         
           // load cpool->resolved_references(index);
           void load_resolved_reference_at_index(Register result, Register index, Register tmp = r5);
        diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
        index 3069df838f8..e3731a13609 100644
        --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
        +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
        @@ -5674,12 +5674,6 @@ void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators,
           }
         }
         
        -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst,
        -                                     Register value_field_info) {
        -  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
        -  bs->flat_field_copy(this, decorators, src, dst, value_field_info);
        -}
        -
         void MacroAssembler::payload_offset(Register value_klass, Register offset) {
           ldr(offset, Address(value_klass, ValueKlass::adr_members_offset()));
           ldrw(offset, Address(offset, ValueKlass::payload_offset_offset()));
        @@ -5810,21 +5804,6 @@ void MacroAssembler::verify_tlab() {
         #endif
         }
         
        -void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) {
        -  assert_different_registers(holder_klass, index, vfi);
        -  ValueFieldInfo array[2];
        -  int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements
        -  if (is_power_of_2(size)) {
        -    lsl(index, index, log2i_exact(size)); // Scale index by power of 2
        -  } else {
        -    mov(vfi, size);
        -    mul(index, index, vfi); // Scale the index to be the entry index * array_element_size
        -  }
        -  ldr(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset()));
        -  add(vfi, vfi, Array::base_offset_in_bytes());
        -  lea(vfi, Address(vfi, index));
        -}
        -
         // Writes to stack successive pages until offset reached to check for
         // stack overflow + shadow pages.  This clobbers tmp.
         void MacroAssembler::bang_stack_size(Register size, Register tmp) {
        diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
        index 6b192bddf43..3aeef85124b 100644
        --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
        +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
        @@ -980,8 +980,6 @@ class MacroAssembler: public Assembler {
           void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val,
                                Register tmp1, Register tmp2, Register tmp3);
         
        -  void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info);
        -
           // value type data payload offsets...
           void payload_offset(Register value_klass, Register offset);
           void payload_address(Register oop, Register data, Register value_klass);
        @@ -1055,8 +1053,6 @@ class MacroAssembler: public Assembler {
           );
           void verify_tlab();
         
        -  void value_field_info(Register holder_klass, Register index, Register vfi);
        -
           // interface method calling
           void lookup_interface_method(Register recv_klass,
                                        Register intf_klass,
        diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
        index 836c1b55892..f1ab668d211 100644
        --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
        +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
        @@ -3066,7 +3066,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr
                 __ b(rewrite_value);
                 __ bind(is_flat);
                 pop_and_check_object(r7);
        -        __ write_flat_field(cache, off, index, flags, r7);
        +        __ write_flat_field(cache, index, flags, r7);
                 __ bind(rewrite_value);
                 if (rc == may_rewrite) {
                   patch_bytecode(Bytecodes::_fast_vputfield, bc, r19, true, byte_no);
        @@ -3295,9 +3295,8 @@ void TemplateTable::fast_storefield(TosState state)
               __ bind(is_flat);
               __ load_field_entry(r4, r5);
               // Re-shuffle registers because of VM calls calling convention
        -      __ mov(r19, r1);
               __ mov(r7, r2);
        -      __ write_flat_field(r4, r19, r6, r8, r7);
        +      __ write_flat_field(r4, r6, r8, r7);
               __ bind(done);
             }
             break;
        diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
        index 2527cb30f84..0bee10a1742 100644
        --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
        +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
        @@ -27,7 +27,6 @@
         #include "classfile/classLoaderData.hpp"
         #include "gc/shared/barrierSetAssembler.hpp"
         #include "gc/shared/barrierSetNMethod.hpp"
        -#include "gc/shared/barrierSetRuntime.hpp"
         #include "interpreter/interp_masm.hpp"
         #include "oops/compressedOops.hpp"
         #include "runtime/jniHandles.hpp"
        @@ -116,19 +115,6 @@ void BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators,
           }
         }
         
        -void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                                          Register src, Register dst, Register value_field_info) {
        -  // flat_field_copy implementation is fairly complex, and there are not any
        -  // "short-cuts" to be made from asm. What there is, appears to have the same
        -  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
        -  // of hand-rolled instructions...
        -  if (decorators & IS_DEST_UNINITIALIZED) {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info);
        -  } else {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info);
        -  }
        -}
        -
         // Generic implementation. GCs can provide an optimized one.
         void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value,
                                                   Register tmp1, Register tmp2,
        diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
        index f24fe11c833..8e8c4d41c3b 100644
        --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
        +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
        @@ -60,9 +60,6 @@ class BarrierSetAssembler: public CHeapObj {
                                Register tmp1, Register tmp2,
                                MacroAssembler::PreservationLevel preservation_level, Label *L_handle_null = nullptr);
         
        -  virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                               Register src, Register dst, Register value_field_info);
        -
           virtual void resolve_jobject(MacroAssembler* masm, Register value,
                                        Register tmp1, Register tmp2,
                                        MacroAssembler::PreservationLevel preservation_level);
        diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
        index 90f509bdfa9..31c23330b38 100644
        --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
        +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
        @@ -293,7 +293,7 @@ class InterpreterMacroAssembler: public MacroAssembler {
           //   - input holder object via "obj", which must be r0,
           //     will return new instance via the same reg
           void read_flat_field(Register entry, Register obj);
        -  void write_flat_field(Register entry, Register tmp1, Register tmp2, Register obj, Register field_offset, Register value);
        +  void write_flat_field(Register entry, Register tmp1, Register tmp2, Register obj, Register value);
         };
         
         #endif // CPU_PPC_INTERP_MASM_PPC_HPP
        diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
        index 5dd5d830252..4226b3dbdbc 100644
        --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
        +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
        @@ -2435,8 +2435,8 @@ void InterpreterMacroAssembler::read_flat_field(Register entry, Register obj) {
         }
         
         void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1, Register tmp2,
        -                                                 Register obj, Register field_offset, Register value) {
        -  assert_different_registers(entry, field_offset, tmp1, tmp2, obj, value);
        +                                                 Register obj, Register value) {
        +  assert_different_registers(entry, tmp1, tmp2, obj, value);
           Label slow_path, done;
         
           lbz(tmp1, in_bytes(ResolvedFieldEntry::flags_offset()), entry);
        @@ -2444,17 +2444,7 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1,
         
           null_check_throw(value, -1, tmp1);
         
        -  add(obj, obj, field_offset);
        -
        -  load_klass(tmp1, value);
        -  payload_address(value, value, tmp1, tmp2);
        -
        -  Register vfi = field_offset;
        -  lhz(tmp1, in_bytes(ResolvedFieldEntry::field_index_offset()), entry);
        -  ld(tmp2, in_bytes(ResolvedFieldEntry::field_holder_offset()), entry);
        -  value_field_info(tmp2, tmp1, vfi);
        -
        -  flat_field_copy(IN_HEAP, value, obj, vfi);
        +  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::write_null_free_flat_field), obj, value, entry);
           b(done);
         
           bind(slow_path);
        diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
        index a9b2eeb2382..c42cf60a1a5 100644
        --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
        +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
        @@ -3374,11 +3374,6 @@ void MacroAssembler::load_metadata(Register dst, Register src) {
           }
         }
         
        -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info) {
        -  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
        -  bs->flat_field_copy(this, decorators, src, dst, value_field_info);
        -}
        -
         void MacroAssembler::payload_offset(Register value_klass, Register offset) {
           ld(offset, in_bytes(ValueKlass::adr_members_offset()), value_klass);
           lwz(offset, in_bytes(ValueKlass::payload_offset_offset()), offset);
        @@ -3390,21 +3385,6 @@ void MacroAssembler::payload_address(Register oop, Register data, Register value
           add(data, oop, t1);
         }
         
        -void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) {
        -  assert_different_registers(holder_klass, index, vfi);
        -  ValueFieldInfo array[2];
        -  int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements
        -  if (is_power_of_2(size)) {
        -    sldi(index, index, log2i_exact(size)); // Scale index by power of 2
        -  } else {
        -    mulld(index, index, size); // Scale the index to be the entry index * array_element_size
        -  }
        -  ld(vfi, InstanceKlass::value_field_info_array_offset(), holder_klass);
        -  addi(vfi, vfi, Array::base_offset_in_bytes());
        -  add(vfi, vfi, index);
        -}
        -
        -
         // Clear Array
         // For very short arrays. tmp == R0 is allowed.
         void MacroAssembler::clear_memory_unrolled(Register base_ptr, int cnt_dwords, Register tmp, int offset) {
        diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
        index 41c40ff0f81..5a65f91f1ff 100644
        --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
        +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
        @@ -844,10 +844,6 @@ class MacroAssembler: public Assembler {
         
           void load_metadata(Register dst, Register src);
         
        -  void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info);
        -
        -  void value_field_info(Register holder_klass, Register index, Register vfi);
        -
           // value type data payload offsets...
           void payload_offset(Register value_klass, Register offset);
           void payload_address(Register oop, Register data, Register value_klass, Register t1);
        diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
        index 74f0d76504f..5ee4e94afe9 100644
        --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
        +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
        @@ -3238,7 +3238,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr
         
               __ bind(is_flat);
               pop_and_check_object(Rclass_or_obj);
        -      __ write_flat_field(Rcache, Rscratch, Rscratch2, Rclass_or_obj, Roffset, R17_tos);
        +      __ write_flat_field(Rcache, Rscratch, Rscratch2, Rclass_or_obj, R17_tos);
               __ bind(rewrite_value);
               if (rc == may_rewrite) {
                 patch_bytecode(Bytecodes::_fast_vputfield, Rbc, Rscratch, true, byte_no);
        @@ -3322,7 +3322,7 @@ void TemplateTable::fast_storefield(TosState state) {
               do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, IN_HEAP);
               __ b(done);
               __ bind(is_flat);
        -      __ write_flat_field(Rcache, Rscratch, Rscratch2, Rclass_or_obj, Roffset, R17_tos);
        +      __ write_flat_field(Rcache, Rscratch, Rscratch2, Rclass_or_obj, R17_tos);
               __ bind(done);
               break;
             }
        diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
        index cc1581942fd..26b71ec6835 100644
        --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
        +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
        @@ -28,7 +28,6 @@
         #include "gc/shared/barrierSet.hpp"
         #include "gc/shared/barrierSetAssembler.hpp"
         #include "gc/shared/barrierSetNMethod.hpp"
        -#include "gc/shared/barrierSetRuntime.hpp"
         #include "gc/shared/collectedHeap.hpp"
         #include "interpreter/interp_masm.hpp"
         #include "memory/universe.hpp"
        @@ -136,19 +135,6 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators
         
         }
         
        -void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                                          Register src, Register dst, Register value_field_info) {
        -  // flat_field_copy implementation is fairly complex, and there are not any
        -  // "short-cuts" to be made from asm. What there is, appears to have the same
        -  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
        -  // of hand-rolled instructions...
        -  if (decorators & IS_DEST_UNINITIALIZED) {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info);
        -  } else {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info);
        -  }
        -}
        -
         void BarrierSetAssembler::copy_load_at(MacroAssembler* masm,
                                                DecoratorSet decorators,
                                                BasicType type,
        diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
        index 572a9574bab..4a14fa65e78 100644
        --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
        +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
        @@ -75,9 +75,6 @@ class BarrierSetAssembler: public CHeapObj {
           virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                 Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
         
        -  virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                               Register src, Register dst, Register value_field_info);
        -
           virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
                                                      Register obj, Register tmp, Label& slowpath);
         
        diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
        index 99690c3c846..3204f081db6 100644
        --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
        +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
        @@ -1870,10 +1870,9 @@ void InterpreterMacroAssembler::read_flat_field(Register entry, Register obj) {
           call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_flat_field), obj, entry);
         }
         
        -void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_offset,
        -                                                 Register tmp1, Register tmp2,
        -                                                 Register obj) {
        -  assert_different_registers(entry, field_offset, tmp1, tmp2, obj);
        +void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1,
        +                                                 Register tmp2, Register obj) {
        +  assert_different_registers(entry, tmp1, tmp2, obj);
           Label slow_path, done;
         
           load_unsigned_byte(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::flags_offset())));
        @@ -1881,17 +1880,8 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_
         
           null_check(x10); // FIXME JDK-8341120
         
        -  add(obj, obj, field_offset);
        -
        -  load_klass(tmp1, x10);
        -  payload_address(x10, x10, tmp1);
        -
        -  Register vfi = field_offset;
        -  load_unsigned_short(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset())));
        -  ld(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset())));
        -  value_field_info(tmp2, tmp1, vfi);
        -
        -  flat_field_copy(IN_HEAP, x10, obj, vfi);
        +  mv(tmp1, x10);
        +  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::write_null_free_flat_field), obj, tmp1, entry);
           j(done);
         
           bind(slow_path);
        diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
        index 702538a5b88..21e34159a49 100644
        --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
        +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
        @@ -168,9 +168,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
           //   - assumes holder_klass and valueKlass field klass have both been resolved
           void read_flat_field(Register entry, Register obj);
         
        -  void write_flat_field(Register entry, Register field_offset,
        -                        Register tmp1, Register tmp2,
        -                        Register obj);
        +  void write_flat_field(Register entry, Register tmp1,
        +                        Register tmp2, Register obj);
         
           // Load cpool->resolved_references(index).
           void load_resolved_reference_at_index(Register result, Register index, Register tmp = x15);
        diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
        index f74845a8880..9c383b6ddd5 100644
        --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
        +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
        @@ -3803,28 +3803,6 @@ void MacroAssembler::mov_metadata(Register dst, Metadata* obj, Register tmp) {
           movptr(dst, Address((address)obj, rspec), tmp);
         }
         
        -void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) {
        -  assert_different_registers(holder_klass, index, vfi);
        -  ValueFieldInfo array[2];
        -  int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements
        -  if (is_power_of_2(size)) {
        -    slli(index, index, log2i_exact(size)); // Scale index by power of 2
        -  } else {
        -    mv(vfi, size);
        -    mul(index, index, vfi); // Scale the index to be the entry index * array_element_size
        -  }
        -  ld(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset()));
        -  add(vfi, vfi, Array::base_offset_in_bytes());
        -  add(vfi, vfi, index);
        -  la(vfi, Address(vfi));
        -}
        -
        -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst,
        -                                     Register value_field_info) {
        -  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
        -  bs->flat_field_copy(this, decorators, src, dst, value_field_info);
        -}
        -
         void MacroAssembler::payload_offset(Register value_klass, Register offset) {
           ld(offset, Address(value_klass, ValueKlass::adr_members_offset()));
           lwu(offset, Address(offset, ValueKlass::payload_offset_offset()));
        diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
        index 4b403a14326..bd736a985fd 100644
        --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
        +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
        @@ -270,10 +270,6 @@ class MacroAssembler: public Assembler {
           // Check array klass layout helper for flat or null-free arrays...
           void test_flat_array_layout(Register lh, Label& is_flat_array);
         
        -  void value_field_info(Register holder_klass, Register index, Register vfi);
        -
        -  void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info);
        -
           // value type data payload offsets...
           void payload_offset(Register value_klass, Register offset);
           void payload_address(Register oop, Register data, Register value_klass);
        diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
        index ae268a73411..7bffae965f4 100644
        --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp
        +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
        @@ -3025,7 +3025,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr
                 __ j(rewrite_value);
                 __ bind(is_flat);
                 pop_and_check_object(x17);
        -        __ write_flat_field(cache, off, index, flags, x17);
        +        __ write_flat_field(cache, index, flags, x17);
                 __ bind(rewrite_value);
                 if (rc == may_rewrite) {
                   patch_bytecode(Bytecodes::_fast_vputfield, bc, x9, true, byte_no);
        @@ -3285,9 +3285,8 @@ void TemplateTable::fast_storefield(TosState state) {
                 __ bind(is_flat);
                 __ load_field_entry(x14, x13);
                 // Re-shuffle registers because of VM calls calling convention
        -        __ mv(x9, x11);
                 __ mv(x17, x12);
        -        __ write_flat_field(x14, x9, x16, x18, x17);
        +        __ write_flat_field(x14, x16, x18, x17);
                 __ bind(done);
               }
               break;
        diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
        index e50f84dfdd6..be1c5b01e42 100644
        --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
        +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
        @@ -28,7 +28,6 @@
         #include "gc/shared/barrierSet.hpp"
         #include "gc/shared/barrierSetAssembler.hpp"
         #include "gc/shared/barrierSetNMethod.hpp"
        -#include "gc/shared/barrierSetRuntime.hpp"
         #include "interpreter/interp_masm.hpp"
         #include "oops/compressedOops.hpp"
         #include "runtime/jniHandles.hpp"
        @@ -111,20 +110,6 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators
           }
         }
         
        -// Generic implementation. GCs can provide an optimized one.
        -void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                                          Register src, Register dst, Register value_field_info) {
        -  // flat_field_copy implementation is fairly complex, and there are not any
        -  // "short-cuts" to be made from asm. What there is, appears to have the same
        -  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
        -  // of hand-rolled instructions...
        -  if (decorators & IS_DEST_UNINITIALIZED) {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info);
        -  } else {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info);
        -  }
        -}
        -
         // Generic implementation. GCs can provide an optimized one.
         void BarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2) {
         
        diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
        index d423210fe33..34d1f017150 100644
        --- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
        +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
        @@ -52,9 +52,6 @@ class BarrierSetAssembler: public CHeapObj {
           virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                 const Address& addr, Register val, Register tmp1, Register tmp2, Register tmp3);
         
        -  virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                               Register src, Register dst, Register value_field_info);
        -
           virtual void resolve_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2);
           virtual void resolve_global_jobject(MacroAssembler* masm, Register value, Register tmp1, Register tmp2);
         
        diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp
        index 399e4a1e8f9..4e5d3c8f148 100644
        --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp
        +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp
        @@ -2220,10 +2220,9 @@ void InterpreterMacroAssembler::read_flat_field(Register entry, Register obj) {
           call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::read_flat_field), obj, entry);
         }
         
        -void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_offset,
        -                                                 Register tmp1, Register tmp2,
        -                                                 Register obj) {
        -  assert_different_registers(entry, field_offset, tmp1, tmp2, obj);
        +void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1,
        +                                                 Register tmp2, Register obj) {
        +  assert_different_registers(entry, tmp1, tmp2, obj);
           Label slow_path, done;
         
           // Load flags and check if field is null-free value type.
        @@ -2233,24 +2232,8 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register field_
           // Null check the value being stored (Z_tos holds the value oop).
           null_check(Z_tos);  // FIXME JDK-8341120
         
        -  // Advance obj to the exact field address inside the object.
        -  z_agr(obj, field_offset);
        -
        -  // Compute the payload address of the value oop (Z_tos).
        -  // load_klass clobbers tmp1; payload_addr overwrites Z_tos in-place using tmp1 as the klass.
        -  load_klass(tmp1, Z_tos);
        -  payload_addr(Z_tos, Z_tos, tmp1);
        -
        -  // Load the ValueFieldInfo for this field:
        -  //   field_index (u2) from the entry, holder klass pointer from the entry.
        -  // Reuse field_offset as the vfi (value_field_info) register from here on.
        -  Register vfi = field_offset;
        -  load_sized_value(tmp1, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset())), sizeof(u2), false);
        -  load_sized_value(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset())), sizeof(void*), false);
        -  value_field_info(tmp2, tmp1, vfi);
        -
        -  // Inline byte-copy of the value's payload into the flat field slot.
        -  flat_field_copy(IN_HEAP, Z_tos, obj, vfi);
        +  z_lgr(tmp1, Z_tos);
        +  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::write_null_free_flat_field), obj, tmp1, entry);
           z_bru(done);
         
           bind(slow_path);
        diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp
        index 0ff223b6098..841f55b44ba 100644
        --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp
        +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp
        @@ -271,9 +271,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
         
           // Valhalla support for flat fields
           void read_flat_field(Register entry, Register obj);
        -  void write_flat_field(Register entry, Register field_offset,
        -                        Register tmp1, Register tmp2,
        -                        Register obj);
        +  void write_flat_field(Register entry, Register tmp1,
        +                        Register tmp2, Register obj);
         
           // Interpreter profiling operations
           void set_method_data_pointer_for_bcp();
        diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
        index 3f8591d6241..b3e1b223bf0 100644
        --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp
        +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
        @@ -4300,28 +4300,6 @@ void MacroAssembler::test_flat_array_layout(Register lh, Label& is_flat_array) {
           z_brnaz(is_flat_array);
         }
         
        -void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) {
        -  assert_different_registers(holder_klass, index, vfi);
        -  z_lg(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset()));
        -#ifdef ASSERT
        -  {
        -    Label done;
        -    z_ltgr(vfi, vfi);
        -    z_brne(done);
        -    stop("value_field_info_array is null");
        -    bind(done);
        -  }
        -#endif
        -  ValueFieldInfo array[2];
        -  int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements
        -  if (is_power_of_2(size)) {
        -    z_sllg(index, index, log2i_exact(size)); // Scale index by power of 2
        -  } else {
        -    z_msgfi(index, size); // Scale the index to be the entry index * array_element_size
        -  }
        -  z_lay(vfi, Address(vfi, index, Array::base_offset_in_bytes()));
        -}
        -
         // Compare klass ptr in memory against klass ptr in register.
         //
         // Rop1            - klass in register, always uncompressed.
        @@ -4565,12 +4543,6 @@ void MacroAssembler::store_heap_oop(Register Roop, const Address &a,
           access_store_at(T_OBJECT, IN_HEAP | decorators, a, Roop, tmp1, tmp2, tmp3);
         }
         
        -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst,
        -                                     Register value_field_info) {
        -  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
        -  bs->flat_field_copy(this, decorators, src, dst, value_field_info);
        -}
        -
         void MacroAssembler::payload_offset(Register value_klass, Register offset) {
           z_lg(offset, Address(value_klass, ValueKlass::adr_members_offset()));
           z_llgf(offset, Address(offset, ValueKlass::payload_offset_offset()));
        diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp
        index df121bd575c..6e210ed7829 100644
        --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp
        +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp
        @@ -880,7 +880,6 @@ class MacroAssembler: public Assembler {
           void test_null_free_array_oop(Register oop, Register temp_reg, Label& is_null_free_array);
           void test_non_null_free_array_oop(Register oop, Register temp_reg, Label& is_non_null_free_array);
           void test_flat_array_layout(Register lh, Label& is_flat_array);
        -  void value_field_info(Register holder_klass, Register index, Register vfi);
         
           // This function calculates the size of the code generated by
           //   decode_klass_not_null(register dst)
        @@ -905,8 +904,6 @@ class MacroAssembler: public Assembler {
                               const Address& addr, Register dst,
                               Register tmp1, Register tmp2, Label *is_null = nullptr);
         
        -  void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info);
        -
           // value type data payload offsets
           void payload_offset(Register value_klass, Register offset);
           void payload_addr(Register oop, Register data, Register value_klass);
        diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp
        index 6a1ae086916..dcb3fa4fc3f 100644
        --- a/src/hotspot/cpu/s390/templateTable_s390.cpp
        +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp
        @@ -3385,7 +3385,7 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr
                   Register flat_entry = tos_state;
                   Register flat_index = oopStore_tmp2; // Z_R1_scratch (index scratch, discarded after load)
                   __ load_field_entry(flat_entry, flat_index);
        -          __ write_flat_field(flat_entry, off, flat_index, oopStore_tmp3, oopStore_tmp1);
        +          __ write_flat_field(flat_entry, flat_index, oopStore_tmp3, oopStore_tmp1);
                 }__ bind(rewrite_value);
                 if (do_rewrite) {
                   patch_bytecode(Bytecodes::_fast_vputfield, bc_reg, patch_tmp, true, byte_no);
        @@ -3573,9 +3573,9 @@ void TemplateTable::fast_storefield(TosState state) {
                   Register flat_entry = Z_ARG4;  // R5  — safe for call_VM arg_3 slot
                   Register flat_index = Z_ARG3;  // R4  — discarded after load_field_entry
                   __ load_field_entry(flat_entry, flat_index);
        -          // entry=R5, field_offset=off=R11, tmp1=Z_ARG2=R3, tmp2=flat_index=Z_ARG3=R4, obj=Z_tmp_1=R10
        -          // All five are distinct; obj(R10) != Z_tos(R2) so flat_field_copy is safe.
        -          __ write_flat_field(flat_entry, off, Z_ARG2, flat_index, obj);
        +          // entry=R5, tmp1=Z_ARG2=R3, tmp2=flat_index=Z_ARG3=R4, obj=Z_tmp_1=R10
        +          // All four are distinct; obj(R10) != Z_tos(R2) so implementation is safe.
        +          __ write_flat_field(flat_entry, Z_ARG2, flat_index, obj);
                 }
                 __ bind(done);
               }
        diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        index 38d2e5f6ae7..d94f4826dc0 100644
        --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
        @@ -28,7 +28,6 @@
         #include "gc/shared/barrierSet.hpp"
         #include "gc/shared/barrierSetAssembler.hpp"
         #include "gc/shared/barrierSetNMethod.hpp"
        -#include "gc/shared/barrierSetRuntime.hpp"
         #include "gc/shared/collectedHeap.hpp"
         #include "interpreter/interp_masm.hpp"
         #include "memory/universe.hpp"
        @@ -164,19 +163,6 @@ void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators
           }
         }
         
        -void BarrierSetAssembler::flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                                          Register src, Register dst, Register value_field_info) {
        -  // flat_field_copy implementation is fairly complex, and there are not any
        -  // "short-cuts" to be made from asm. What there is, appears to have the same
        -  // cost in C++, so just "call_VM_leaf" for now rather than maintain hundreds
        -  // of hand-rolled instructions...
        -  if (decorators & IS_DEST_UNINITIALIZED) {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy_is_dest_uninitialized), src, dst, value_field_info);
        -  } else {
        -    __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetRuntime::value_copy), src, dst, value_field_info);
        -  }
        -}
        -
         void BarrierSetAssembler::copy_load_at(MacroAssembler* masm,
                                                DecoratorSet decorators,
                                                BasicType type,
        diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
        index 44cd2d9becf..caf341dcdf3 100644
        --- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
        +++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
        @@ -48,9 +48,6 @@ class BarrierSetAssembler: public CHeapObj {
           virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
                                 Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
         
        -  virtual void flat_field_copy(MacroAssembler* masm, DecoratorSet decorators,
        -                               Register src, Register dst, Register value_field_info);
        -
           // The copy_[load/store]_at functions are used by arraycopy stubs. Be careful to only use
           // r10 (aka rscratch1) in a context where restore_arg_regs_using_thread has been used instead
           // of the looser setup_arg_regs. Currently this is done when using type T_OBJECT.
        diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp
        index 9a91c07156c..c2f5eb68c2b 100644
        --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp
        +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp
        @@ -1126,8 +1126,8 @@ void InterpreterMacroAssembler::read_flat_field(Register entry, Register obj) {
         }
         
         void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1, Register tmp2,
        -                                                 Register obj, Register off, Register value) {
        -  assert_different_registers(entry, tmp1, tmp2, obj, off, value);
        +                                                 Register obj, Register value) {
        +  assert_different_registers(entry, tmp1, tmp2, obj, value);
         
           Label slow_path, done;
         
        @@ -1136,19 +1136,7 @@ void InterpreterMacroAssembler::write_flat_field(Register entry, Register tmp1,
         
           null_check(value); // FIXME JDK-8341120
         
        -  lea(obj, Address(obj, off, Address::times_1));
        -
        -  load_klass(tmp2, value, tmp1);
        -  payload_addr(value, value, tmp2);
        -
        -  Register idx = tmp1;
        -  load_unsigned_short(idx, Address(entry, in_bytes(ResolvedFieldEntry::field_index_offset())));
        -  movptr(tmp2, Address(entry, in_bytes(ResolvedFieldEntry::field_holder_offset())));
        -
        -  Register vfi = off;
        -  value_field_info(tmp2, idx, vfi);
        -
        -  flat_field_copy(IN_HEAP, value, obj, vfi);
        +  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::write_null_free_flat_field), obj, value, entry);
           jmp(done);
         
           bind(slow_path);
        diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp
        index d4bfb9ba89f..2419044505a 100644
        --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp
        +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp
        @@ -220,9 +220,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
           //   - input holder object via "obj", which must be rax,
           //     will return new instance via the same reg
           void read_flat_field(Register entry, Register obj);
        -  void write_flat_field(Register entry,
        -                        Register tmp1, Register tmp2,
        -                        Register obj, Register off, Register value);
        +  void write_flat_field(Register entry, Register tmp1, Register tmp2,
        +                        Register obj, Register value);
         
           // Object locking
           void lock_object  (Register lock_reg);
        diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        index cd0a8add1f3..ae7304b354b 100644
        --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
        @@ -3816,28 +3816,6 @@ void MacroAssembler::zero_memory(Register address, Register length_in_bytes, int
           bind(done);
         }
         
        -void MacroAssembler::value_field_info(Register holder_klass, Register index, Register vfi) {
        -  movptr(vfi, Address(holder_klass, InstanceKlass::value_field_info_array_offset()));
        -#ifdef ASSERT
        -  {
        -    Label done;
        -    cmpptr(vfi, 0);
        -    jcc(Assembler::notEqual, done);
        -    stop("value_field_info_array is null");
        -    bind(done);
        -  }
        -#endif
        -
        -  ValueFieldInfo array[2];
        -  int size = (char*)&array[1] - (char*)&array[0]; // computing size of array elements
        -  if (is_power_of_2(size)) {
        -    shll(index, log2i_exact(size)); // Scale index by power of 2
        -  } else {
        -    imull(index, index, size); // Scale the index to be the entry index * array_element_size
        -  }
        -  lea(vfi, Address(vfi, index, Address::times_1, Array::base_offset_in_bytes()));
        -}
        -
         // Look up the method for a megamorphic invokeinterface call.
         // The target method is determined by .
         // The receiver klass is in recv_klass.
        @@ -5643,12 +5621,6 @@ void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators, Ad
           }
         }
         
        -void MacroAssembler::flat_field_copy(DecoratorSet decorators, Register src, Register dst,
        -                                     Register value_field_info) {
        -  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
        -  bs->flat_field_copy(this, decorators, src, dst, value_field_info);
        -}
        -
         void MacroAssembler::payload_offset(Register value_klass, Register offset) {
           movptr(offset, Address(value_klass, ValueKlass::adr_members_offset()));
           movl(offset, Address(offset, ValueKlass::payload_offset_offset()));
        diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
        index 3ffe8eba6c2..87299b3046b 100644
        --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
        +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
        @@ -392,8 +392,6 @@ class MacroAssembler: public Assembler {
           void access_store_at(BasicType type, DecoratorSet decorators, Address dst, Register val,
                                Register tmp1, Register tmp2, Register tmp3);
         
        -  void flat_field_copy(DecoratorSet decorators, Register src, Register dst, Register value_field_info);
        -
           // value type data payload offsets...
           void payload_offset(Register value_klass, Register offset);
           void payload_addr(Register oop, Register data, Register value_klass);
        @@ -554,8 +552,6 @@ class MacroAssembler: public Assembler {
           );
           void zero_memory(Register address, Register length_in_bytes, int offset_in_bytes, Register temp);
         
        -  void value_field_info(Register klass, Register index, Register vfi);
        -
           void population_count(Register dst, Register src, Register scratch1, Register scratch2);
         
           // interface method calling
        diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp
        index b79bd534405..620498378e5 100644
        --- a/src/hotspot/cpu/x86/templateTable_x86.cpp
        +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp
        @@ -3012,7 +3012,7 @@ void TemplateTable::putfield_or_static_helper(int byte_no, bool is_static, Rewri
                 __ jmp(rewrite_value);
                 __ bind(is_flat);
                 pop_and_check_object(rscratch2);
        -        __ write_flat_field(rcx, r8, rscratch1, rscratch2, rbx, rax);
        +        __ write_flat_field(rcx, r8, rscratch1, rscratch2, rax);
                 __ bind(rewrite_value);
                 if (rc == may_rewrite) {
                   patch_bytecode(Bytecodes::_fast_vputfield, bc, rbx, true, byte_no);
        @@ -3249,7 +3249,7 @@ void TemplateTable::fast_storefield_helper(Address field, Register rax, Register
               __ bind(is_flat);
               __ load_field_entry(r8, r9);
               __ movptr(rscratch2, rcx);  // re-shuffle registers because of VM call calling convention
        -      __ write_flat_field(r8, rscratch1, r9, rscratch2, rbx, rax);
        +      __ write_flat_field(r8, rscratch1, r9, rscratch2, rax);
               __ bind(done);
             }
             break;
        diff --git a/src/hotspot/share/gc/shared/barrierSetRuntime.cpp b/src/hotspot/share/gc/shared/barrierSetRuntime.cpp
        deleted file mode 100644
        index 67d7455dd46..00000000000
        --- a/src/hotspot/share/gc/shared/barrierSetRuntime.cpp
        +++ /dev/null
        @@ -1,41 +0,0 @@
        -/*
        - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
        - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        - *
        - * This code is free software; you can redistribute it and/or modify it
        - * under the terms of the GNU General Public License version 2 only, as
        - * published by the Free Software Foundation.
        - *
        - * This code is distributed in the hope that it will be useful, but WITHOUT
        - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        - * version 2 for more details (a copy is included in the LICENSE file that
        - * accompanied this code).
        - *
        - * You should have received a copy of the GNU General Public License version
        - * 2 along with this work; if not, write to the Free Software Foundation,
        - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
        - *
        - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
        - * or visit www.oracle.com if you need additional information or have any
        - * questions.
        - *
        - */
        -
        -#include "gc/shared/barrierSetRuntime.hpp"
        -#include "oops/access.inline.hpp"
        -#include "oops/valuePayload.inline.hpp"
        -#include "runtime/interfaceSupport.inline.hpp"
        -#include "utilities/macros.hpp"
        -
        -JRT_LEAF(void, BarrierSetRuntime::value_copy(address src, address dst, ValueFieldInfo* vfi))
        -  ValuePayload src_payload = ValuePayload::construct_from_parts(src, vfi->klass(), vfi->kind());
        -  ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, vfi->klass(), vfi->kind());
        -  HeapAccess<>::value_copy(src_payload, dst_payload);
        -JRT_END
        -
        -JRT_LEAF(void, BarrierSetRuntime::value_copy_is_dest_uninitialized(address src, address dst, ValueFieldInfo* vfi))
        -  ValuePayload src_payload = ValuePayload::construct_from_parts(src, vfi->klass(), vfi->kind());
        -  ValuePayload dst_payload = ValuePayload::construct_from_parts(dst, vfi->klass(), vfi->kind());
        -  HeapAccess::value_copy(src_payload, dst_payload);
        -JRT_END
        diff --git a/src/hotspot/share/gc/shared/barrierSetRuntime.hpp b/src/hotspot/share/gc/shared/barrierSetRuntime.hpp
        deleted file mode 100644
        index 14abbc4bd9b..00000000000
        --- a/src/hotspot/share/gc/shared/barrierSetRuntime.hpp
        +++ /dev/null
        @@ -1,39 +0,0 @@
        -/*
        - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
        - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        - *
        - * This code is free software; you can redistribute it and/or modify it
        - * under the terms of the GNU General Public License version 2 only, as
        - * published by the Free Software Foundation.
        - *
        - * This code is distributed in the hope that it will be useful, but WITHOUT
        - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        - * version 2 for more details (a copy is included in the LICENSE file that
        - * accompanied this code).
        - *
        - * You should have received a copy of the GNU General Public License version
        - * 2 along with this work; if not, write to the Free Software Foundation,
        - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
        - *
        - * Please 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_GC_SHARED_BARRIERSETRUNTIME_HPP
        -#define SHARE_GC_SHARED_BARRIERSETRUNTIME_HPP
        -
        -#include "memory/allocation.hpp"
        -#include "oops/valueKlass.hpp"
        -#include "utilities/globalDefinitions.hpp"
        -
        -class BarrierSetRuntime: public AllStatic {
        -public:
        -  // Template interpreter...
        -  static void value_copy(address src, address dst, ValueFieldInfo* vfi);
        -  static void value_copy_is_dest_uninitialized(address src, address dst, ValueFieldInfo* vfi);
        -};
        -
        -#endif // SHARE_GC_SHARED_BARRIERSETRUNTIME_HPP
        diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
        index 9bed85d1e16..cf356c6b19a 100644
        --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
        +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
        @@ -250,6 +250,15 @@ JRT_BLOCK_ENTRY(void, InterpreterRuntime::read_flat_field(JavaThread* current, o
           JRT_BLOCK_END
         JRT_END
         
        +JRT_LEAF(void, InterpreterRuntime::write_null_free_flat_field(oopDesc* obj, oopDesc* value, ResolvedFieldEntry* entry))
        +  assert(value != nullptr, "Expecting null-free value");
        +  assert(oopDesc::is_oop(obj), "Sanity check");
        +  assert(oopDesc::is_oop(value), "Sanity check");
        +
        +  FlatFieldPayload payload(instanceOop(obj), entry);
        +  payload.write_without_nullability_check(valueOop(value));
        +JRT_END
        +
         JRT_ENTRY(void, InterpreterRuntime::write_flat_field(JavaThread* current, oopDesc* obj, oopDesc* value, ResolvedFieldEntry* entry))
           assert(oopDesc::is_oop(obj), "Sanity check");
           assert(oopDesc::is_oop_or_null(value), "Sanity check");
        diff --git a/src/hotspot/share/interpreter/interpreterRuntime.hpp b/src/hotspot/share/interpreter/interpreterRuntime.hpp
        index 57731a96644..59246c1d7ea 100644
        --- a/src/hotspot/share/interpreter/interpreterRuntime.hpp
        +++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp
        @@ -62,6 +62,7 @@ class InterpreterRuntime: AllStatic {
           static void    multianewarray(JavaThread* current, jint* first_size_address);
           static void    register_finalizer(JavaThread* current, oopDesc* obj);
           static void    read_flat_field(JavaThread* current, oopDesc* object, ResolvedFieldEntry* entry);
        +  static void    write_null_free_flat_field(oopDesc* object, oopDesc* value, ResolvedFieldEntry* entry);
           static void    write_flat_field(JavaThread* current, oopDesc* object, oopDesc* value, ResolvedFieldEntry* entry);
         
           static void flat_array_load(JavaThread* current, arrayOopDesc* array, int index);
        diff --git a/src/hotspot/share/oops/valuePayload.hpp b/src/hotspot/share/oops/valuePayload.hpp
        index 545c8ae84c2..eceb0140352 100644
        --- a/src/hotspot/share/oops/valuePayload.hpp
        +++ b/src/hotspot/share/oops/valuePayload.hpp
        @@ -45,29 +45,16 @@ class ValuePayload {
         private:
           template  class StorageImpl {
           private:
        -    union {
        -      struct {
        -        OopOrHandle _container;
        -        ptrdiff_t _offset;
        -      };
        -      address _absolute_addr;
        -    };
        +    OopOrHandle _container;
        +    ptrdiff_t _offset;
             ValueKlass* _klass;
             LayoutKind _layout_kind;
        -    bool _uses_absolute_addr;
         
           public:
        -    inline StorageImpl();
             inline StorageImpl(OopOrHandle container,
                                ptrdiff_t offset,
                                ValueKlass* klass,
                                LayoutKind layout_kind);
        -    inline StorageImpl(address absolute_addr,
        -                       ValueKlass* klass,
        -                       LayoutKind layout_kind);
        -    inline ~StorageImpl();
        -    inline StorageImpl(const StorageImpl& other);
        -    inline StorageImpl& operator=(const StorageImpl& other);
         
             inline OopOrHandle& container();
             inline OopOrHandle container() const;
        @@ -75,14 +62,9 @@ class ValuePayload {
             inline ptrdiff_t& offset();
             inline ptrdiff_t offset() const;
         
        -    inline address& absolute_addr();
        -    inline address absolute_addr() const;
        -
             inline ValueKlass* klass() const;
         
             inline LayoutKind layout_kind() const;
        -
        -    inline bool uses_absolute_addr() const;
           };
         
           using Storage = StorageImpl;
        @@ -92,7 +74,6 @@ class ValuePayload {
         protected:
           static constexpr ptrdiff_t BAD_OFFSET = -1;
         
        -  ValuePayload() = default;
           ValuePayload(const ValuePayload&) = default;
           ValuePayload& operator=(const ValuePayload&) = default;
         
        @@ -102,11 +83,6 @@ class ValuePayload {
                               ValueKlass* klass,
                               LayoutKind layout_kind);
         
        -  // Constructed from parts absolute_addr
        -  inline ValuePayload(address absolute_addr,
        -                      ValueKlass* klass,
        -                      LayoutKind layout_kind);
        -
           inline void set_offset(ptrdiff_t offset);
         
           static inline void copy(const ValuePayload& src,
        @@ -118,8 +94,6 @@ class ValuePayload {
           inline bool has_null_marker() const;
           inline bool is_payload_null() const;
         
        -  inline bool uses_absolute_addr() const;
        -
           inline oop& container();
           inline oop container() const;
         
        @@ -144,10 +118,6 @@ class ValuePayload {
           class OopHandle;
         
           static inline size_t copy_size_in_bytes(const ValuePayload& src, const ValuePayload& dst);
        -
        -  [[nodiscard]] static inline ValuePayload construct_from_parts(address absolute_addr,
        -                                                                ValueKlass* klass,
        -                                                                LayoutKind layout_kind);
         };
         
         class BufferedValuePayload : public ValuePayload {
        @@ -160,7 +130,6 @@ class BufferedValuePayload : public ValuePayload {
                                       LayoutKind layout_kind);
         
         public:
        -  BufferedValuePayload() = default;
           BufferedValuePayload(const BufferedValuePayload&) = default;
           BufferedValuePayload& operator=(const BufferedValuePayload&) = default;
         
        @@ -189,7 +158,6 @@ class FlatValuePayload : public ValuePayload {
           inline valueOop allocate_instance(TRAPS);
         
         public:
        -  FlatValuePayload() = default;
           FlatValuePayload(const FlatValuePayload&) = default;
           FlatValuePayload& operator=(const FlatValuePayload&) = default;
         
        @@ -234,8 +202,6 @@ class FlatFieldPayload : public FlatValuePayload {
                                                           fieldDescriptor* field_descriptor) const NOT_DEBUG_RETURN;
         
         public:
        -  FlatFieldPayload() = default;
        -
           inline FlatFieldPayload(instanceOop container,
                                   fieldDescriptor* field_descriptor);
         
        @@ -266,7 +232,6 @@ class FlatArrayPayload : public FlatValuePayload {
                                   int element_size);
         
         public:
        -  FlatArrayPayload() = default;
           FlatArrayPayload(const FlatArrayPayload&) = default;
           FlatArrayPayload& operator=(const FlatArrayPayload&) = default;
         
        @@ -307,7 +272,6 @@ class ValuePayload::Handle {
           inline oop container() const;
         
         public:
        -  Handle() = default;
           Handle(const Handle&) = default;
           Handle& operator=(const Handle&) = default;
         
        @@ -328,7 +292,6 @@ class ValuePayload::OopHandle {
           inline oop container() const;
         
         public:
        -  OopHandle() = default;
           OopHandle(const OopHandle&) = default;
           OopHandle& operator=(const OopHandle&) = default;
         
        diff --git a/src/hotspot/share/oops/valuePayload.inline.hpp b/src/hotspot/share/oops/valuePayload.inline.hpp
        index f0bd29b06bc..ab83872aee2 100644
        --- a/src/hotspot/share/oops/valuePayload.inline.hpp
        +++ b/src/hotspot/share/oops/valuePayload.inline.hpp
        @@ -27,7 +27,6 @@
         
         #include "oops/valuePayload.hpp"
         
        -#include "cppstdlib/type_traits.hpp"
         #include "oops/flatArrayKlass.inline.hpp"
         #include "oops/flatArrayOop.inline.hpp"
         #include "oops/layoutKind.hpp"
        @@ -43,14 +42,6 @@
         #include "utilities/ostream.hpp"
         #include "utilities/vmError.hpp"
         
        -template 
        -inline ValuePayload::StorageImpl::StorageImpl()
        -    : _container(nullptr),
        -      _offset(BAD_OFFSET),
        -      _klass(nullptr),
        -      _layout_kind(LayoutKind::UNKNOWN),
        -      _uses_absolute_addr(false) {}
        -
         template 
         inline ValuePayload::StorageImpl::StorageImpl(OopOrHandle container,
                                                                    ptrdiff_t offset,
        @@ -59,96 +50,28 @@ inline ValuePayload::StorageImpl::StorageImpl(OopOrHandle container
             : _container(container),
               _offset(offset),
               _klass(klass),
        -      _layout_kind(layout_kind),
        -      _uses_absolute_addr(false) {}
        -
        -template 
        -inline ValuePayload::StorageImpl::StorageImpl(address absolute_addr,
        -                                                           ValueKlass* klass,
        -                                                           LayoutKind layout_kind)
        -    : _absolute_addr(absolute_addr),
        -      _klass(klass),
        -      _layout_kind(layout_kind),
        -      _uses_absolute_addr(true) {}
        -
        -template 
        -inline ValuePayload::StorageImpl::~StorageImpl() {
        -#ifdef CHECK_UNHANDLED_OOPS
        -  if (!_uses_absolute_addr) {
        -    _container.~OopOrHandle();
        -  }
        -#else  // CHECK_UNHANDLED_OOPS
        -  static_assert(std::is_trivially_destructible_v);
        -#endif // CHECK_UNHANDLED_OOPS
        -}
        -
        -template 
        -inline ValuePayload::StorageImpl::StorageImpl(const StorageImpl& other)
        -    : _klass(other._klass),
        -      _layout_kind(other._layout_kind),
        -      _uses_absolute_addr(other._uses_absolute_addr) {
        -  if (_uses_absolute_addr) {
        -    _absolute_addr = other._absolute_addr;
        -  } else {
        -    _container = other._container;
        -    _offset = other._offset;
        -  }
        -}
        -
        -template 
        -inline ValuePayload::StorageImpl&
        -ValuePayload::StorageImpl::operator=(const StorageImpl& other) {
        -  if (&other != this) {
        -    _klass = other._klass;
        -    _layout_kind = other._layout_kind;
        -    _uses_absolute_addr = other._uses_absolute_addr;
        -    if (_uses_absolute_addr) {
        -      _absolute_addr = other._absolute_addr;
        -    } else {
        -      _container = other._container;
        -      _offset = other._offset;
        -    }
        -  }
        -  return *this;
        -}
        +      _layout_kind(layout_kind) {}
         
         template 
         inline OopOrHandle& ValuePayload::StorageImpl::container() {
        -  precond(!_uses_absolute_addr);
           return _container;
         }
         
         template 
         inline OopOrHandle ValuePayload::StorageImpl::container() const {
        -  precond(!_uses_absolute_addr);
           return _container;
         }
         
         template 
         inline ptrdiff_t& ValuePayload::StorageImpl::offset() {
        -  precond(!_uses_absolute_addr);
           return _offset;
         }
         
         template 
         inline ptrdiff_t ValuePayload::StorageImpl::offset() const {
        -  precond(!_uses_absolute_addr);
           return _offset;
         }
         
        -template 
        -inline address& ValuePayload::StorageImpl::absolute_addr() {
        -  precond(_uses_absolute_addr);
        -  return _absolute_addr;
        -}
        -
        -template 
        -inline address ValuePayload::StorageImpl::absolute_addr() const {
        -  precond(_uses_absolute_addr);
        -  return _absolute_addr;
        -
        -}
        -
         template 
         inline ValueKlass* ValuePayload::StorageImpl::klass() const {
           return _klass;
        @@ -159,11 +82,6 @@ inline LayoutKind ValuePayload::StorageImpl::layout_kind() const {
           return _layout_kind;
         }
         
        -template 
        -inline bool ValuePayload::StorageImpl::uses_absolute_addr() const {
        -  return _uses_absolute_addr;
        -}
        -
         inline ValuePayload::ValuePayload(oop container,
                                           ptrdiff_t offset,
                                           ValueKlass* klass,
        @@ -172,13 +90,6 @@ inline ValuePayload::ValuePayload(oop container,
           assert_post_construction_invariants();
         }
         
        -inline ValuePayload::ValuePayload(address absolute_addr,
        -                                  ValueKlass* klass,
        -                                  LayoutKind layout_kind)
        -    : _storage{absolute_addr, klass, layout_kind} {
        -  assert_post_construction_invariants();
        -}
        -
         inline void ValuePayload::set_offset(ptrdiff_t offset) {
           _storage.offset() = offset;
         }
        @@ -207,10 +118,6 @@ inline bool ValuePayload::is_payload_null() const {
           return has_null_marker() && klass()->is_payload_marked_as_null(addr());
         }
         
        -inline bool ValuePayload::uses_absolute_addr() const {
        -  return _storage.uses_absolute_addr();
        -}
        -
         inline oop& ValuePayload::container() {
           return _storage.container();
         }
        @@ -222,27 +129,21 @@ inline oop ValuePayload::container() const {
         #ifdef ASSERT
         
         inline void ValuePayload::print_on(outputStream* st) const {
        -  if (uses_absolute_addr()) {
        -    st->print_cr("--- absolute_addr ---");
        +  {
        +    oop container = _storage.container();
        +    st->print_cr("--- container ---");
             StreamIndentor si(st);
        -    st->print_cr("_absolute_addr: " PTR_FORMAT, p2i(_storage.absolute_addr()));
        -  } else {
        -    {
        -      oop container = _storage.container();
        -      st->print_cr("--- container ---");
        -      StreamIndentor si(st);
        -      st->print_cr("_container: " PTR_FORMAT, p2i(container));
        -      if (container != nullptr) {
        -        container->print_on(st);
        -        st->cr();
        -      }
        -    }
        -    {
        -      st->print_cr("--- offset ---");
        -      StreamIndentor si(st);
        -      st->print_cr("_offset: %zd", _storage.offset());
        +    st->print_cr("_container: " PTR_FORMAT, p2i(container));
        +    if (container != nullptr) {
        +      container->print_on(st);
        +      st->cr();
             }
           }
        +  {
        +    st->print_cr("--- offset ---");
        +    StreamIndentor si(st);
        +    st->print_cr("_offset: %zd", _storage.offset());
        +  }
           {
             ValueKlass* const klass = _storage.klass();
             st->print_cr("--- klass ---");
        @@ -316,31 +217,29 @@ inline void ValuePayload::assert_post_construction_invariants() const {
           postcond(layout_kind() != LayoutKind::UNKNOWN);
           postcond(klass()->is_layout_supported(layout_kind()));
         
        -  if (!uses_absolute_addr()) {
        -    postcond(container() != nullptr);
        -    const Klass* const container_klass = container()->klass();
        -    if (container_klass == klass()) {
        -      postcond(layout_kind() == LayoutKind::BUFFERED);
        +  postcond(container() != nullptr);
        +  const Klass* const container_klass = container()->klass();
        +  if (container_klass == klass()) {
        +    postcond(layout_kind() == LayoutKind::BUFFERED);
        +  } else {
        +    postcond(layout_kind() != LayoutKind::BUFFERED);
        +    if (container_klass->is_mirror_instance_klass()) {
        +      fatal("java.lang.Class has no flat fields. Static fields are not flattened");
        +    } else if (container_klass->is_instance_klass()) {
        +      assert_is_flat_field(InstanceKlass::cast(container_klass), checked_cast(offset()));
             } else {
        -      postcond(layout_kind() != LayoutKind::BUFFERED);
        -      if (container_klass->is_mirror_instance_klass()) {
        -        fatal("java.lang.Class has no flat fields. Static fields are not flattened");
        -      } else if (container_klass->is_instance_klass()) {
        -        assert_is_flat_field(InstanceKlass::cast(container_klass), checked_cast(offset()));
        +      const FlatArrayKlass* const container_flat_array_klass = FlatArrayKlass::cast(container_klass);
        +      if (container_flat_array_klass->element_klass() == klass()) {
        +        postcond(container_flat_array_klass->layout_kind() == layout_kind());
               } else {
        -        const FlatArrayKlass* const container_flat_array_klass = FlatArrayKlass::cast(container_klass);
        -        if (container_flat_array_klass->element_klass() == klass()) {
        -          postcond(container_flat_array_klass->layout_kind() == layout_kind());
        -        } else {
        -          // Accessing nested flat field
        -          const ValueKlass* const element_klass = container_flat_array_klass->element_klass();
        -          const int element_offset =
        -              (checked_cast(this->offset()) -
        -               checked_cast(flatArrayOopDesc::base_offset_in_bytes())) %
        -              container_flat_array_klass->element_byte_size();
        -          const int payload_offset = element_klass->payload_offset();
        -          assert_is_flat_field(element_klass, element_offset + payload_offset);
        -        }
        +        // Accessing nested flat field
        +        const ValueKlass* const element_klass = container_flat_array_klass->element_klass();
        +        const int element_offset =
        +            (checked_cast(this->offset()) -
        +              checked_cast(flatArrayOopDesc::base_offset_in_bytes())) %
        +            container_flat_array_klass->element_byte_size();
        +        const int payload_offset = element_klass->payload_offset();
        +        assert_is_flat_field(element_klass, element_offset + payload_offset);
               }
             }
           }
        @@ -382,11 +281,7 @@ inline void ValuePayload::assert_pre_copy_invariants(const ValuePayload& src,
           precond(src_has_copy_layout || dst_has_copy_layout);
         
           if (src_is_buffered) {
        -    oop container = src.uses_absolute_addr()
        -        ? cast_to_oop(src.addr() - src_klass->payload_offset())
        -        : src.container();
        -
        -    precond(!src_klass->supports_nullable_layouts() || container != src_klass->null_reset_value());
        +    precond(!src_klass->supports_nullable_layouts() || src.container() != src_klass->null_reset_value());
           }
         
           const size_t src_layout_size_in_bytes = src.size_in_bytes();
        @@ -413,9 +308,7 @@ inline LayoutKind ValuePayload::layout_kind() const {
         }
         
         inline address ValuePayload::addr() const {
        -  return uses_absolute_addr()
        -      ? _storage.absolute_addr()
        -      : (cast_from_oop
        (container()) + offset()); + return cast_from_oop
        (container()) + offset(); } inline size_t ValuePayload::size_in_bytes() const { @@ -429,12 +322,6 @@ inline size_t ValuePayload::copy_size_in_bytes(const ValuePayload& src, const Va return integer_cast(src.klass()->layout_size_in_bytes(copy_layout_kind)); } -inline ValuePayload ValuePayload::construct_from_parts(address absolute_addr, - ValueKlass* klass, - LayoutKind layout_kind) { - return ValuePayload(absolute_addr, klass, layout_kind); -} - inline BufferedValuePayload::BufferedValuePayload(valueOop container, ptrdiff_t offset, ValueKlass* klass, From e07fcffc4b4e1cb0c35be8c4e5ef7965b059bd55 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 8 Oct 2026 06:26:45 +0000 Subject: [PATCH 94/98] 8196432: Many ModalFocusTransfer tests fail on Linux and macOS Reviewed-by: azvegint, kizune --- .../classes/sun/lwawt/LWWindowPeer.java | 106 ++++++++++++++++++ test/jdk/ProblemList.txt | 22 ---- 2 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java index 61f9d543394..43f971a6245 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -25,6 +25,7 @@ package sun.lwawt; +import java.lang.ref.WeakReference; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Component; @@ -139,6 +140,14 @@ public enum PeerType { private volatile boolean skipNextFocusChange; + /* + * Per-peer state for restoring Java focus to a simple Window after the + * native Frame or Dialog that took focus from it is hidden. + */ + private final Object focusRestoreLock = new Object(); + private WeakReference focusRestoreTarget; + private FocusRestoreRequest pendingFocusRestore; + private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0); private volatile boolean textured; @@ -248,6 +257,7 @@ protected LWWindowPeer getWindowPeerOrSelf() { @Override protected void disposeImpl() { + registerFocusRestoreOnHide(); deactivateDisplayListener(); SurfaceData oldData = getSurfaceData(); synchronized (surfaceDataLock){ @@ -276,6 +286,9 @@ public void setBackground(final Color c) { @Override protected void setVisibleImpl(final boolean visible) { + if (!visible) { + registerFocusRestoreOnHide(); + } updateFocusableWindowState(); super.setVisibleImpl(visible); // TODO: update graphicsConfig, see 4868278 @@ -754,6 +767,12 @@ public void notifyUpdateCursor() { @Override public void notifyActivation(boolean activation, LWWindowPeer opposite) { Window oppositeWindow = (opposite == null)? null : opposite.getTarget(); + if (activation) { + if (restoreFocusAfterHide()) { + return; + } + rememberFocusRestoreTarget(); + } changeFocusedWindow(activation, oppositeWindow); } @@ -1409,6 +1428,93 @@ public LWWindowPeer getBlocker() { } } + // Saves the currently focused simple Window as this native top-level's + // Java focus restore target when it becomes active. + private void rememberFocusRestoreTarget() { + Window focusedWindow = LWKeyboardFocusManagerPeer.getInstance() + .getCurrentFocusedWindow(); + LWWindowPeer focusedPeer = focusedWindow == null ? null + : (LWWindowPeer) AWTAccessor.getComponentAccessor() + .getPeer(focusedWindow); + + if (focusedPeer == null || focusedPeer == this) { + return; + } + synchronized (focusRestoreLock) { + focusRestoreTarget = focusedPeer.isSimpleWindow() + ? new WeakReference<>(focusedPeer) : null; + } + } + + // Registers this peer's saved Java focus restore target for later restoration + // before this native top-level is hidden or disposed. + private void registerFocusRestoreOnHide() { + LWWindowPeer target; + synchronized (focusRestoreLock) { + KeyboardFocusManagerPeer kfmPeer = + LWKeyboardFocusManagerPeer.getInstance(); + if (kfmPeer.getCurrentFocusedWindow() != getTarget()) { + return; + } + + target = focusRestoreTarget == null ? null + : focusRestoreTarget.get(); + focusRestoreTarget = null; + } + + if (target == null) { + return; + } + + LWWindowPeer owner = getOwnerFrameDialog(target); + if (owner != null && owner != this) { + owner.registerFocusRestore(this, target); + } + } + + private void registerFocusRestore(LWWindowPeer source, LWWindowPeer target) { + synchronized (focusRestoreLock) { + pendingFocusRestore = new FocusRestoreRequest(source, target); + } + } + + // Restores the pending Java focus target when its nearest owning Frame or Dialog + // becomes active after the source peer was hidden, including by disposal. + private boolean restoreFocusAfterHide() { + FocusRestoreRequest request; + synchronized (focusRestoreLock) { + request = pendingFocusRestore; + pendingFocusRestore = null; + } + + if (request == null) { + return false; + } + + LWWindowPeer source = request.source.get(); + LWWindowPeer target = request.target.get(); + if (source == null || target == null + || source.getTarget().isVisible() + || getOwnerFrameDialog(target) != this + || !target.focusAllowedFor() + || target.getBlocker() != null) { + return false; + } + + target.changeFocusedWindow(true, source.getTarget()); + return true; + } + + private static final class FocusRestoreRequest { + final WeakReference source; + final WeakReference target; + + FocusRestoreRequest(LWWindowPeer source, LWWindowPeer target) { + this.source = new WeakReference<>(source); + this.target = new WeakReference<>(target); + } + } + @Override public void enterFullScreenMode() { platformWindow.enterFullScreenMode(); diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 8d10abb2df5..c5aea218b79 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -253,28 +253,6 @@ java/awt/Modal/ModalExclusionTests/ToolkitExcludeFramePrintSetupTest.java 819643 java/awt/Modal/ModalFocusTransferTests/FocusTransferWDFAppModal2Test.java 8058813 windows-all java/awt/Modal/ModalFocusTransferTests/FocusTransferWDFModeless2Test.java 8196191 windows-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferDWFDocModalTest.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferDWFModelessTest.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferDWFNonModalTest.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferDialogsModelessTest.java 8196432 linux-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferDialogsNonModalTest.java 8196432 linux-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDAppModal1Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDAppModal2Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDAppModal3Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDAppModal4Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDDocModal1Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDDocModal2Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDDocModal3Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDDocModal4Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDModeless1Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDModeless2Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDModeless3Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDModeless4Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDNonModal1Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDNonModal2Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDNonModal3Test.java 8196432 macosx-all -java/awt/Modal/ModalFocusTransferTests/FocusTransferFWDNonModal4Test.java 8196432 macosx-all - java/awt/Mouse/EnterExitEvents/DragWindowOutOfFrameTest.java 8177326 macosx-all java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java 8005021 macosx-all java/awt/Mouse/EnterExitEvents/FullscreenEnterEventTest.java 8051455 macosx-all From 942aff4452c8caf9a92a82011490ae4a3372fee2 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 9 Oct 2026 16:04:36 +0000 Subject: [PATCH 95/98] 8393884: MiscTests.testExecutableJARNoManifest fails when -Djava.awt.headless=true is in test VM options Reviewed-by: lancea, azeller, mbaesken --- .../sun/launcher/resources/launcher.properties | 2 +- test/jdk/tools/launcher/MiscTests.java | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index ddae0e1317c..674d723ba6c 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -290,7 +290,7 @@ java.launcher.cls.error8=\ please use a concrete class java.launcher.jar.error1=\ Error: An unexpected error occurred while trying to open file {0} -java.launcher.jar.error2=manifest not found in {0} +java.launcher.jar.error2=Error: No manifest in JAR file {0} java.launcher.jar.error3=no main manifest attribute, in {0} java.launcher.jar.error4=error loading java agent in {0} java.launcher.jar.error5=\ diff --git a/test/jdk/tools/launcher/MiscTests.java b/test/jdk/tools/launcher/MiscTests.java index fa865007628..5aec7c226a4 100644 --- a/test/jdk/tools/launcher/MiscTests.java +++ b/test/jdk/tools/launcher/MiscTests.java @@ -121,10 +121,18 @@ void testExecutableJARNoManifest() throws Exception { noManifest.write("bar".getBytes(US_ASCII)); noManifest.closeEntry(); } - // run "java -jar" against that JAR file and expect the launch to fail - final OutputAnalyzer oa = ProcessTools.executeTestJava("-jar", jarFile.toString()); - oa.shouldNotHaveExitValue(0); // expected to fail with non-zero exit code - // verify it failed for the right reason - oa.shouldContain("Error: No manifest in JAR file"); + // run "java -jar" against that JAR file and expect the launch to fail. + // once in headless mode and once headful + for (boolean headless : new boolean[]{true, false}) { + System.err.println("running with -Djava.awt.headless=" + headless); + final OutputAnalyzer oa = ProcessTools.executeTestJava( + "-Djava.awt.headless=" + headless, + // force the error message from the launcher to be in English + "-Duser.language=en", "-Duser.country=US", + "-jar", jarFile.toString()); + oa.shouldNotHaveExitValue(0); // expected to fail with non-zero exit code + // verify it failed for the right reason + oa.shouldContain("Error: No manifest in JAR file"); + } } } From 0e17b0267e13e221a4c3f900b61859833cd11319 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 9 Oct 2026 19:27:33 +0000 Subject: [PATCH 96/98] 8394091: gc/z/TestZHugeMaxHeapSize.java fails when test VM options set -Xmx Reviewed-by: stefank, mdoerr --- .../jtreg/gc/z/TestZHugeMaxHeapSize.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java b/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java index 39273555dea..e35c413e1a0 100644 --- a/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java +++ b/test/hotspot/jtreg/gc/z/TestZHugeMaxHeapSize.java @@ -25,7 +25,7 @@ /** * @test - * @bug 8382070 + * @bug 8382070 8394091 * @summary A max heap size large enough to overflow the ZGC address offset * request must be rejected with an error message, not crash the VM * @requires vm.gc.Z @@ -33,12 +33,20 @@ * @run driver gc.z.TestZHugeMaxHeapSize */ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import jdk.test.lib.process.ProcessTools; public class TestZHugeMaxHeapSize { - private static void test(String option) throws Exception { - ProcessTools.executeTestJava("-XX:+UseZGC", option, "-version") + private static void test(String... options) throws Exception { + List args = new ArrayList<>(); + args.add("-XX:+UseZGC"); + args.addAll(Arrays.asList(options)); + args.add("-version"); + ProcessTools.executeTestJava(args) .outputTo(System.out) .errorTo(System.out) .shouldHaveExitValue(1) @@ -51,7 +59,9 @@ public static void main(String[] args) throws Exception { test("-XX:MaxHeapSize=1152921504606846976"); // Value found by the flag fuzzer test("-XX:MaxHeapSize=17232779273145073716"); - // MinHeapSize larger than the default max heap size raises MaxHeapSize - test("-XX:MinHeapSize=17232779273145073716"); + // MinHeapSize as large as MaxHeapSize. MaxHeapSize is set as well so that + // a smaller -Xmx passed down by the test environment is overridden. + test("-XX:MinHeapSize=17232779273145073716", + "-XX:MaxHeapSize=17232779273145073716"); } } From 1b32b2159f29683b60ffcd688e91f40f8db0442c Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Fri, 9 Oct 2026 19:01:43 +0000 Subject: [PATCH 97/98] 8394208: ProblemList java/foreign/TestSharedCloseDeopts.java on all configs Reviewed-by: lmesnik --- test/jdk/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index c5aea218b79..25fbbb628a0 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -574,6 +574,8 @@ jdk/internal/platform/docker/TestLimitsUpdating.java 8390823 linux-all # jdk_foreign +java/foreign/TestSharedCloseDeopts.java 8393853 generic-all + ############################################################################ # Client manual tests From 43e4465994d5e4529c71d3db2deb45d0fe276831 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Fri, 9 Oct 2026 07:55:36 +0000 Subject: [PATCH 98/98] 8393061: java/nio/channels/Channels/Available.java failing (lnx) Reviewed-by: alanb, haosun --- .../java/nio/channels/Channels/Available.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/test/jdk/java/nio/channels/Channels/Available.java b/test/jdk/java/nio/channels/Channels/Available.java index 6f5c19d783a..9e37598bbb6 100644 --- a/test/jdk/java/nio/channels/Channels/Available.java +++ b/test/jdk/java/nio/channels/Channels/Available.java @@ -55,7 +55,7 @@ class Available { /** * Test InputStream.available when input stream reads from a SeekableByteChannel. */ - void testSeekableByteChannel(SeekableByteChannel ch) throws Exception { + private static void testSeekableByteChannel(SeekableByteChannel ch) throws Exception { InputStream in = Channels.newInputStream(ch); long size = ch.size(); long[] positions = new long[] { @@ -68,20 +68,16 @@ void testSeekableByteChannel(SeekableByteChannel ch) throws Exception { Integer.MAX_VALUE - size + 1L, Integer.MAX_VALUE - 1L, Integer.MAX_VALUE, - Integer.MAX_VALUE + 1L, - Long.MAX_VALUE - size - 1L, - Long.MAX_VALUE - size, - Long.MAX_VALUE - size + 1L, - Long.MAX_VALUE - 1L, - Long.MAX_VALUE + Integer.MAX_VALUE + 1L }; for (long pos : positions) { if (pos >= 0) { - ch.position(pos); int expectedAvailable = Math.clamp(size - pos, 0, Integer.MAX_VALUE); - int available = in.available(); - System.err.format(" position = %d, available = %d%n", pos, available); - assertEquals(expectedAvailable, available); + System.err.format( + " size = %d, position = %d, expected available = %d%n", + size, pos, expectedAvailable); + ch.position(pos); + assertEquals(expectedAvailable, in.available()); } } }